Initial (semi-broken) support for await and yield statements/expressions. Minor changes to the lexer and grammar
This commit is contained in:
parent
74b8328bf7
commit
151b8da0d5
|
@ -90,6 +90,7 @@ importStmt -> ("from" IDENTIFIER)? "import" (IDENTIFIER ("as" IDENTIFIER)? "
|
||||||
assertStmt → "assert" expression ";";
|
assertStmt → "assert" expression ";";
|
||||||
delStmt → "del" expression ";";
|
delStmt → "del" expression ";";
|
||||||
yieldStmt → "yield" expression ";";
|
yieldStmt → "yield" expression ";";
|
||||||
|
awaitStmt → "await" expression ";";
|
||||||
continueStmt → "continue" ";";
|
continueStmt → "continue" ";";
|
||||||
blockStmt → "{" declaration* "}"; // Blocks create a new scope that lasts until they're closed
|
blockStmt → "{" declaration* "}"; // Blocks create a new scope that lasts until they're closed
|
||||||
ifStmt → "if" "(" expression ")" statement ("else" statement)?; // If statements are conditional jumps
|
ifStmt → "if" "(" expression ")" statement ("else" statement)?; // If statements are conditional jumps
|
||||||
|
@ -100,15 +101,16 @@ foreachStmt → "foreach" "(" (IDENTIFIER ":" expression) ")" statement;
|
||||||
|
|
||||||
// Expressions (rules that produce a value, but also have side effects)
|
// Expressions (rules that produce a value, but also have side effects)
|
||||||
expression → assignment;
|
expression → assignment;
|
||||||
assignment → (call ".")? IDENTIFIER "=" yield; // Assignment is the highest-level expression
|
assignment → (call ".")? IDENTIFIER "=" expression; // Assignment is the highest-level expression
|
||||||
yield → "(" "yield" expression ")";
|
yieldExpr → "yield" expression;
|
||||||
|
awaitExpr → "await" expression;
|
||||||
logic_or → logic_and ("and" logic_and)*;
|
logic_or → logic_and ("and" logic_and)*;
|
||||||
logic_and → equality ("or" equality)*;
|
logic_and → equality ("or" equality)*;
|
||||||
equality → comparison (( "!=" | "==" ) comparison )*;
|
equality → comparison (( "!=" | "==" ) comparison )*;
|
||||||
comparison → term (( ">" | ">=" | "<" | "<=" ) term )*;
|
comparison → term (( ">" | ">=" | "<" | "<=" ) term )*;
|
||||||
term → factor (( "-" | "+" ) factor )*; // Precedence for + and - in operations
|
term → factor (( "-" | "+" ) factor )*; // Precedence for + and - in operations
|
||||||
factor → unary (("/" | "*" | "**" | "^" | "&") unary)*; // All other binary operators have the same precedence
|
factor → unary (("/" | "*" | "**" | "^" | "&") unary)*; // All other binary operators have the same precedence
|
||||||
unary → ("!" | "-" | "~" | "await") unary | call;
|
unary → ("!" | "-" | "~") unary | call;
|
||||||
call → primary ("(" arguments? ")" | "." IDENTIFIER)*;
|
call → primary ("(" arguments? ")" | "." IDENTIFIER)*;
|
||||||
// Below are some collection literals: lists, sets, dictionaries and tuples
|
// Below are some collection literals: lists, sets, dictionaries and tuples
|
||||||
listExpr → "[" arguments? "]"
|
listExpr → "[" arguments? "]"
|
||||||
|
|
|
@ -457,7 +457,8 @@ proc next(self: Lexer) =
|
||||||
self.line += 1
|
self.line += 1
|
||||||
elif single in ['"', '\'']:
|
elif single in ['"', '\'']:
|
||||||
if self.check(single) and self.check(single, 1):
|
if self.check(single) and self.check(single, 1):
|
||||||
# Multiline strings start with 3 apexes
|
# Multiline strings start with 3 quotes
|
||||||
|
discard self.step(2)
|
||||||
self.parseString(single, "multi")
|
self.parseString(single, "multi")
|
||||||
else:
|
else:
|
||||||
self.parseString(single)
|
self.parseString(single)
|
||||||
|
@ -479,7 +480,7 @@ proc next(self: Lexer) =
|
||||||
else:
|
else:
|
||||||
# Comments are a special case
|
# Comments are a special case
|
||||||
if single == '#':
|
if single == '#':
|
||||||
while not self.check('\n'):
|
while not (self.check('\n') or self.done()):
|
||||||
discard self.step()
|
discard self.step()
|
||||||
return
|
return
|
||||||
# We start by checking for multi-character tokens,
|
# We start by checking for multi-character tokens,
|
||||||
|
|
|
@ -45,12 +45,15 @@ type
|
||||||
raiseStmt,
|
raiseStmt,
|
||||||
assertStmt,
|
assertStmt,
|
||||||
delStmt,
|
delStmt,
|
||||||
|
yieldStmt,
|
||||||
|
awaitStmt,
|
||||||
fromImportStmt,
|
fromImportStmt,
|
||||||
importStmt,
|
importStmt,
|
||||||
# An expression followed by a semicolon
|
# An expression followed by a semicolon
|
||||||
exprStmt,
|
exprStmt,
|
||||||
# Expressions
|
# Expressions
|
||||||
assignExpr,
|
assignExpr,
|
||||||
|
awaitExpr,
|
||||||
yieldExpr,
|
yieldExpr,
|
||||||
setItemExpr, # Set expressions like a.b = "c"
|
setItemExpr, # Set expressions like a.b = "c"
|
||||||
binaryExpr,
|
binaryExpr,
|
||||||
|
@ -160,6 +163,9 @@ type
|
||||||
YieldExpr* = ref object of ASTNode
|
YieldExpr* = ref object of ASTNode
|
||||||
expression*: ASTNode
|
expression*: ASTNode
|
||||||
|
|
||||||
|
AwaitExpr* = ref object of ASTNode
|
||||||
|
awaitee*: ASTNode
|
||||||
|
|
||||||
AssignExpr* = ref object of ASTNode
|
AssignExpr* = ref object of ASTNode
|
||||||
name*: ASTNode
|
name*: ASTNode
|
||||||
value*: ASTNode
|
value*: ASTNode
|
||||||
|
@ -198,6 +204,9 @@ type
|
||||||
condition*: ASTNode
|
condition*: ASTNode
|
||||||
body*: ASTNode
|
body*: ASTNode
|
||||||
|
|
||||||
|
AwaitStmt* = ref object of ASTNode
|
||||||
|
awaitee*: ASTNode
|
||||||
|
|
||||||
BreakStmt* = ref object of ASTNode
|
BreakStmt* = ref object of ASTNode
|
||||||
|
|
||||||
ContinueStmt* = ref object of ASTNode
|
ContinueStmt* = ref object of ASTNode
|
||||||
|
@ -210,6 +219,9 @@ type
|
||||||
thenBranch*: ASTNode
|
thenBranch*: ASTNode
|
||||||
elseBranch*: ASTNode
|
elseBranch*: ASTNode
|
||||||
|
|
||||||
|
YieldStmt* = ref object of ASTNode
|
||||||
|
expression*: ASTNode
|
||||||
|
|
||||||
VarDecl* = ref object of ASTNode
|
VarDecl* = ref object of ASTNode
|
||||||
name*: ASTNode
|
name*: ASTNode
|
||||||
value*: ASTNode
|
value*: ASTNode
|
||||||
|
@ -373,6 +385,11 @@ proc newAssignExpr*(name, value: ASTNode): AssignExpr =
|
||||||
result.value = value
|
result.value = value
|
||||||
|
|
||||||
|
|
||||||
|
proc newAwaitExpr*(awaitee: ASTNode): AwaitExpr =
|
||||||
|
result = AwaitExpr(kind: awaitExpr)
|
||||||
|
result.awaitee = awaitee
|
||||||
|
|
||||||
|
|
||||||
proc newExprStmt*(expression: ASTNode): ExprStmt =
|
proc newExprStmt*(expression: ASTNode): ExprStmt =
|
||||||
result = ExprStmt(kind: exprStmt)
|
result = ExprStmt(kind: exprStmt)
|
||||||
result.expression = expression
|
result.expression = expression
|
||||||
|
@ -394,6 +411,16 @@ proc newDelStmt*(name: ASTNode): DelStmt =
|
||||||
result.name = name
|
result.name = name
|
||||||
|
|
||||||
|
|
||||||
|
proc newYieldStmt*(expression: ASTNode): YieldStmt =
|
||||||
|
result = YieldStmt(kind: yieldStmt)
|
||||||
|
result.expression = expression
|
||||||
|
|
||||||
|
|
||||||
|
proc newAwaitStmt*(awaitee: ASTNode): AwaitExpr =
|
||||||
|
result = AwaitExpr(kind: awaitExpr)
|
||||||
|
result.awaitee = awaitee
|
||||||
|
|
||||||
|
|
||||||
proc newAssertStmt*(expression: ASTNode): AssertStmt =
|
proc newAssertStmt*(expression: ASTNode): AssertStmt =
|
||||||
result = AssertStmt(kind: assertStmt)
|
result = AssertStmt(kind: assertStmt)
|
||||||
result.expression = expression
|
result.expression = expression
|
||||||
|
@ -439,7 +466,7 @@ proc newIfStmt*(condition: ASTNode, thenBranch, elseBranch: ASTNode): IfStmt =
|
||||||
|
|
||||||
|
|
||||||
proc newVarDecl*(name: ASTNode, value: ASTNode = newNilExpr(),
|
proc newVarDecl*(name: ASTNode, value: ASTNode = newNilExpr(),
|
||||||
isStatic: bool = true, isConst,
|
isStatic: bool = true, isConst: bool = false,
|
||||||
isPrivate: bool = true): VarDecl =
|
isPrivate: bool = true): VarDecl =
|
||||||
result = VarDecl(kind: varDecl)
|
result = VarDecl(kind: varDecl)
|
||||||
result.name = name
|
result.name = name
|
||||||
|
@ -479,6 +506,8 @@ proc `$`*(self: ASTNode): string =
|
||||||
of intExpr, floatExpr, hexExpr, binExpr, octExpr, strExpr, trueExpr, falseExpr, nanExpr, nilExpr, infExpr:
|
of intExpr, floatExpr, hexExpr, binExpr, octExpr, strExpr, trueExpr, falseExpr, nanExpr, nilExpr, infExpr:
|
||||||
if self.kind in {trueExpr, falseExpr, nanExpr, nilExpr, infExpr}:
|
if self.kind in {trueExpr, falseExpr, nanExpr, nilExpr, infExpr}:
|
||||||
result &= &"Literal({($self.kind)[0..^5]})"
|
result &= &"Literal({($self.kind)[0..^5]})"
|
||||||
|
elif self.kind == strExpr:
|
||||||
|
result &= &"Literal({LiteralExpr(self).literal.lexeme.escape()})"
|
||||||
else:
|
else:
|
||||||
result &= &"Literal({LiteralExpr(self).literal.lexeme})"
|
result &= &"Literal({LiteralExpr(self).literal.lexeme})"
|
||||||
of identExpr:
|
of identExpr:
|
||||||
|
@ -502,7 +531,7 @@ proc `$`*(self: ASTNode): string =
|
||||||
result &= &"Binary({self.a}, Operator('{self.operator.lexeme}'), {self.b})"
|
result &= &"Binary({self.a}, Operator('{self.operator.lexeme}'), {self.b})"
|
||||||
of assignExpr:
|
of assignExpr:
|
||||||
var self = AssignExpr(self)
|
var self = AssignExpr(self)
|
||||||
result &= &"Assign(name='{self.namec}', value={self.value})"
|
result &= &"Assign(name='{self.name}', value={self.value})"
|
||||||
of exprStmt:
|
of exprStmt:
|
||||||
var self = ExprStmt(self)
|
var self = ExprStmt(self)
|
||||||
result &= &"ExpressionStatement({self.expression})"
|
result &= &"ExpressionStatement({self.expression})"
|
||||||
|
@ -533,12 +562,18 @@ proc `$`*(self: ASTNode): string =
|
||||||
of returnStmt:
|
of returnStmt:
|
||||||
var self = ReturnStmt(self)
|
var self = ReturnStmt(self)
|
||||||
result &= &"Return({self.value})"
|
result &= &"Return({self.value})"
|
||||||
|
of yieldExpr:
|
||||||
|
var self = YieldExpr(self)
|
||||||
|
result &= &"Yield(expression={self.expression})"
|
||||||
of ifStmt:
|
of ifStmt:
|
||||||
var self = IfStmt(self)
|
var self = IfStmt(self)
|
||||||
if self.elseBranch == nil:
|
if self.elseBranch == nil:
|
||||||
result &= &"If(condition={self.condition}, thenBranch={self.thenBranch}, elseBranch=nil)"
|
result &= &"If(condition={self.condition}, thenBranch={self.thenBranch}, elseBranch=nil)"
|
||||||
else:
|
else:
|
||||||
result &= &"If(condition={self.condition}, thenBranch={self.thenBranch}, elseBranch={self.elseBranch})"
|
result &= &"If(condition={self.condition}, thenBranch={self.thenBranch}, elseBranch={self.elseBranch})"
|
||||||
|
of yieldStmt:
|
||||||
|
var self = YieldStmt(self)
|
||||||
|
result &= &"YieldStmt(expression={self.expression})"
|
||||||
of varDecl:
|
of varDecl:
|
||||||
var self = VarDecl(self)
|
var self = VarDecl(self)
|
||||||
result &= &"Var(name={self.name}, value={self.value}, const={self.isConst}, static={self.isStatic}, private={self.isPrivate})"
|
result &= &"Var(name={self.name}, value={self.value}, const={self.isConst}, static={self.isStatic}, private={self.isPrivate})"
|
||||||
|
|
|
@ -37,15 +37,14 @@ type
|
||||||
|
|
||||||
Optimizer* = ref object
|
Optimizer* = ref object
|
||||||
warnings: seq[Warning]
|
warnings: seq[Warning]
|
||||||
dryRun: bool
|
foldConstants: bool
|
||||||
|
|
||||||
|
|
||||||
proc initOptimizer*(self: Optimizer = nil): Optimizer =
|
proc initOptimizer*(foldConstants: bool = true): Optimizer =
|
||||||
## Initializes a new optimizer object
|
## Initializes a new optimizer object
|
||||||
## or resets the state of an existing one
|
|
||||||
if self != nil:
|
|
||||||
result = self
|
|
||||||
new(result)
|
new(result)
|
||||||
|
result.foldConstants = foldConstants
|
||||||
|
result.warnings = @[]
|
||||||
|
|
||||||
|
|
||||||
proc newWarning(self: Optimizer, kind: WarningKind, node: ASTNode) =
|
proc newWarning(self: Optimizer, kind: WarningKind, node: ASTNode) =
|
||||||
|
@ -63,6 +62,8 @@ proc optimizeConstant(self: Optimizer, node: ASTNode): ASTNode =
|
||||||
## integers. This method converts all of the different
|
## integers. This method converts all of the different
|
||||||
## integer forms (binary, octal and hexadecimal) to
|
## integer forms (binary, octal and hexadecimal) to
|
||||||
## decimal integers. Overflows are checked here too
|
## decimal integers. Overflows are checked here too
|
||||||
|
if not self.foldConstants:
|
||||||
|
return node
|
||||||
case node.kind:
|
case node.kind:
|
||||||
of intExpr:
|
of intExpr:
|
||||||
var x: int
|
var x: int
|
||||||
|
@ -272,6 +273,8 @@ proc optimizeNode(self: Optimizer, node: ASTNode): ASTNode =
|
||||||
## Analyzes an AST node and attempts to perform
|
## Analyzes an AST node and attempts to perform
|
||||||
## optimizations on it. If no optimization can be
|
## optimizations on it. If no optimization can be
|
||||||
## applied, the same node is returned
|
## applied, the same node is returned
|
||||||
|
if not self.foldConstants:
|
||||||
|
return node
|
||||||
case node.kind:
|
case node.kind:
|
||||||
of exprStmt:
|
of exprStmt:
|
||||||
result = newExprStmt(self.optimizeNode(ExprStmt(node).expression))
|
result = newExprStmt(self.optimizeNode(ExprStmt(node).expression))
|
||||||
|
@ -321,7 +324,9 @@ proc optimize*(self: Optimizer, tree: seq[ASTNode]): tuple[tree: seq[ASTNode], w
|
||||||
## as well as a list of warnings that may
|
## as well as a list of warnings that may
|
||||||
## be of interest. The input tree may be
|
## be of interest. The input tree may be
|
||||||
## identical to the output tree if no optimization
|
## identical to the output tree if no optimization
|
||||||
## could be performed
|
## could be performed. Constant folding can be
|
||||||
|
## turned off by setting foldConstants to false
|
||||||
|
## when initializing the optimizer object
|
||||||
var newTree: seq[ASTNode] = @[]
|
var newTree: seq[ASTNode] = @[]
|
||||||
for node in tree:
|
for node in tree:
|
||||||
newTree.add(self.optimizeNode(node))
|
newTree.add(self.optimizeNode(node))
|
||||||
|
|
|
@ -196,10 +196,6 @@ proc primary(self: Parser): ASTNode =
|
||||||
if self.match(RightParen):
|
if self.match(RightParen):
|
||||||
# This yields an empty tuple
|
# This yields an empty tuple
|
||||||
result = newTupleExpr(@[])
|
result = newTupleExpr(@[])
|
||||||
elif self.match(Yield):
|
|
||||||
if self.context != Function:
|
|
||||||
self.error("'yield' outside function")
|
|
||||||
result = newYieldExpr(self.expression())
|
|
||||||
else:
|
else:
|
||||||
result = self.expression()
|
result = self.expression()
|
||||||
if self.match(Comma):
|
if self.match(Comma):
|
||||||
|
@ -213,6 +209,14 @@ proc primary(self: Parser): ASTNode =
|
||||||
else:
|
else:
|
||||||
self.expect(RightParen, "unterminated parenthesized expression")
|
self.expect(RightParen, "unterminated parenthesized expression")
|
||||||
result = newGroupingExpr(result)
|
result = newGroupingExpr(result)
|
||||||
|
of Yield:
|
||||||
|
if self.context != Function:
|
||||||
|
self.error("'yield' cannot be outside functions")
|
||||||
|
result = newYieldExpr(self.expression())
|
||||||
|
of Await:
|
||||||
|
if self.context != Function:
|
||||||
|
self.error("'await' cannot be used outside functions")
|
||||||
|
result = newAwaitExpr(self.expression())
|
||||||
of RightParen:
|
of RightParen:
|
||||||
# This is *technically* unnecessary: the parser would
|
# This is *technically* unnecessary: the parser would
|
||||||
# throw an error regardless, but it's a little bit nicer
|
# throw an error regardless, but it's a little bit nicer
|
||||||
|
@ -280,9 +284,7 @@ proc call(self: Parser): ASTNode =
|
||||||
|
|
||||||
proc unary(self: Parser): ASTNode =
|
proc unary(self: Parser): ASTNode =
|
||||||
## Parses unary expressions
|
## Parses unary expressions
|
||||||
if self.match([Minus, Tilde, Await]):
|
if self.match([Minus, Tilde]):
|
||||||
if self.peek(-1).kind == Await and self.context != Function:
|
|
||||||
self.error("'await' cannot be used outside functions")
|
|
||||||
result = newUnaryExpr(self.peek(-1), self.unary())
|
result = newUnaryExpr(self.peek(-1), self.unary())
|
||||||
else:
|
else:
|
||||||
result = self.call()
|
result = self.call()
|
||||||
|
@ -476,11 +478,19 @@ proc returnStmt(self: Parser): ASTNode =
|
||||||
proc yieldStmt(self: Parser): ASTNode =
|
proc yieldStmt(self: Parser): ASTNode =
|
||||||
## Parses yield Statements
|
## Parses yield Statements
|
||||||
if self.context != Function:
|
if self.context != Function:
|
||||||
self.error("'yield' outside function")
|
self.error("'yield' cannot be used outside functions")
|
||||||
result = newYieldExpr(self.expression)
|
result = newYieldExpr(self.expression)
|
||||||
endOfLine("missing semicolon after yield statement")
|
endOfLine("missing semicolon after yield statement")
|
||||||
|
|
||||||
|
|
||||||
|
proc awaitStmt(self: Parser): ASTNode =
|
||||||
|
## Parses yield Statements
|
||||||
|
if self.context != Function:
|
||||||
|
self.error("'await' cannot be used outside functions")
|
||||||
|
result = newAwaitExpr(self.expression)
|
||||||
|
endOfLine("missing semicolon after yield statement")
|
||||||
|
|
||||||
|
|
||||||
proc raiseStmt(self: Parser): ASTNode =
|
proc raiseStmt(self: Parser): ASTNode =
|
||||||
## Parses raise statements
|
## Parses raise statements
|
||||||
var exception: ASTNode
|
var exception: ASTNode
|
||||||
|
@ -653,20 +663,20 @@ proc funDecl(self: Parser, isAsync: bool = false, isStatic: bool = true, isPriva
|
||||||
# We do some analysis on the code of the function. Namely we check
|
# We do some analysis on the code of the function. Namely we check
|
||||||
# if the user used 'await' in a non-async function and if the
|
# if the user used 'await' in a non-async function and if the
|
||||||
# function has any yield expressions in them, making it a
|
# function has any yield expressions in them, making it a
|
||||||
# generator. Async generators are also (syntactically) supported
|
# generator. Async generators are also supported
|
||||||
for line in BlockStmt(result.body).code:
|
for line in BlockStmt(result.body).code:
|
||||||
if line.kind == exprStmt:
|
if line.kind == exprStmt:
|
||||||
if ExprStmt(line).expression.kind == unaryExpr:
|
echo ExprStmt(line).expression.kind
|
||||||
if not result.isGenerator and UnaryExpr(ExprStmt(line).expression).operator.kind == Yield:
|
if ExprStmt(line).expression.kind == yieldExpr:
|
||||||
# If the function has the isGenerator field already set, checking again
|
|
||||||
# is redundant
|
|
||||||
result.isGenerator = true
|
result.isGenerator = true
|
||||||
elif UnaryExpr(ExprStmt(line).expression).operator.kind == Await and not result.isAsync:
|
elif not result.isAsync and ExprStmt(line).expression.kind == awaitExpr:
|
||||||
self.error("'await' outside async function")
|
self.error("'await' cannot be used outside async functions")
|
||||||
elif not result.isGenerator and line.kind == NodeKind.funDecl:
|
elif line.kind == NodeKind.funDecl:
|
||||||
# Nested function declarations with yield expressions as
|
# Nested function declarations with yield expressions as
|
||||||
# default arguments are valid, but for them to work we
|
# default arguments are valid, but for them to work we
|
||||||
# need to make the containing function a generator!
|
# need to make the containing function a generator. These
|
||||||
|
# yield expressions will run in the outer function's scope
|
||||||
|
# and are lazily evaluated at runtime
|
||||||
for default in FunDecl(line).defaults:
|
for default in FunDecl(line).defaults:
|
||||||
if default.kind == NodeKind.yieldExpr:
|
if default.kind == NodeKind.yieldExpr:
|
||||||
result.isGenerator = true
|
result.isGenerator = true
|
||||||
|
@ -745,6 +755,9 @@ proc statement(self: Parser): ASTNode =
|
||||||
of Yield:
|
of Yield:
|
||||||
discard self.step()
|
discard self.step()
|
||||||
result = self.yieldStmt()
|
result = self.yieldStmt()
|
||||||
|
of Await:
|
||||||
|
discard self.step()
|
||||||
|
result = self.awaitStmt()
|
||||||
else:
|
else:
|
||||||
result = self.expressionStatement()
|
result = self.expressionStatement()
|
||||||
|
|
||||||
|
@ -796,7 +809,9 @@ proc declaration(self: Parser): ASTNode =
|
||||||
let isStatic: bool = if self.peek(-1).kind == Static: true else: false
|
let isStatic: bool = if self.peek(-1).kind == Static: true else: false
|
||||||
if self.match(Async):
|
if self.match(Async):
|
||||||
self.expect(Fun)
|
self.expect(Fun)
|
||||||
|
self.context = Function
|
||||||
result = self.funDecl(isStatic=isStatic, isPrivate=true, isAsync=true)
|
result = self.funDecl(isStatic=isStatic, isPrivate=true, isAsync=true)
|
||||||
|
self.context = Script
|
||||||
else:
|
else:
|
||||||
case self.peek().kind:
|
case self.peek().kind:
|
||||||
of Var, Const:
|
of Var, Const:
|
||||||
|
@ -815,7 +830,9 @@ proc declaration(self: Parser): ASTNode =
|
||||||
of Async:
|
of Async:
|
||||||
discard self.step()
|
discard self.step()
|
||||||
self.expect(Fun)
|
self.expect(Fun)
|
||||||
|
self.context = Function
|
||||||
result = self.funDecl(isAsync=true)
|
result = self.funDecl(isAsync=true)
|
||||||
|
self.context = Script
|
||||||
else:
|
else:
|
||||||
result = self.statement()
|
result = self.statement()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue