BROKEN: Initial work on closed-over variables and attempt to cleanup the AST type structure
This commit is contained in:
parent
43040df9f9
commit
78d955541d
|
@ -45,6 +45,7 @@ type
|
|||
isPrivate: bool
|
||||
isConst: bool
|
||||
valueType: IdentExpr
|
||||
codePos: int
|
||||
|
||||
Loop = object
|
||||
## A "loop object" used
|
||||
|
@ -470,8 +471,8 @@ proc binary(self: Compiler, node: BinaryExpr) =
|
|||
|
||||
|
||||
|
||||
proc declareName(self: Compiler, node: ASTNode, kind: IdentExpr) =
|
||||
## Compiles all name declarations
|
||||
proc declareName(self: Compiler, node: Declaration, kind: IdentExpr) =
|
||||
## Statically declares a name into the current scope
|
||||
case node.kind:
|
||||
of NodeKind.varDecl:
|
||||
var node = VarDecl(node)
|
||||
|
@ -480,18 +481,27 @@ proc declareName(self: Compiler, node: ASTNode, kind: IdentExpr) =
|
|||
# If someone ever hits this limit in real-world scenarios, I swear I'll
|
||||
# slap myself 100 times with a sign saying "I'm dumb". Mark my words
|
||||
self.error("cannot declare more than 16777216 variables at a time")
|
||||
self.names.add(Name(depth: self.scopeDepth, name: IdentExpr(node.name),
|
||||
self.names.add(Name(depth: self.scopeDepth,
|
||||
name: node.name,
|
||||
isPrivate: node.isPrivate,
|
||||
owner: self.currentModule,
|
||||
isConst: node.isConst,
|
||||
valueType: kind))
|
||||
valueType: kind,
|
||||
codePos: self.chunk.code.len()))
|
||||
self.emitByte(StoreVar)
|
||||
self.emitBytes(self.names.high().toTriple())
|
||||
of funDecl:
|
||||
var node = FunDecl(node)
|
||||
# Declares the function's name in the
|
||||
# current (outer) scope...
|
||||
self.declareName(node.name, IdentExpr(node.returnType))
|
||||
self.names.add(Name(depth: self.scopeDepth,
|
||||
isPrivate: node.isPrivate,
|
||||
isConst: false,
|
||||
owner: self.currentModule,
|
||||
valueType: node.returnType,
|
||||
codePos: self.chunk.code.len()))
|
||||
self.emitByte(StoreVar)
|
||||
self.emitBytes(self.names.high().toTriple())
|
||||
# ... but its arguments in an inner one!
|
||||
self.scopeDepth += 1
|
||||
# (this ugly part is needed because
|
||||
|
@ -500,35 +510,42 @@ proc declareName(self: Compiler, node: ASTNode, kind: IdentExpr) =
|
|||
for argument in node.arguments:
|
||||
if self.names.high() > 16777215:
|
||||
self.error("cannot declare more than 16777216 variables at a time")
|
||||
self.names.add(Name(depth: self.scopeDepth + 1, isPrivate: true, owner: self.currentModule, isConst: false, name: IdentExpr(argument.name), valueType: kind))
|
||||
self.emitByte(LoadVar)
|
||||
self.names.add(Name(depth: self.scopeDepth + 1,
|
||||
isPrivate: true,
|
||||
owner: self.currentModule,
|
||||
isConst: false,
|
||||
name: argument.name,
|
||||
valueType: kind,
|
||||
codePos: self.chunk.code.len()))
|
||||
self.emitByte(StoreVar)
|
||||
self.emitBytes(self.names.high().toTriple())
|
||||
self.scopeDepth -= 1
|
||||
# TODO: Default arguments and unpacking
|
||||
else:
|
||||
discard # TODO: Classes
|
||||
discard # Unreachable
|
||||
|
||||
|
||||
proc resolveStatic(self: Compiler, name: IdentExpr,
|
||||
proc resolve(self: Compiler, name: IdentExpr,
|
||||
depth: int = self.scopeDepth): Name =
|
||||
## Traverses self.staticNames backwards and returns the
|
||||
## Traverses self.names backwards and returns the
|
||||
## first name object with the given name. Returns
|
||||
## nil when the name can't be found. This function
|
||||
## has no concept of scope depth, because getStaticIndex
|
||||
## has no concept of scope depth, because getStackPos
|
||||
## does that job. Note that private names declared in
|
||||
## other modules will not be resolved!
|
||||
for obj in reversed(self.names):
|
||||
if obj.name.token.lexeme == name.token.lexeme:
|
||||
if obj.isPrivate and obj.owner != self.currentModule:
|
||||
return nil
|
||||
continue # There may be a name in the current module that
|
||||
# matches, so we skip this
|
||||
return obj
|
||||
return nil
|
||||
|
||||
|
||||
proc getStaticIndex(self: Compiler, name: IdentExpr, depth: int = self.scopeDepth): tuple[closedOver: bool, pos: int] =
|
||||
## Gets the predicted stack position of the given variable and
|
||||
## returns a tuple (closedOver, pos) that tells the caller whether
|
||||
## the variable is to be emitted as a closure as well as its predicted
|
||||
proc getStackPos(self: Compiler, name: IdentExpr, depth: int = self.scopeDepth): tuple[closedOver: bool, pos: int] =
|
||||
## Iterates the internal list of declared names backwards and
|
||||
## returns a tuple (closedOver, pos) that tells the caller whether the
|
||||
## the name is to be emitted as a closure as well as its predicted
|
||||
## stack/closure array position. Returns (false, -1) if the variable's
|
||||
## location can not be determined at compile time (this is an error!).
|
||||
## Note that private names declared in other modules will not be resolved!
|
||||
|
@ -536,7 +553,7 @@ proc getStaticIndex(self: Compiler, name: IdentExpr, depth: int = self.scopeDept
|
|||
for variable in reversed(self.names):
|
||||
if name.name.lexeme == variable.name.name.lexeme:
|
||||
if variable.isPrivate and variable.owner != self.currentModule:
|
||||
return (false, -1)
|
||||
continue
|
||||
if variable.depth == depth or variable.depth == 0:
|
||||
# variable.depth == 0 for globals!
|
||||
return (false, i)
|
||||
|
@ -548,32 +565,69 @@ proc getStaticIndex(self: Compiler, name: IdentExpr, depth: int = self.scopeDept
|
|||
return (false, -1)
|
||||
|
||||
|
||||
proc detectClosureVariable(self: Compiler, name: IdentExpr, depth: int = self.scopeDepth) =
|
||||
## Detects if the given name is used in a local scope deeper
|
||||
## than the given one and modifies the code emitted for it
|
||||
## to store it as a closure variable if it is. Does nothing if the name
|
||||
## hasn't been declared yet or is unreachable (for example if it's
|
||||
## declared as private in another module), if the name itself is a
|
||||
## global variable and if either the current or the outer scope are
|
||||
## the global (outermost) one. This function must be called each
|
||||
## time a name is referenced in order for closed-over variables
|
||||
## to be emitted properly, otherwise the runtime may behave
|
||||
## unpredictably or crash
|
||||
if depth == 0 or depth - 1 == 0:
|
||||
return
|
||||
var entry = self.resolve(name)
|
||||
if entry == nil:
|
||||
return
|
||||
if entry.depth == 0:
|
||||
return
|
||||
if entry.depth < depth:
|
||||
# Ding! The given name is closed over: we need to
|
||||
# change the StoreVar instruction that created this
|
||||
# name entry into a StoreHeap. We don't need to change
|
||||
# other pieces of code because self.identifier() already
|
||||
# emits LoadHeap if it detects the variable is closed over,
|
||||
# whether or not this function is called
|
||||
self.closedOver.add(entry.name)
|
||||
if self.closedOver.len() >= 16777216:
|
||||
self.error("too many consecutive closure-over variables (max is 16777216)")
|
||||
let idx = self.closedOver.high().toTriple()
|
||||
self.chunk.code[entry.codePos] = StoreHeap.uint8
|
||||
self.chunk.code[entry.codePos + 1] = idx[0]
|
||||
self.chunk.code[entry.codePos + 2] = idx[1]
|
||||
self.chunk.code[entry.codePos + 3] = idx[2]
|
||||
|
||||
|
||||
proc identifier(self: Compiler, node: IdentExpr) =
|
||||
## Compiles access to identifiers
|
||||
let s = self.resolveStatic(node)
|
||||
if s != nil:
|
||||
if s.isConst:
|
||||
# Constants are emitted as, you guessed it, constant instructions
|
||||
# no matter the scope depth. Also, name resolution specifiers do not
|
||||
# apply to them (because what would it mean for a constant to be dynamic
|
||||
# anyway?)
|
||||
let s = self.resolve(node)
|
||||
if s == nil:
|
||||
self.error(&"reference to undeclared name '{node.token.lexeme}'")
|
||||
elif s.isConst:
|
||||
# Constants are emitted as, you guessed it, LoadConstant instructions
|
||||
# no matter the scope depth. If optimizations are enabled, the compiler
|
||||
# will reuse the same constant every time it is referenced instead
|
||||
self.emitConstant(node)
|
||||
else:
|
||||
let t = self.getStaticIndex(node)
|
||||
self.detectClosureVariable(s.name)
|
||||
let t = self.getStackPos(node)
|
||||
let index = t.pos
|
||||
if index != -1:
|
||||
# We don't check if index is -1 because if it
|
||||
# were, self.resolve() would have returned nil
|
||||
if not t.closedOver:
|
||||
self.emitByte(LoadVar) # Static name resolution, loads value at index in the stack. Very fast. Much wow.
|
||||
# Static name resolution, loads value at index in the stack. Very fast. Much wow.
|
||||
self.emitByte(LoadVar)
|
||||
self.emitBytes(index.toTriple())
|
||||
else:
|
||||
if self.closedOver.len() == 0:
|
||||
self.error("error: closure variable array is empty but LoadHeap would be emitted (this is an internal error and most likely a bug)")
|
||||
if self.closedOver.len() >= 16777216:
|
||||
self.error("too many consecutive closure-over variables (max is 16777216)")
|
||||
self.emitByte(LoadHeap) # Heap-allocated closure variable. Stored in a separate "closure array" in the VM that does not have stack semantics
|
||||
# Heap-allocated closure variable. Stored in a separate "closure array" in the VM that does not have stack semantics.
|
||||
# This makes closures work as expected and is not comparatively slower than indexing our stack (since they're both
|
||||
# dynamic arrays at runtime anyway)
|
||||
self.emitByte(LoadHeap)
|
||||
self.emitBytes(self.closedOver.high().toTriple())
|
||||
else:
|
||||
self.error(&"reference to undeclared name '{node.token.lexeme}'")
|
||||
|
||||
|
||||
proc assignment(self: Compiler, node: ASTNode) =
|
||||
|
@ -581,12 +635,13 @@ proc assignment(self: Compiler, node: ASTNode) =
|
|||
case node.kind:
|
||||
of assignExpr:
|
||||
var node = AssignExpr(node)
|
||||
# TODO: This will explode with slicing!
|
||||
var name = IdentExpr(node.name)
|
||||
let r = self.resolveStatic(name)
|
||||
let r = self.resolve(name)
|
||||
if r != nil and r.isConst:
|
||||
self.error("cannot assign to constant")
|
||||
self.expression(node.value)
|
||||
let t = self.getStaticIndex(name)
|
||||
let t = self.getStackPos(name)
|
||||
let index = t.pos
|
||||
case node.token.kind:
|
||||
of InplaceAdd:
|
||||
|
@ -615,13 +670,17 @@ proc assignment(self: Compiler, node: ASTNode) =
|
|||
discard # Unreachable
|
||||
# In-place operators just change
|
||||
# what values is set to a given
|
||||
# stack offset/name, so we only
|
||||
# need to perform the operation
|
||||
# as usual and then store it.
|
||||
# TODO: A better optimization would
|
||||
# be to have everything in one opcode,
|
||||
# but that requires variants for stack,
|
||||
# heap, and closure variables and I cba
|
||||
# offset in a dynamic array, so we only
|
||||
# need to perform the operation as usual
|
||||
# and then store it. We could combine
|
||||
# everything into a single opcode, but
|
||||
# that would require variants of each
|
||||
# one for regular stack variables as
|
||||
# well as closed-over ones
|
||||
|
||||
# Cannot be nil, we already resolved this!
|
||||
if self.resolve(name).isConst:
|
||||
self.error("cannot mutate constant")
|
||||
if index != -1:
|
||||
if not t.closedOver:
|
||||
self.emitByte(StoreVar)
|
||||
|
@ -782,7 +841,7 @@ proc inferExprType(self: Compiler, node: ASTNode): ASTNode =
|
|||
case node.kind:
|
||||
of identExpr:
|
||||
var node = IdentExpr(node)
|
||||
var name = self.resolveStatic(node)
|
||||
var name = self.resolve(node)
|
||||
if name == nil:
|
||||
return nil
|
||||
return name.valueType
|
||||
|
@ -1019,7 +1078,7 @@ proc funDecl(self: Compiler, node: FunDecl) =
|
|||
# A function's code is just compiled linearly
|
||||
# and then jumped over
|
||||
let jmp = self.emitJump(JumpForwards)
|
||||
self.declareName(node, IdentExpr(node.returnType))
|
||||
self.declareName(node, node.returnType)
|
||||
|
||||
# Since the deferred array is a linear
|
||||
# sequence of instructions and we want
|
||||
|
|
|
@ -368,6 +368,7 @@ proc parseString(self: Lexer, delimiter: string, mode: string = "single") =
|
|||
## lines and escape sequences in them are not parsed, like in raw
|
||||
## strings, so a multi-line string prefixed with the "r" modifier
|
||||
## is redundant, although multi-line byte/format strings are supported
|
||||
var slen = 0
|
||||
while not self.check(delimiter) and not self.done():
|
||||
if self.match("\n"):
|
||||
if mode == "multi":
|
||||
|
@ -399,6 +400,9 @@ proc parseString(self: Lexer, delimiter: string, mode: string = "single") =
|
|||
self.source = self.source[0..<self.current] & self.source[
|
||||
self.current + 1..^1]
|
||||
discard self.step()
|
||||
inc(slen)
|
||||
if slen > 1 and delimiter == "'":
|
||||
self.error("invalid character literal (length must be one!)")
|
||||
if mode == "multi":
|
||||
if not self.match(delimiter.repeat(3)):
|
||||
self.error("unexpected EOL while parsing multi-line string literal")
|
||||
|
@ -406,7 +410,10 @@ proc parseString(self: Lexer, delimiter: string, mode: string = "single") =
|
|||
self.error("unexpected EOF while parsing string literal")
|
||||
else:
|
||||
discard self.step()
|
||||
if delimiter == "\"":
|
||||
self.createToken(String)
|
||||
else:
|
||||
self.createToken(Char)
|
||||
|
||||
|
||||
proc parseBinary(self: Lexer) =
|
||||
|
@ -528,9 +535,9 @@ proc next(self: Lexer) =
|
|||
# New line
|
||||
self.incLine()
|
||||
elif self.match(["\"", "'"]):
|
||||
# String literal
|
||||
# String or character literal
|
||||
var mode = "single"
|
||||
if self.check(self.peek(-1)) and self.check(self.peek(-1), 1):
|
||||
if self.peek(-1) != "'" and self.check(self.peek(-1)) and self.check(self.peek(-1), 1):
|
||||
# Multiline strings start with 3 quotes
|
||||
discard self.step(2)
|
||||
mode = "multi"
|
||||
|
|
|
@ -71,6 +71,7 @@ type
|
|||
setExpr,
|
||||
falseExpr,
|
||||
strExpr,
|
||||
charExpr,
|
||||
intExpr,
|
||||
floatExpr,
|
||||
hexExpr,
|
||||
|
@ -81,7 +82,14 @@ type
|
|||
infExpr,
|
||||
identExpr, # Identifier
|
||||
|
||||
# Here I would've rather used object variants, and in fact that's what was in
|
||||
# place before, but not being able to re-declare a field of the same type in
|
||||
# another case branch is kind of a deal breaker long-term, so until that is
|
||||
# fixed (check out https://github.com/nim-lang/RFCs/issues/368 for more info).
|
||||
# I'll stick to using inheritance instead
|
||||
|
||||
|
||||
# Generic AST node types
|
||||
ASTNode* = ref object of RootObj
|
||||
## An AST node
|
||||
kind*: NodeKind
|
||||
|
@ -89,14 +97,19 @@ type
|
|||
# This is not shown when the node is printed, but makes it a heck of a lot easier to report
|
||||
# errors accurately even deep in the compilation pipeline
|
||||
token*: Token
|
||||
# This weird inheritance chain is just a map of
|
||||
# our grammar's precedence rules (expressions first,
|
||||
# then statements and declarations last)
|
||||
Expression* = ref object of ASTNode
|
||||
## An expression
|
||||
Statement* = ref object of Expression
|
||||
## A statement
|
||||
Declaration* = ref object of Statement
|
||||
## A declaration
|
||||
closedOver*: bool
|
||||
pragmas*: seq[Token]
|
||||
|
||||
# Here I would've rather used object variants, and in fact that's what was in
|
||||
# place before, but not being able to re-declare a field of the same type in
|
||||
# another case branch is kind of a deal breaker long-term, so until that is
|
||||
# fixed (check out https://github.com/nim-lang/RFCs/issues/368 for more info).
|
||||
# I'll stick to using inheritance instead
|
||||
|
||||
LiteralExpr* = ref object of ASTNode
|
||||
LiteralExpr* = ref object of Expression
|
||||
# Using a string for literals makes it much easier to handle numeric types, as
|
||||
# there is no overflow nor underflow or float precision issues during parsing.
|
||||
# Numbers are just serialized as strings and then converted back to numbers
|
||||
|
@ -114,6 +127,7 @@ type
|
|||
BinExpr* = ref object of LiteralExpr
|
||||
FloatExpr* = ref object of LiteralExpr
|
||||
StrExpr* = ref object of LiteralExpr
|
||||
CharExpr* = ref object of LiteralExpr
|
||||
|
||||
TrueExpr* = ref object of LiteralExpr
|
||||
FalseExpr* = ref object of LiteralExpr
|
||||
|
@ -122,156 +136,143 @@ type
|
|||
InfExpr* = ref object of LiteralExpr
|
||||
|
||||
ListExpr* = ref object of LiteralExpr
|
||||
members*: seq[ASTNode]
|
||||
members*: seq[Expression]
|
||||
valueType*: IdentExpr
|
||||
|
||||
SetExpr* = ref object of ListExpr
|
||||
|
||||
TupleExpr* = ref object of ListExpr
|
||||
|
||||
DictExpr* = ref object of ASTNode
|
||||
keys*: seq[ASTNode]
|
||||
values*: seq[ASTNode]
|
||||
DictExpr* = ref object of Expression
|
||||
keys*: seq[Expression]
|
||||
values*: seq[Expression]
|
||||
keyType*: IdentExpr
|
||||
valueType*: IdentExpr
|
||||
|
||||
IdentExpr* = ref object of ASTNode
|
||||
IdentExpr* = ref object of Expression
|
||||
name*: Token
|
||||
|
||||
GroupingExpr* = ref object of ASTNode
|
||||
expression*: ASTNode
|
||||
GroupingExpr* = ref object of Expression
|
||||
expression*: Expression
|
||||
|
||||
GetItemExpr* = ref object of ASTNode
|
||||
obj*: ASTNode
|
||||
name*: ASTNode
|
||||
GetItemExpr* = ref object of Expression
|
||||
obj*: Expression
|
||||
name*: IdentExpr
|
||||
|
||||
SetItemExpr* = ref object of GetItemExpr
|
||||
# Since a setItem expression is just
|
||||
# a getItem one followed by an assignment,
|
||||
# inheriting it from getItem makes sense
|
||||
value*: ASTNode
|
||||
value*: Expression
|
||||
|
||||
CallExpr* = ref object of ASTNode
|
||||
CallExpr* = ref object of Expression
|
||||
callee*: ASTNode # The thing being called
|
||||
arguments*: tuple[positionals: seq[ASTNode], keyword: seq[tuple[
|
||||
name: ASTNode, value: ASTNode]]]
|
||||
arguments*: tuple[positionals: seq[Expression], keyword: seq[tuple[
|
||||
name: IdentExpr, value: Expression]]]
|
||||
|
||||
UnaryExpr* = ref object of ASTNode
|
||||
UnaryExpr* = ref object of Expression
|
||||
operator*: Token
|
||||
a*: ASTNode
|
||||
a*: Expression
|
||||
|
||||
BinaryExpr* = ref object of UnaryExpr
|
||||
# Binary expressions can be seen here as unary
|
||||
# expressions with an extra operand so we just
|
||||
# inherit from that and add a second operand
|
||||
b*: ASTNode
|
||||
b*: Expression
|
||||
|
||||
YieldExpr* = ref object of ASTNode
|
||||
expression*: ASTNode
|
||||
YieldExpr* = ref object of Expression
|
||||
expression*: Expression
|
||||
|
||||
AwaitExpr* = ref object of ASTNode
|
||||
expression*: ASTNode
|
||||
AwaitExpr* = ref object of Expression
|
||||
expression*: Expression
|
||||
|
||||
LambdaExpr* = ref object of ASTNode
|
||||
body*: ASTNode
|
||||
arguments*: seq[tuple[name: ASTNode, valueType: ASTNode]]
|
||||
defaults*: seq[ASTNode]
|
||||
LambdaExpr* = ref object of Expression
|
||||
body*: Statement
|
||||
arguments*: seq[tuple[name: IdentExpr, valueType: IdentExpr]]
|
||||
defaults*: seq[Expression]
|
||||
isGenerator*: bool
|
||||
isAsync*: bool
|
||||
returnType*: ASTNode
|
||||
returnType*: IdentExpr
|
||||
|
||||
SliceExpr* = ref object of ASTNode
|
||||
expression*: ASTNode
|
||||
SliceExpr* = ref object of Expression
|
||||
expression*: Expression
|
||||
ends*: seq[ASTNode]
|
||||
|
||||
AssignExpr* = ref object of ASTNode
|
||||
name*: ASTNode
|
||||
value*: ASTNode
|
||||
AssignExpr* = ref object of Expression
|
||||
name*: Expression
|
||||
value*: Expression
|
||||
|
||||
ExprStmt* = ref object of ASTNode
|
||||
expression*: ASTNode
|
||||
ExprStmt* = ref object of Statement
|
||||
expression*: Expression
|
||||
|
||||
ImportStmt* = ref object of ASTNode
|
||||
moduleName*: ASTNode
|
||||
ImportStmt* = ref object of Statement
|
||||
moduleName*: IdentExpr
|
||||
|
||||
AssertStmt* = ref object of ASTNode
|
||||
expression*: ASTNode
|
||||
AssertStmt* = ref object of Statement
|
||||
expression*: Expression
|
||||
|
||||
RaiseStmt* = ref object of ASTNode
|
||||
exception*: ASTNode
|
||||
RaiseStmt* = ref object of Statement
|
||||
exception*: Expression
|
||||
|
||||
BlockStmt* = ref object of ASTNode
|
||||
code*: seq[ASTNode]
|
||||
BlockStmt* = ref object of Statement
|
||||
code*: seq[Declaration]
|
||||
|
||||
ForStmt* = ref object of ASTNode
|
||||
ForStmt* = ref object of Statement
|
||||
discard # Unused
|
||||
|
||||
ForEachStmt* = ref object of ASTNode
|
||||
ForEachStmt* = ref object of Statement
|
||||
identifier*: ASTNode
|
||||
expression*: ASTNode
|
||||
body*: ASTNode
|
||||
|
||||
DeferStmt* = ref object of ASTNode
|
||||
DeferStmt* = ref object of Statement
|
||||
expression*: ASTNode
|
||||
|
||||
TryStmt* = ref object of ASTNode
|
||||
body*: ASTNode
|
||||
handlers*: seq[tuple[body: ASTNode, exc: ASTNode, name: ASTNode]]
|
||||
finallyClause*: ASTNode
|
||||
elseClause*: ASTNode
|
||||
TryStmt* = ref object of Statement
|
||||
body*: Statement
|
||||
handlers*: seq[tuple[body: Statement, exc: IdentExpr, name: IdentExpr]]
|
||||
finallyClause*: Statement
|
||||
elseClause*: Statement
|
||||
|
||||
WhileStmt* = ref object of ASTNode
|
||||
condition*: ASTNode
|
||||
body*: ASTNode
|
||||
WhileStmt* = ref object of Statement
|
||||
condition*: Expression
|
||||
body*: Statement
|
||||
|
||||
AwaitStmt* = ref object of ASTNode
|
||||
expression*: ASTNode
|
||||
AwaitStmt* = ref object of Statement
|
||||
expression*: Expression
|
||||
|
||||
BreakStmt* = ref object of ASTNode
|
||||
BreakStmt* = ref object of Statement
|
||||
|
||||
ContinueStmt* = ref object of ASTNode
|
||||
ContinueStmt* = ref object of Statement
|
||||
|
||||
ReturnStmt* = ref object of ASTNode
|
||||
value*: ASTNode
|
||||
ReturnStmt* = ref object of Statement
|
||||
value*: Expression
|
||||
|
||||
IfStmt* = ref object of ASTNode
|
||||
condition*: ASTNode
|
||||
thenBranch*: ASTNode
|
||||
elseBranch*: ASTNode
|
||||
IfStmt* = ref object of Statement
|
||||
condition*: Expression
|
||||
thenBranch*: Statement
|
||||
elseBranch*: Statement
|
||||
|
||||
YieldStmt* = ref object of ASTNode
|
||||
expression*: ASTNode
|
||||
|
||||
Declaration* = ref object of ASTNode
|
||||
closedOver*: bool
|
||||
pragmas*: seq[Token]
|
||||
YieldStmt* = ref object of Statement
|
||||
expression*: Expression
|
||||
|
||||
VarDecl* = ref object of Declaration
|
||||
name*: ASTNode
|
||||
value*: ASTNode
|
||||
name*: IdentExpr
|
||||
value*: Expression
|
||||
isConst*: bool
|
||||
isPrivate*: bool
|
||||
isLet*: bool
|
||||
valueType*: ASTNode
|
||||
valueType*: IdentExpr
|
||||
|
||||
FunDecl* = ref object of Declaration
|
||||
name*: ASTNode
|
||||
body*: ASTNode
|
||||
arguments*: seq[tuple[name: ASTNode, valueType: ASTNode]]
|
||||
defaults*: seq[ASTNode]
|
||||
name*: IdentExpr
|
||||
body*: Statement
|
||||
arguments*: seq[tuple[name: IdentExpr, valueType: IdentExpr]]
|
||||
defaults*: seq[Expression]
|
||||
isAsync*: bool
|
||||
isGenerator*: bool
|
||||
isPrivate*: bool
|
||||
returnType*: ASTNode
|
||||
|
||||
Expression* = LiteralExpr | ListExpr | GetItemExpr | SetItemExpr | UnaryExpr | BinaryExpr | CallExpr | AssignExpr |
|
||||
GroupingExpr | IdentExpr | DictExpr | TupleExpr | SetExpr | TrueExpr | FalseExpr | NilExpr |
|
||||
NanExpr | InfExpr
|
||||
|
||||
Statement* = ExprStmt | ImportStmt | AssertStmt | RaiseStmt | BlockStmt | ForStmt | WhileStmt |
|
||||
ForStmt | BreakStmt | ContinueStmt | ReturnStmt | IfStmt
|
||||
|
||||
|
||||
returnType*: IdentExpr
|
||||
|
||||
|
||||
proc newASTNode*(kind: NodeKind, token: Token): ASTNode =
|
||||
|
@ -356,19 +357,25 @@ proc newStrExpr*(literal: Token): StrExpr =
|
|||
result.token = literal
|
||||
|
||||
|
||||
proc newCharExpr*(literal: Token): CharExpr =
|
||||
result = CharExpr(kind: charExpr)
|
||||
result.literal = literal
|
||||
result.token = literal
|
||||
|
||||
|
||||
proc newIdentExpr*(name: Token): IdentExpr =
|
||||
result = IdentExpr(kind: identExpr)
|
||||
result.name = name
|
||||
result.token = name
|
||||
|
||||
|
||||
proc newGroupingExpr*(expression: ASTNode, token: Token): GroupingExpr =
|
||||
proc newGroupingExpr*(expression: Expression, token: Token): GroupingExpr =
|
||||
result = GroupingExpr(kind: groupingExpr)
|
||||
result.expression = expression
|
||||
result.token = token
|
||||
|
||||
|
||||
proc newLambdaExpr*(arguments: seq[tuple[name: ASTNode, valueType: ASTNode]], defaults: seq[ASTNode], body: ASTNode,
|
||||
proc newLambdaExpr*(arguments: seq[tuple[name: IdentExpr, valueType: IdentExpr]], defaults: seq[Expression], body: Declaration,
|
||||
isGenerator: bool, isAsync: bool, token: Token): LambdaExpr =
|
||||
result = LambdaExpr(kind: lambdaExpr)
|
||||
result.body = body
|
||||
|
@ -379,42 +386,42 @@ proc newLambdaExpr*(arguments: seq[tuple[name: ASTNode, valueType: ASTNode]], de
|
|||
result.token = token
|
||||
|
||||
|
||||
proc newGetItemExpr*(obj: ASTNode, name: ASTNode, token: Token): GetItemExpr =
|
||||
proc newGetItemExpr*(obj: Expression, name: IdentExpr, token: Token): GetItemExpr =
|
||||
result = GetItemExpr(kind: getItemExpr)
|
||||
result.obj = obj
|
||||
result.name = name
|
||||
result.token = token
|
||||
|
||||
|
||||
proc newListExpr*(members: seq[ASTNode], token: Token): ListExpr =
|
||||
proc newListExpr*(members: seq[Expression], token: Token): ListExpr =
|
||||
result = ListExpr(kind: listExpr)
|
||||
result.members = members
|
||||
result.token = token
|
||||
result.literal = result.token
|
||||
|
||||
|
||||
proc newSetExpr*(members: seq[ASTNode], token: Token): SetExpr =
|
||||
proc newSetExpr*(members: seq[Expression], token: Token): SetExpr =
|
||||
result = SetExpr(kind: setExpr)
|
||||
result.members = members
|
||||
result.token = token
|
||||
result.literal = result.token
|
||||
|
||||
|
||||
proc newTupleExpr*(members: seq[ASTNode], token: Token): TupleExpr =
|
||||
proc newTupleExpr*(members: seq[Expression], token: Token): TupleExpr =
|
||||
result = TupleExpr(kind: tupleExpr)
|
||||
result.members = members
|
||||
result.token = token
|
||||
result.literal = result.token
|
||||
|
||||
|
||||
proc newDictExpr*(keys, values: seq[ASTNode], token: Token): DictExpr =
|
||||
proc newDictExpr*(keys, values: seq[Expression], token: Token): DictExpr =
|
||||
result = DictExpr(kind: dictExpr)
|
||||
result.keys = keys
|
||||
result.values = values
|
||||
result.token = token
|
||||
|
||||
|
||||
proc newSetItemExpr*(obj, name, value: ASTNode, token: Token): SetItemExpr =
|
||||
proc newSetItemExpr*(obj: Expression, name: IdentExpr, value: Expression, token: Token): SetItemExpr =
|
||||
result = SetItemExpr(kind: setItemExpr)
|
||||
result.obj = obj
|
||||
result.name = name
|
||||
|
@ -422,8 +429,8 @@ proc newSetItemExpr*(obj, name, value: ASTNode, token: Token): SetItemExpr =
|
|||
result.token = token
|
||||
|
||||
|
||||
proc newCallExpr*(callee: ASTNode, arguments: tuple[positionals: seq[ASTNode],
|
||||
keyword: seq[tuple[name: ASTNode, value: ASTNode]]],
|
||||
proc newCallExpr*(callee: ASTNode, arguments: tuple[positionals: seq[Expression],
|
||||
keyword: seq[tuple[name: IdentExpr, value: Expression]]],
|
||||
token: Token): CallExpr =
|
||||
result = CallExpr(kind: callExpr)
|
||||
result.callee = callee
|
||||
|
@ -431,7 +438,7 @@ proc newCallExpr*(callee: ASTNode, arguments: tuple[positionals: seq[ASTNode],
|
|||
result.token = token
|
||||
|
||||
|
||||
proc newSliceExpr*(expression: ASTNode, ends: seq[ASTNode],
|
||||
proc newSliceExpr*(expression: Expression, ends: seq[ASTNode],
|
||||
token: Token): SliceExpr =
|
||||
result = SliceExpr(kind: sliceExpr)
|
||||
result.expression = expression
|
||||
|
@ -439,14 +446,14 @@ proc newSliceExpr*(expression: ASTNode, ends: seq[ASTNode],
|
|||
result.token = token
|
||||
|
||||
|
||||
proc newUnaryExpr*(operator: Token, a: ASTNode): UnaryExpr =
|
||||
proc newUnaryExpr*(operator: Token, a: Expression): UnaryExpr =
|
||||
result = UnaryExpr(kind: unaryExpr)
|
||||
result.operator = operator
|
||||
result.a = a
|
||||
result.token = result.operator
|
||||
|
||||
|
||||
proc newBinaryExpr*(a: ASTNode, operator: Token, b: ASTNode): BinaryExpr =
|
||||
proc newBinaryExpr*(a: Expression, operator: Token, b: Expression): BinaryExpr =
|
||||
result = BinaryExpr(kind: binaryExpr)
|
||||
result.operator = operator
|
||||
result.a = a
|
||||
|
@ -454,70 +461,70 @@ proc newBinaryExpr*(a: ASTNode, operator: Token, b: ASTNode): BinaryExpr =
|
|||
result.token = operator
|
||||
|
||||
|
||||
proc newYieldExpr*(expression: ASTNode, token: Token): YieldExpr =
|
||||
proc newYieldExpr*(expression: Expression, token: Token): YieldExpr =
|
||||
result = YieldExpr(kind: yieldExpr)
|
||||
result.expression = expression
|
||||
result.token = token
|
||||
|
||||
|
||||
proc newAssignExpr*(name, value: ASTNode, token: Token): AssignExpr =
|
||||
proc newAssignExpr*(name: Expression, value: Expression, token: Token): AssignExpr =
|
||||
result = AssignExpr(kind: assignExpr)
|
||||
result.name = name
|
||||
result.value = value
|
||||
result.token = token
|
||||
|
||||
|
||||
proc newAwaitExpr*(expression: ASTNode, token: Token): AwaitExpr =
|
||||
proc newAwaitExpr*(expression: Expression, token: Token): AwaitExpr =
|
||||
result = AwaitExpr(kind: awaitExpr)
|
||||
result.expression = expression
|
||||
result.token = token
|
||||
|
||||
|
||||
proc newExprStmt*(expression: ASTNode, token: Token): ExprStmt =
|
||||
proc newExprStmt*(expression: Expression, token: Token): ExprStmt =
|
||||
result = ExprStmt(kind: exprStmt)
|
||||
result.expression = expression
|
||||
result.token = token
|
||||
|
||||
|
||||
proc newImportStmt*(moduleName: ASTNode, token: Token): ImportStmt =
|
||||
proc newImportStmt*(moduleName: IdentExpr, token: Token): ImportStmt =
|
||||
result = ImportStmt(kind: importStmt)
|
||||
result.moduleName = moduleName
|
||||
result.token = token
|
||||
|
||||
|
||||
proc newYieldStmt*(expression: ASTNode, token: Token): YieldStmt =
|
||||
proc newYieldStmt*(expression: Expression, token: Token): YieldStmt =
|
||||
result = YieldStmt(kind: yieldStmt)
|
||||
result.expression = expression
|
||||
result.token = token
|
||||
|
||||
|
||||
proc newAwaitStmt*(expression: ASTNode, token: Token): AwaitExpr =
|
||||
result = AwaitExpr(kind: awaitExpr)
|
||||
proc newAwaitStmt*(expression: Expression, token: Token): AwaitStmt =
|
||||
result = AwaitStmt(kind: awaitStmt)
|
||||
result.expression = expression
|
||||
result.token = token
|
||||
|
||||
|
||||
proc newAssertStmt*(expression: ASTNode, token: Token): AssertStmt =
|
||||
proc newAssertStmt*(expression: Expression, token: Token): AssertStmt =
|
||||
result = AssertStmt(kind: assertStmt)
|
||||
result.expression = expression
|
||||
result.token = token
|
||||
|
||||
|
||||
proc newDeferStmt*(expression: ASTNode, token: Token): DeferStmt =
|
||||
proc newDeferStmt*(expression: Expression, token: Token): DeferStmt =
|
||||
result = DeferStmt(kind: deferStmt)
|
||||
result.expression = expression
|
||||
result.token = token
|
||||
|
||||
|
||||
proc newRaiseStmt*(exception: ASTNode, token: Token): RaiseStmt =
|
||||
proc newRaiseStmt*(exception: Expression, token: Token): RaiseStmt =
|
||||
result = RaiseStmt(kind: raiseStmt)
|
||||
result.exception = exception
|
||||
result.token = token
|
||||
|
||||
|
||||
proc newTryStmt*(body: ASTNode, handlers: seq[tuple[body: ASTNode, exc: ASTNode, name: ASTNode]],
|
||||
finallyClause: ASTNode,
|
||||
elseClause: ASTNode, token: Token): TryStmt =
|
||||
proc newTryStmt*(body: Statement, handlers: seq[tuple[body: Statement, exc: IdentExpr, name: IdentExpr]],
|
||||
finallyClause: Statement,
|
||||
elseClause: Statement, token: Token): TryStmt =
|
||||
result = TryStmt(kind: tryStmt)
|
||||
result.body = body
|
||||
result.handlers = handlers
|
||||
|
@ -526,20 +533,20 @@ proc newTryStmt*(body: ASTNode, handlers: seq[tuple[body: ASTNode, exc: ASTNode,
|
|||
result.token = token
|
||||
|
||||
|
||||
proc newBlockStmt*(code: seq[ASTNode], token: Token): BlockStmt =
|
||||
proc newBlockStmt*(code: seq[Declaration], token: Token): BlockStmt =
|
||||
result = BlockStmt(kind: blockStmt)
|
||||
result.code = code
|
||||
result.token = token
|
||||
|
||||
|
||||
proc newWhileStmt*(condition: ASTNode, body: ASTNode, token: Token): WhileStmt =
|
||||
proc newWhileStmt*(condition: Expression, body: Statement, token: Token): WhileStmt =
|
||||
result = WhileStmt(kind: whileStmt)
|
||||
result.condition = condition
|
||||
result.body = body
|
||||
result.token = token
|
||||
|
||||
|
||||
proc newForEachStmt*(identifier: ASTNode, expression, body: ASTNode,
|
||||
proc newForEachStmt*(identifier: IdentExpr, expression: Expression, body: Statement,
|
||||
token: Token): ForEachStmt =
|
||||
result = ForEachStmt(kind: forEachStmt)
|
||||
result.identifier = identifier
|
||||
|
@ -558,13 +565,13 @@ proc newContinueStmt*(token: Token): ContinueStmt =
|
|||
result.token = token
|
||||
|
||||
|
||||
proc newReturnStmt*(value: ASTNode, token: Token): ReturnStmt =
|
||||
proc newReturnStmt*(value: Expression, token: Token): ReturnStmt =
|
||||
result = ReturnStmt(kind: returnStmt)
|
||||
result.value = value
|
||||
result.token = token
|
||||
|
||||
|
||||
proc newIfStmt*(condition: ASTNode, thenBranch, elseBranch: ASTNode,
|
||||
proc newIfStmt*(condition: Expression, thenBranch, elseBranch: Statement,
|
||||
token: Token): IfStmt =
|
||||
result = IfStmt(kind: ifStmt)
|
||||
result.condition = condition
|
||||
|
@ -573,9 +580,9 @@ proc newIfStmt*(condition: ASTNode, thenBranch, elseBranch: ASTNode,
|
|||
result.token = token
|
||||
|
||||
|
||||
proc newVarDecl*(name: ASTNode, value: ASTNode, isConst: bool = false,
|
||||
proc newVarDecl*(name: IdentExpr, value: Expression, isConst: bool = false,
|
||||
isPrivate: bool = true, token: Token, closedOver: bool,
|
||||
isLet: bool = false, valueType: ASTNode): VarDecl =
|
||||
isLet: bool = false, valueType: IdentExpr): VarDecl =
|
||||
result = VarDecl(kind: varDecl)
|
||||
result.name = name
|
||||
result.value = value
|
||||
|
@ -586,8 +593,8 @@ proc newVarDecl*(name: ASTNode, value: ASTNode, isConst: bool = false,
|
|||
result.valueType = valueType
|
||||
|
||||
|
||||
proc newFunDecl*(name: ASTNode, arguments: seq[tuple[name: ASTNode, valueType: ASTNode]], defaults: seq[ASTNode],
|
||||
body: ASTNode, isAsync, isGenerator: bool,
|
||||
proc newFunDecl*(name: IdentExpr, arguments: seq[tuple[name: IdentExpr, valueType: IdentExpr]], defaults: seq[Expression],
|
||||
body: Statement, isAsync, isGenerator: bool,
|
||||
isPrivate: bool, token: Token, closedOver: bool): FunDecl =
|
||||
result = FunDecl(kind: funDecl)
|
||||
result.name = name
|
||||
|
|
|
@ -57,9 +57,7 @@ type
|
|||
# used for more complex opcodes. All
|
||||
# arguments to opcodes (if they take
|
||||
# arguments) come from popping off the
|
||||
# stack. Unsupported operations will
|
||||
# raise TypeError or ValueError exceptions
|
||||
# and never fail silently
|
||||
# stack
|
||||
LoadConstant = 0u8, # Pushes constant at position x in the constant table onto the stack
|
||||
## Binary operators
|
||||
UnaryNegate, # Pushes the result of -x onto the stack
|
||||
|
@ -169,7 +167,7 @@ const simpleInstructions* = {OpCode.Return, BinaryAdd, BinaryMultiply,
|
|||
OpCode.LessOrEqual, BinaryOr, BinaryAnd,
|
||||
UnaryNot, BinaryFloorDiv, BinaryOf, OpCode.Raise,
|
||||
ReRaise, BeginTry, FinishTry, OpCode.Yield, OpCode.Await,
|
||||
MakeClass, ImplicitReturn}
|
||||
MakeClass, ImplicitReturn, UnaryPlus}
|
||||
|
||||
# Constant instructions are instructions that operate on the bytecode constant table
|
||||
const constantInstructions* = {LoadConstant, }
|
||||
|
|
|
@ -42,7 +42,7 @@ type
|
|||
|
||||
# Literal types
|
||||
Integer, Float, String, Identifier,
|
||||
Binary, Octal, Hex
|
||||
Binary, Octal, Hex, Char
|
||||
|
||||
# Brackets, parentheses,
|
||||
# operators and others
|
||||
|
|
|
@ -60,7 +60,7 @@ type
|
|||
# is at the top-level. It allows the
|
||||
# parser to detect errors like return
|
||||
# outside functions
|
||||
currentFunction: ASTNode
|
||||
currentFunction: Declaration
|
||||
# Stores the current scope depth (0 = global, > 0 local)
|
||||
scopeDepth: int
|
||||
# We store user-defined operators for later use
|
||||
|
@ -194,15 +194,15 @@ proc expect(self: Parser, kinds: openarray[TokenType], message: string = "") =
|
|||
|
||||
|
||||
# Forward declarations
|
||||
proc expression(self: Parser): ASTNode
|
||||
proc expressionStatement(self: Parser): ASTNode
|
||||
proc statement(self: Parser): ASTNode
|
||||
proc varDecl(self: Parser, isLet: bool = false, isConst: bool = false): ASTNode
|
||||
proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isLambda: bool = false, isOperator: bool = false): ASTNode
|
||||
proc declaration(self: Parser): ASTNode
|
||||
proc expression(self: Parser): Expression
|
||||
proc expressionStatement(self: Parser): Statement
|
||||
proc statement(self: Parser): Statement
|
||||
proc varDecl(self: Parser, isLet: bool = false, isConst: bool = false): Declaration
|
||||
proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isLambda: bool = false, isOperator: bool = false): Declaration
|
||||
proc declaration(self: Parser): Declaration
|
||||
|
||||
|
||||
proc primary(self: Parser): ASTNode =
|
||||
proc primary(self: Parser): Expression =
|
||||
## Parses primary expressions such
|
||||
## as integer literals and keywords
|
||||
## that map to builtin types (true,
|
||||
|
@ -331,13 +331,13 @@ proc primary(self: Parser): ASTNode =
|
|||
self.error("invalid syntax")
|
||||
|
||||
|
||||
proc makeCall(self: Parser, callee: ASTNode): ASTNode =
|
||||
proc makeCall(self: Parser, callee: Expression): Expression =
|
||||
## Utility function called iteratively by self.call()
|
||||
## to parse a function call
|
||||
let tok = self.peek(-1)
|
||||
var argNames: seq[ASTNode] = @[]
|
||||
var arguments: tuple[positionals: seq[ASTNode], keyword: seq[tuple[name: ASTNode, value: ASTNode]]] = (positionals: @[], keyword: @[])
|
||||
var argument: ASTNode = nil
|
||||
var argNames: seq[IdentExpr] = @[]
|
||||
var arguments: tuple[positionals: seq[Expression], keyword: seq[tuple[name: IdentExpr, value: Expression]]] = (positionals: @[], keyword: @[])
|
||||
var argument: Expression = nil
|
||||
var argCount = 0
|
||||
if not self.check(RightParen):
|
||||
while true:
|
||||
|
@ -346,10 +346,11 @@ proc makeCall(self: Parser, callee: ASTNode): ASTNode =
|
|||
break
|
||||
argument = self.expression()
|
||||
if argument.kind == assignExpr:
|
||||
if AssignExpr(argument).name in argNames:
|
||||
# TODO: This will explode with slices!
|
||||
if IdentExpr(AssignExpr(argument).name) in argNames:
|
||||
self.error("duplicate keyword argument in call")
|
||||
argNames.add(AssignExpr(argument).name)
|
||||
arguments.keyword.add((name: AssignExpr(argument).name, value: AssignExpr(argument).value))
|
||||
argNames.add(IdentExpr(AssignExpr(argument).name))
|
||||
arguments.keyword.add((name: IdentExpr(AssignExpr(argument).name), value: AssignExpr(argument).value))
|
||||
elif arguments.keyword.len() == 0:
|
||||
arguments.positionals.add(argument)
|
||||
else:
|
||||
|
@ -361,8 +362,8 @@ proc makeCall(self: Parser, callee: ASTNode): ASTNode =
|
|||
result = newCallExpr(callee, arguments, tok)
|
||||
|
||||
|
||||
proc call(self: Parser): ASTNode =
|
||||
## Parses func5ion calls, object field
|
||||
proc call(self: Parser): Expression =
|
||||
## Parses function calls, object field
|
||||
## accessing and slicing expressions
|
||||
result = self.primary()
|
||||
while true:
|
||||
|
@ -388,7 +389,7 @@ proc call(self: Parser): ASTNode =
|
|||
break
|
||||
|
||||
|
||||
proc unary(self: Parser): ASTNode =
|
||||
proc unary(self: Parser): Expression =
|
||||
## Parses unary expressions
|
||||
if self.match([Minus, Tilde, LogicalNot, Plus]):
|
||||
result = newUnaryExpr(self.peek(-1), self.unary())
|
||||
|
@ -396,7 +397,7 @@ proc unary(self: Parser): ASTNode =
|
|||
result = self.call()
|
||||
|
||||
|
||||
proc customUnaryOperator(self: Parser): ASTNode =
|
||||
proc customUnaryOperator(self: Parser): Expression =
|
||||
## Parses user-defined unary expressions
|
||||
if self.peek().lexeme in self.operators:
|
||||
discard self.step()
|
||||
|
@ -405,120 +406,120 @@ proc customUnaryOperator(self: Parser): ASTNode =
|
|||
result = self.unary()
|
||||
|
||||
|
||||
proc pow(self: Parser): ASTNode =
|
||||
proc pow(self: Parser): Expression =
|
||||
## Parses exponentiation expressions
|
||||
result = self.customUnaryOperator()
|
||||
var operator: Token
|
||||
var right: ASTNode
|
||||
var right: Expression
|
||||
while self.match(DoubleStar):
|
||||
operator = self.peek(-1)
|
||||
right = self.customUnaryOperator()
|
||||
result = newBinaryExpr(result, operator, right)
|
||||
|
||||
|
||||
proc mul(self: Parser): ASTNode =
|
||||
proc mul(self: Parser): Expression =
|
||||
## Parses multiplication and division expressions
|
||||
result = self.pow()
|
||||
var operator: Token
|
||||
var right: ASTNode
|
||||
var right: Expression
|
||||
while self.match([Slash, Percentage, FloorDiv, Star]):
|
||||
operator = self.peek(-1)
|
||||
right = self.pow()
|
||||
result = newBinaryExpr(result, operator, right)
|
||||
|
||||
|
||||
proc add(self: Parser): ASTNode =
|
||||
proc add(self: Parser): Expression =
|
||||
## Parses addition and subtraction expressions
|
||||
result = self.mul()
|
||||
var operator: Token
|
||||
var right: ASTNode
|
||||
var right: Expression
|
||||
while self.match([Plus, Minus]):
|
||||
operator = self.peek(-1)
|
||||
right = self.mul()
|
||||
result = newBinaryExpr(result, operator, right)
|
||||
|
||||
|
||||
proc comparison(self: Parser): ASTNode =
|
||||
proc comparison(self: Parser): Expression =
|
||||
## Parses other comparison expressions
|
||||
## and some other operators
|
||||
result = self.add()
|
||||
var operator: Token
|
||||
var right: ASTNode
|
||||
var right: Expression
|
||||
while self.match([LessThan, GreaterThan, LessOrEqual, GreaterOrEqual, Is, As, Of, IsNot]):
|
||||
operator = self.peek(-1)
|
||||
right = self.add()
|
||||
result = newBinaryExpr(result, operator, right)
|
||||
|
||||
|
||||
proc equality(self: Parser): ASTNode =
|
||||
proc equality(self: Parser): Expression =
|
||||
## Parses equality expressions
|
||||
result = self.comparison()
|
||||
var operator: Token
|
||||
var right: ASTNode
|
||||
var right: Expression
|
||||
while self.match([DoubleEqual, NotEqual]):
|
||||
operator = self.peek(-1)
|
||||
right = self.comparison()
|
||||
result = newBinaryExpr(result, operator, right)
|
||||
|
||||
|
||||
proc logicalAnd(self: Parser): ASTNode =
|
||||
proc logicalAnd(self: Parser): Expression =
|
||||
## Parses logical and expressions
|
||||
## (a and b)
|
||||
result = self.equality()
|
||||
var operator: Token
|
||||
var right: ASTNode
|
||||
var right: Expression
|
||||
while self.match(LogicalAnd):
|
||||
operator = self.peek(-1)
|
||||
right = self.equality()
|
||||
result = newBinaryExpr(result, operator, right)
|
||||
|
||||
|
||||
proc logicalOr(self: Parser): ASTNode =
|
||||
proc logicalOr(self: Parser): Expression =
|
||||
## Parses logical or expressions
|
||||
## (a or b)
|
||||
result = self.logicalAnd()
|
||||
var operator: Token
|
||||
var right: ASTNode
|
||||
var right: Expression
|
||||
while self.match(LogicalOr):
|
||||
operator = self.peek(-1)
|
||||
right = self.logicalAnd()
|
||||
result = newBinaryExpr(result, operator, right)
|
||||
|
||||
|
||||
proc bitwiseAnd(self: Parser): ASTNode =
|
||||
proc bitwiseAnd(self: Parser): Expression =
|
||||
## Parses a & b expressions
|
||||
result = self.logicalOr()
|
||||
var operator: Token
|
||||
var right: ASTNode
|
||||
var right: Expression
|
||||
while self.match(Pipe):
|
||||
operator = self.peek(-1)
|
||||
right = self.logicalOr()
|
||||
result = newBinaryExpr(result, operator, right)
|
||||
|
||||
|
||||
proc bitwiseOr(self: Parser): ASTNode =
|
||||
proc bitwiseOr(self: Parser): Expression =
|
||||
## Parses a | b expressions
|
||||
result = self.bitwiseAnd()
|
||||
var operator: Token
|
||||
var right: ASTNode
|
||||
var right: Expression
|
||||
while self.match(Ampersand):
|
||||
operator = self.peek(-1)
|
||||
right = self.bitwiseAnd()
|
||||
result = newBinaryExpr(result, operator, right)
|
||||
|
||||
|
||||
proc customBinaryOperator(self: Parser): ASTNode =
|
||||
proc customBinaryOperator(self: Parser): Expression =
|
||||
## Parses user-defined binary operators
|
||||
result = self.bitwiseOr()
|
||||
var operator: Token
|
||||
var right: ASTNode
|
||||
var right: Expression
|
||||
while self.peek().lexeme in self.operators:
|
||||
operator = self.step()
|
||||
right = self.bitwiseOr()
|
||||
result = newBinaryExpr(result, operator, right)
|
||||
|
||||
|
||||
proc assignment(self: Parser): ASTNode =
|
||||
proc assignment(self: Parser): Expression =
|
||||
## Parses assignment, the highest-level
|
||||
## expression (including stuff like a.b = 1).
|
||||
## Slice assignments are also parsed here
|
||||
|
@ -536,7 +537,7 @@ proc assignment(self: Parser): ASTNode =
|
|||
self.error("invalid assignment target")
|
||||
|
||||
|
||||
proc assertStmt(self: Parser): ASTNode =
|
||||
proc assertStmt(self: Parser): Statement =
|
||||
## Parses "assert" statements, which
|
||||
## raise an error if the expression
|
||||
## fed into them is falsey
|
||||
|
@ -556,13 +557,13 @@ proc endScope(self: Parser) =
|
|||
dec(self.scopeDepth)
|
||||
|
||||
|
||||
proc blockStmt(self: Parser): ASTNode =
|
||||
proc blockStmt(self: Parser): Statement =
|
||||
## Parses block statements. A block
|
||||
## statement simply opens a new local
|
||||
## scope
|
||||
self.beginScope()
|
||||
let tok = self.peek(-1)
|
||||
var code: seq[ASTNode] = @[]
|
||||
var code: seq[Declaration] = @[]
|
||||
while not self.check(RightBrace) and not self.done():
|
||||
code.add(self.declaration())
|
||||
self.expect(RightBrace, "expecting '}'")
|
||||
|
@ -570,7 +571,7 @@ proc blockStmt(self: Parser): ASTNode =
|
|||
self.endScope()
|
||||
|
||||
|
||||
proc breakStmt(self: Parser): ASTNode =
|
||||
proc breakStmt(self: Parser): Statement =
|
||||
## Parses break statements
|
||||
let tok = self.peek(-1)
|
||||
if self.currentLoop != Loop:
|
||||
|
@ -579,7 +580,7 @@ proc breakStmt(self: Parser): ASTNode =
|
|||
result = newBreakStmt(tok)
|
||||
|
||||
|
||||
proc deferStmt(self: Parser): ASTNode =
|
||||
proc deferStmt(self: Parser): Statement =
|
||||
## Parses defer statements
|
||||
let tok = self.peek(-1)
|
||||
if self.currentFunction == nil:
|
||||
|
@ -588,7 +589,7 @@ proc deferStmt(self: Parser): ASTNode =
|
|||
endOfLine("missing semicolon after defer statement")
|
||||
|
||||
|
||||
proc continueStmt(self: Parser): ASTNode =
|
||||
proc continueStmt(self: Parser): Statement =
|
||||
## Parses continue statements
|
||||
let tok = self.peek(-1)
|
||||
if self.currentLoop != Loop:
|
||||
|
@ -597,12 +598,12 @@ proc continueStmt(self: Parser): ASTNode =
|
|||
result = newContinueStmt(tok)
|
||||
|
||||
|
||||
proc returnStmt(self: Parser): ASTNode =
|
||||
proc returnStmt(self: Parser): Statement =
|
||||
## Parses return statements
|
||||
let tok = self.peek(-1)
|
||||
if self.currentFunction == nil:
|
||||
self.error("'return' cannot be used outside functions")
|
||||
var value: ASTNode = newNilExpr(Token(lexeme: "nil"))
|
||||
var value: Expression = newNilExpr(Token(lexeme: "nil"))
|
||||
if not self.check(Semicolon):
|
||||
# Since return can be used on its own too
|
||||
# (in which case it implicitly returns nil),
|
||||
|
@ -613,7 +614,7 @@ proc returnStmt(self: Parser): ASTNode =
|
|||
result = newReturnStmt(value, tok)
|
||||
|
||||
|
||||
proc yieldStmt(self: Parser): ASTNode =
|
||||
proc yieldStmt(self: Parser): Statement =
|
||||
## Parses yield statements
|
||||
let tok = self.peek(-1)
|
||||
if self.currentFunction == nil:
|
||||
|
@ -627,7 +628,7 @@ proc yieldStmt(self: Parser): ASTNode =
|
|||
endOfLine("missing semicolon after yield statement")
|
||||
|
||||
|
||||
proc awaitStmt(self: Parser): ASTNode =
|
||||
proc awaitStmt(self: Parser): Statement =
|
||||
## Parses await statements
|
||||
let tok = self.peek(-1)
|
||||
if self.currentFunction == nil:
|
||||
|
@ -638,9 +639,9 @@ proc awaitStmt(self: Parser): ASTNode =
|
|||
endOfLine("missing semicolon after await statement")
|
||||
|
||||
|
||||
proc raiseStmt(self: Parser): ASTNode =
|
||||
proc raiseStmt(self: Parser): Statement =
|
||||
## Parses raise statements
|
||||
var exception: ASTNode
|
||||
var exception: Expression
|
||||
let tok = self.peek(-1)
|
||||
if not self.check(Semicolon):
|
||||
# Raise can be used on its own, in which
|
||||
|
@ -650,7 +651,7 @@ proc raiseStmt(self: Parser): ASTNode =
|
|||
result = newRaiseStmt(exception, tok)
|
||||
|
||||
|
||||
proc forEachStmt(self: Parser): ASTNode =
|
||||
proc forEachStmt(self: Parser): Statement =
|
||||
## Parses C#-like foreach loops
|
||||
let tok = self.peek(-1)
|
||||
var enclosingLoop = self.currentLoop
|
||||
|
@ -666,7 +667,7 @@ proc forEachStmt(self: Parser): ASTNode =
|
|||
self.currentLoop = enclosingLoop
|
||||
|
||||
|
||||
proc importStmt(self: Parser, fromStmt: bool = false): ASTNode =
|
||||
proc importStmt(self: Parser, fromStmt: bool = false): Statement =
|
||||
## Parses import statements
|
||||
var tok: Token
|
||||
if fromStmt:
|
||||
|
@ -680,26 +681,28 @@ proc importStmt(self: Parser, fromStmt: bool = false): ASTNode =
|
|||
|
||||
|
||||
|
||||
proc tryStmt(self: Parser): ASTNode =
|
||||
proc tryStmt(self: Parser): Statement =
|
||||
## Parses try/except/else/finally blocks
|
||||
let tok = self.peek(-1)
|
||||
var body = self.statement()
|
||||
var handlers: seq[tuple[body, exc, name: ASTNode]] = @[]
|
||||
var finallyClause: ASTNode
|
||||
var elseClause: ASTNode
|
||||
var asName: ASTNode
|
||||
var excName: ASTNode
|
||||
var handlerBody: ASTNode
|
||||
var handlers: seq[tuple[body: Statement, exc: IdentExpr, name: IdentExpr]] = @[]
|
||||
var finallyClause: Statement
|
||||
var elseClause: Statement
|
||||
var asName: IdentExpr
|
||||
var excName: Expression
|
||||
var handlerBody: Statement
|
||||
while self.match(Except):
|
||||
excName = self.expression()
|
||||
if excName.kind == identExpr:
|
||||
handlerBody = self.statement()
|
||||
handlers.add((body: handlerBody, exc: excName, name: asName))
|
||||
handlers.add((body: handlerBody, exc: IdentExpr(excName), name: asName))
|
||||
asName = nil
|
||||
elif excName.kind == binaryExpr and BinaryExpr(excName).operator.kind == As:
|
||||
asName = BinaryExpr(excName).b
|
||||
asName = IdentExpr(BinaryExpr(excName).b)
|
||||
if BinaryExpr(excName).b.kind != identExpr:
|
||||
self.error("expecting alias name after 'except ... as'")
|
||||
elif BinaryExpr(excName).a.kind != identExpr:
|
||||
self.error("expecting exception name")
|
||||
excName = BinaryExpr(excName).a
|
||||
# Note how we don't use elif here: when the if above sets excName to As'
|
||||
# first operand, that might be a tuple, which we unpack below
|
||||
|
@ -710,7 +713,9 @@ proc tryStmt(self: Parser): ASTNode =
|
|||
# types each time
|
||||
handlerBody = self.statement()
|
||||
for element in TupleExpr(excName).members:
|
||||
handlers.add((body: handlerBody, exc: element, name: asName))
|
||||
if element.kind != identExpr:
|
||||
self.error("expecting exception name")
|
||||
handlers.add((body: handlerBody, exc: IdentExpr(element), name: asName))
|
||||
continue
|
||||
else:
|
||||
excName = nil
|
||||
|
@ -726,7 +731,7 @@ proc tryStmt(self: Parser): ASTNode =
|
|||
result = newTryStmt(body, handlers, finallyClause, elseClause, tok)
|
||||
|
||||
|
||||
proc whileStmt(self: Parser): ASTNode =
|
||||
proc whileStmt(self: Parser): Statement =
|
||||
## Parses a C-style while loop statement
|
||||
let tok = self.peek(-1)
|
||||
self.beginScope()
|
||||
|
@ -740,7 +745,7 @@ proc whileStmt(self: Parser): ASTNode =
|
|||
self.endScope()
|
||||
|
||||
|
||||
proc forStmt(self: Parser): ASTNode =
|
||||
proc forStmt(self: Parser): Statement =
|
||||
## Parses a C-style for loop
|
||||
self.beginScope()
|
||||
let tok = self.peek(-1)
|
||||
|
@ -748,8 +753,8 @@ proc forStmt(self: Parser): ASTNode =
|
|||
self.currentLoop = Loop
|
||||
self.expect(LeftParen, "expecting '(' after 'for'")
|
||||
var initializer: ASTNode = nil
|
||||
var condition: ASTNode = nil
|
||||
var increment: ASTNode = nil
|
||||
var condition: Expression = nil
|
||||
var increment: Expression = nil
|
||||
if self.match(Semicolon):
|
||||
discard
|
||||
elif self.match(Var):
|
||||
|
@ -768,7 +773,7 @@ proc forStmt(self: Parser): ASTNode =
|
|||
if increment != nil:
|
||||
# The increment runs after each iteration, so we
|
||||
# inject it into the block as the last statement
|
||||
body = newBlockStmt(@[body, newExprStmt(increment, increment.token)], tok)
|
||||
body = newBlockStmt(@[Declaration(body), Declaration(Expression(newExprStmt(increment, increment.token)))], tok)
|
||||
if condition == nil:
|
||||
## An empty condition is functionally
|
||||
## equivalent to "true"
|
||||
|
@ -778,7 +783,7 @@ proc forStmt(self: Parser): ASTNode =
|
|||
if initializer != nil:
|
||||
# Nested blocks, so the initializer is
|
||||
# only executed once
|
||||
body = newBlockStmt(@[initializer, body], tok)
|
||||
body = newBlockStmt(@[Declaration(initializer), Declaration(body)], tok)
|
||||
# This desgugars the following code:
|
||||
# for (var i = 0; i < 10; i += 1) {
|
||||
# print(i);
|
||||
|
@ -797,14 +802,14 @@ proc forStmt(self: Parser): ASTNode =
|
|||
self.endScope()
|
||||
|
||||
|
||||
proc ifStmt(self: Parser): ASTNode =
|
||||
proc ifStmt(self: Parser): Statement =
|
||||
## Parses if statements
|
||||
let tok = self.peek(-1)
|
||||
self.expect(LeftParen, "expecting '(' before if condition")
|
||||
var condition = self.expression()
|
||||
self.expect(RightParen, "expecting ')' after if condition")
|
||||
var thenBranch = self.statement()
|
||||
var elseBranch: ASTNode = nil
|
||||
var elseBranch: Statement = nil
|
||||
if self.match(Else):
|
||||
elseBranch = self.statement()
|
||||
result = newIfStmt(condition, thenBranch, elseBranch, tok)
|
||||
|
@ -819,15 +824,15 @@ template checkDecl(self: Parser, isPrivate: bool) =
|
|||
self.error("cannot bind public names inside local scopes")
|
||||
|
||||
|
||||
proc varDecl(self: Parser, isLet: bool = false, isConst: bool = false): ASTNode =
|
||||
proc varDecl(self: Parser, isLet: bool = false, isConst: bool = false): Declaration =
|
||||
## Parses variable declarations
|
||||
var tok = self.peek(-1)
|
||||
var value: ASTNode
|
||||
var value: Expression
|
||||
self.expect(Identifier, &"expecting identifier after '{tok.lexeme}'")
|
||||
var name = newIdentExpr(self.peek(-1))
|
||||
let isPrivate = not self.match(Star)
|
||||
self.checkDecl(isPrivate)
|
||||
var valueType: ASTNode
|
||||
var valueType: IdentExpr
|
||||
if self.match(Colon):
|
||||
# We don't enforce it here because
|
||||
# the compiler may be able to infer
|
||||
|
@ -854,13 +859,13 @@ proc varDecl(self: Parser, isLet: bool = false, isConst: bool = false): ASTNode
|
|||
discard # Unreachable
|
||||
|
||||
|
||||
proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isLambda: bool = false, isOperator: bool = false): ASTNode =
|
||||
proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isLambda: bool = false, isOperator: bool = false): Declaration =
|
||||
## Parses functions, coroutines, generators, anonymous functions and custom operators
|
||||
let tok = self.peek(-1)
|
||||
var enclosingFunction = self.currentFunction
|
||||
var arguments: seq[tuple[name: ASTNode, valueType: ASTNode]] = @[]
|
||||
var defaults: seq[ASTNode] = @[]
|
||||
var returnType: ASTNode
|
||||
var arguments: seq[tuple[name: IdentExpr, valueType: IdentExpr]] = @[]
|
||||
var defaults: seq[Expression] = @[]
|
||||
var returnType: IdentExpr
|
||||
if not isLambda and self.check(Identifier):
|
||||
# We do this extra check because we might
|
||||
# be called from a contexst where it's
|
||||
|
@ -885,11 +890,11 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isL
|
|||
# call us back with isLambda=true, allowing us
|
||||
# to actually parse the function as an expression
|
||||
dec(self.current)
|
||||
result = self.expressionStatement()
|
||||
result = Declaration(self.expressionStatement())
|
||||
self.currentFunction = enclosingFunction
|
||||
return result
|
||||
elif isLambda:
|
||||
self.currentFunction = newLambdaExpr(arguments, defaults, newBlockStmt(@[], Token()), isGenerator=isGenerator, isAsync=isAsync, token=tok)
|
||||
self.currentFunction = Declaration(Expression(newLambdaExpr(arguments, defaults, Declaration(Statement(newBlockStmt(@[], Token()))), isGenerator=isGenerator, isAsync=isAsync, token=tok)))
|
||||
elif not isOperator:
|
||||
self.error("funDecl: invalid state")
|
||||
if self.match(Colon):
|
||||
|
@ -900,7 +905,7 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isL
|
|||
returnType = newIdentExpr(self.peek(-1))
|
||||
if not self.match(LeftBrace):
|
||||
# Argument-less function
|
||||
var parameter: tuple[name: ASTNode, valueType: ASTNode]
|
||||
var parameter: tuple[name: IdentExpr, valueType: IdentExpr]
|
||||
self.expect(LeftParen)
|
||||
while not self.check(RightParen):
|
||||
if arguments.len > 255:
|
||||
|
@ -938,9 +943,9 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isL
|
|||
FunDecl(self.currentFunction).arguments = arguments
|
||||
FunDecl(self.currentFunction).returnType = returnType
|
||||
else:
|
||||
LambdaExpr(self.currentFunction).body = self.blockStmt()
|
||||
LambdaExpr(self.currentFunction).arguments = arguments
|
||||
LambdaExpr(self.currentFunction).returnType = returnType
|
||||
LambdaExpr(Expression(self.currentFunction)).body = self.blockStmt()
|
||||
LambdaExpr(Expression(self.currentFunction)).arguments = arguments
|
||||
LambdaExpr(Expression(self.currentFunction)).returnType = returnType
|
||||
result = self.currentFunction
|
||||
if isOperator:
|
||||
# isOperator is only true for functions
|
||||
|
@ -955,20 +960,20 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isL
|
|||
self.currentFunction = enclosingFunction
|
||||
|
||||
|
||||
proc expression(self: Parser): ASTNode =
|
||||
proc expression(self: Parser): Expression =
|
||||
## Parses expressions
|
||||
result = self.assignment()
|
||||
|
||||
|
||||
proc expressionStatement(self: Parser): ASTNode =
|
||||
proc expressionStatement(self: Parser): Statement =
|
||||
## Parses expression statements, which
|
||||
## are expressions followed by a semicolon
|
||||
var expression = self.expression()
|
||||
endOfLine("missing semicolon after expression")
|
||||
result = newExprStmt(expression, expression.token)
|
||||
result = Statement(newExprStmt(expression, expression.token))
|
||||
|
||||
|
||||
proc statement(self: Parser): ASTNode =
|
||||
proc statement(self: Parser): Statement =
|
||||
## Parses statements
|
||||
case self.peek().kind:
|
||||
of If:
|
||||
|
@ -1025,7 +1030,7 @@ proc statement(self: Parser): ASTNode =
|
|||
result = self.expressionStatement()
|
||||
|
||||
|
||||
proc declaration(self: Parser): ASTNode =
|
||||
proc declaration(self: Parser): Declaration =
|
||||
## Parses declarations
|
||||
case self.peek().kind:
|
||||
of Var, Const, Let:
|
||||
|
@ -1046,7 +1051,7 @@ proc declaration(self: Parser): ASTNode =
|
|||
of Type, Comment, TokenType.Whitespace, TokenType.Tab:
|
||||
discard self.step() # TODO
|
||||
else:
|
||||
result = self.statement()
|
||||
result = Declaration(self.statement())
|
||||
|
||||
|
||||
proc parse*(self: Parser, tokens: seq[Token], file: string): seq[ASTNode] =
|
||||
|
@ -1066,13 +1071,10 @@ proc parse*(self: Parser, tokens: seq[Token], file: string): seq[ASTNode] =
|
|||
if token.kind == Operator:
|
||||
if i == self.tokens.high():
|
||||
self.error("invalid state: found malformed tokenizer input while looking for operators (missing EOF)")
|
||||
elif self.tokens[i + 1].kind != Identifier:
|
||||
# The input is probably malformed,
|
||||
# we'll catch this error later
|
||||
break
|
||||
else:
|
||||
self.operators.add(self.tokens[i + 1].lexeme)
|
||||
if i == self.tokens.high() and token.kind != EndOfFile:
|
||||
# Since we're iterating this list anyway might as
|
||||
# well perform some extra checks
|
||||
self.error("invalid state: found malformed tokenizer input while looking for operators (missing EOF)")
|
||||
while not self.done():
|
||||
result.add(self.declaration())
|
|
@ -16,7 +16,8 @@ import jale/multiline
|
|||
import frontend/lexer as l
|
||||
import frontend/parser as p
|
||||
import frontend/compiler as c
|
||||
import frontend/optimizer as o
|
||||
# TODO: Broken
|
||||
# import frontend/optimizer as o
|
||||
import util/serializer as s
|
||||
import util/debugger
|
||||
|
||||
|
@ -41,7 +42,7 @@ when isMainModule:
|
|||
tokens: seq[Token] = @[]
|
||||
tree: seq[ASTNode] = @[]
|
||||
compiled: Chunk
|
||||
optimized: tuple[tree: seq[ASTNode], warnings: seq[Warning]]
|
||||
# optimized: tuple[tree: seq[ASTNode], warnings: seq[Warning]]
|
||||
serialized: Serialized
|
||||
serializedRaw: seq[byte]
|
||||
tokenizer = newLexer()
|
||||
|
|
Loading…
Reference in New Issue