Fixed typos, added more docs. Renamed Local in compiler.nim to a more generic Name. Removed 'raise' from main.nim
This commit is contained in:
parent
44b52949e4
commit
8bac5fe657
|
@ -30,19 +30,29 @@ export bytecode
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
||||||
Local = ref object
|
Name = ref object
|
||||||
|
## A wrapper around declared names.
|
||||||
|
## Depth indicates to which scope
|
||||||
|
## the variable belongs, zero meaning
|
||||||
|
## the global one. Note that all names
|
||||||
|
## are resolved statically unless the
|
||||||
|
## dynamic specifier is used, hence if
|
||||||
|
## the compiler cannot resolve a name
|
||||||
|
## at compile-time it will error out even
|
||||||
|
## if everything would be fine at runtime
|
||||||
name: ASTNode
|
name: ASTNode
|
||||||
isStatic: bool
|
isStatic: bool
|
||||||
isPrivate: bool
|
isPrivate: bool
|
||||||
depth: int
|
depth: int
|
||||||
|
|
||||||
Compiler* = ref object
|
Compiler* = ref object
|
||||||
|
## A wrapper around the compiler's state
|
||||||
chunk: Chunk
|
chunk: Chunk
|
||||||
ast: seq[ASTNode]
|
ast: seq[ASTNode]
|
||||||
enclosing: Compiler
|
enclosing: Compiler
|
||||||
current: int
|
current: int
|
||||||
file: string
|
file: string
|
||||||
locals: seq[Local]
|
names: seq[Name]
|
||||||
scopeDepth: int
|
scopeDepth: int
|
||||||
currentFunction: FunDecl
|
currentFunction: FunDecl
|
||||||
|
|
||||||
|
@ -53,11 +63,13 @@ proc initCompiler*(): Compiler =
|
||||||
result.ast = @[]
|
result.ast = @[]
|
||||||
result.current = 0
|
result.current = 0
|
||||||
result.file = ""
|
result.file = ""
|
||||||
result.locals = @[]
|
result.names = @[]
|
||||||
result.scopeDepth = 0
|
result.scopeDepth = 0
|
||||||
result.currentFunction = nil
|
result.currentFunction = nil
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Forward declarations
|
||||||
proc expression(self: Compiler, node: ASTNode)
|
proc expression(self: Compiler, node: ASTNode)
|
||||||
|
|
||||||
|
|
||||||
|
@ -66,8 +78,8 @@ proc peek(self: Compiler, distance: int = 0): ASTNode =
|
||||||
## Peeks at the AST node at the given distance.
|
## Peeks at the AST node at the given distance.
|
||||||
## If the distance is out of bounds, the last
|
## If the distance is out of bounds, the last
|
||||||
## AST node in the tree is returned. A negative
|
## AST node in the tree is returned. A negative
|
||||||
## distance may be used to retrieve previously consumed
|
## distance may be used to retrieve previously
|
||||||
## AST nodes
|
## consumed AST nodes
|
||||||
if self.ast.high() == -1 or self.current + distance > self.ast.high() or self.current + distance < 0:
|
if self.ast.high() == -1 or self.current + distance > self.ast.high() or self.current + distance < 0:
|
||||||
result = self.ast[^1]
|
result = self.ast[^1]
|
||||||
else:
|
else:
|
||||||
|
@ -81,12 +93,16 @@ proc done(self: Compiler): bool =
|
||||||
|
|
||||||
|
|
||||||
proc check(self: Compiler, kind: NodeKind): bool =
|
proc check(self: Compiler, kind: NodeKind): bool =
|
||||||
|
## Returns if the current node is of the
|
||||||
|
## expected kind
|
||||||
if self.done():
|
if self.done():
|
||||||
return false
|
return false
|
||||||
return self.peek().kind == kind
|
return self.peek().kind == kind
|
||||||
|
|
||||||
|
|
||||||
proc check(self: Compiler, kinds: openarray[NodeKind]): bool =
|
proc check(self: Compiler, kinds: openarray[NodeKind]): bool =
|
||||||
|
## Returns if the current node's kind matches any
|
||||||
|
## of the given ones. Bails out at the first match
|
||||||
for kind in kinds:
|
for kind in kinds:
|
||||||
if self.check(kind):
|
if self.check(kind):
|
||||||
return true
|
return true
|
||||||
|
@ -94,14 +110,16 @@ proc check(self: Compiler, kinds: openarray[NodeKind]): bool =
|
||||||
|
|
||||||
|
|
||||||
proc step(self: Compiler): ASTNode =
|
proc step(self: Compiler): ASTNode =
|
||||||
## Steps n nodes into the input,
|
## Steps to the next node and returns
|
||||||
## returning the last consumed one
|
## the consumed one
|
||||||
result = self.peek()
|
result = self.peek()
|
||||||
if not self.done():
|
if not self.done():
|
||||||
self.current += 1
|
self.current += 1
|
||||||
|
|
||||||
|
|
||||||
proc match(self: Compiler, kind: NodeKind): bool =
|
proc match(self: Compiler, kind: NodeKind): bool =
|
||||||
|
## Same as self.check(), but it calls self.step()
|
||||||
|
## internally if self.check() returns true
|
||||||
if self.check(kind):
|
if self.check(kind):
|
||||||
discard self.step()
|
discard self.step()
|
||||||
return true
|
return true
|
||||||
|
@ -109,6 +127,8 @@ proc match(self: Compiler, kind: NodeKind): bool =
|
||||||
|
|
||||||
|
|
||||||
proc match(self: Compiler, kinds: openarray[NodeKind]): bool =
|
proc match(self: Compiler, kinds: openarray[NodeKind]): bool =
|
||||||
|
## Same as match, but can match more than one node
|
||||||
|
## kind at a time
|
||||||
for kind in kinds:
|
for kind in kinds:
|
||||||
if self.match(kind):
|
if self.match(kind):
|
||||||
return true
|
return true
|
||||||
|
@ -117,13 +137,13 @@ proc match(self: Compiler, kinds: openarray[NodeKind]): bool =
|
||||||
|
|
||||||
proc error(self: Compiler, message: string) =
|
proc error(self: Compiler, message: string) =
|
||||||
## Raises a formatted CompileError exception
|
## Raises a formatted CompileError exception
|
||||||
let tok = if not self.done(): self.peek().token else: self.peek(-1).token
|
let tok = self.peek().token
|
||||||
raise newException(CompileError, &"A fatal error occurred while compiling '{self.file}', line {tok.line} at '{tok.lexeme}' -> {message}")
|
raise newException(CompileError, &"A fatal error occurred while compiling '{self.file}', line {tok.line} at '{tok.lexeme}' -> {message}")
|
||||||
|
|
||||||
|
|
||||||
proc emitByte(self: Compiler, byt: OpCode|uint8) =
|
proc emitByte(self: Compiler, byt: OpCode|uint8) =
|
||||||
## Emits a single bytecode instruction and writes it
|
## Emits a single byte, writing it to
|
||||||
## to the current chunk being compiled
|
## the current chunk being compiled
|
||||||
when DEBUG_TRACE_COMPILER:
|
when DEBUG_TRACE_COMPILER:
|
||||||
echo &"DEBUG - Compiler: Emitting {$byt}"
|
echo &"DEBUG - Compiler: Emitting {$byt}"
|
||||||
self.chunk.write(uint8 byt, self.peek().token.line)
|
self.chunk.write(uint8 byt, self.peek().token.line)
|
||||||
|
@ -139,7 +159,7 @@ proc emitBytes(self: Compiler, byt1: OpCode|uint8, byt2: OpCode|uint8) =
|
||||||
|
|
||||||
proc emitBytes(self: Compiler, bytarr: array[3, uint8]) =
|
proc emitBytes(self: Compiler, bytarr: array[3, uint8]) =
|
||||||
## Handy helper method to write an array of 3 bytes into
|
## Handy helper method to write an array of 3 bytes into
|
||||||
## the current chunk, calling emiteByte(s) on each of its
|
## the current chunk, calling emitByte on each of its
|
||||||
## elements
|
## elements
|
||||||
self.emitBytes(bytarr[0], bytarr[1])
|
self.emitBytes(bytarr[0], bytarr[1])
|
||||||
self.emitByte(bytarr[2])
|
self.emitByte(bytarr[2])
|
||||||
|
@ -253,9 +273,9 @@ proc literal(self: Compiler, node: ASTNode) =
|
||||||
|
|
||||||
|
|
||||||
proc unary(self: Compiler, node: UnaryExpr) =
|
proc unary(self: Compiler, node: UnaryExpr) =
|
||||||
## Parses unary expressions such as negation or
|
## Compiles unary expressions such as negation or
|
||||||
## bitwise inversion
|
## bitwise inversion
|
||||||
self.expression(node.a)
|
self.expression(node.a) # Pushes the operand onto the stack
|
||||||
case node.operator.kind:
|
case node.operator.kind:
|
||||||
of Minus:
|
of Minus:
|
||||||
self.emitByte(UnaryNegate)
|
self.emitByte(UnaryNegate)
|
||||||
|
@ -270,6 +290,10 @@ proc unary(self: Compiler, node: UnaryExpr) =
|
||||||
|
|
||||||
|
|
||||||
proc binary(self: Compiler, node: BinaryExpr) =
|
proc binary(self: Compiler, node: BinaryExpr) =
|
||||||
|
## Compiles all binary expressions
|
||||||
|
|
||||||
|
# These two lines prepare the stack by pushing the
|
||||||
|
# opcode's operands onto it
|
||||||
self.expression(node.a)
|
self.expression(node.a)
|
||||||
self.expression(node.b)
|
self.expression(node.b)
|
||||||
case node.operator.kind:
|
case node.operator.kind:
|
||||||
|
@ -305,12 +329,13 @@ proc binary(self: Compiler, node: BinaryExpr) =
|
||||||
self.emitByte(BinaryShiftRight)
|
self.emitByte(BinaryShiftRight)
|
||||||
of LeftShift:
|
of LeftShift:
|
||||||
self.emitByte(BinaryShiftLeft)
|
self.emitByte(BinaryShiftLeft)
|
||||||
# TODO: In-place operations
|
# TODO: In-place operations (requires variables)
|
||||||
else:
|
else:
|
||||||
self.error(&"invalid AST node of kind {node.kind} at binary(): {node} (This is an internal error and most likely a bug)")
|
self.error(&"invalid AST node of kind {node.kind} at binary(): {node} (This is an internal error and most likely a bug)")
|
||||||
|
|
||||||
|
|
||||||
proc expression(self: Compiler, node: ASTNode) =
|
proc expression(self: Compiler, node: ASTNode) =
|
||||||
|
## Compiles all expressions
|
||||||
case node.kind:
|
case node.kind:
|
||||||
of unaryExpr:
|
of unaryExpr:
|
||||||
self.unary(UnaryExpr(node))
|
self.unary(UnaryExpr(node))
|
||||||
|
@ -327,6 +352,7 @@ proc expression(self: Compiler, node: ASTNode) =
|
||||||
|
|
||||||
|
|
||||||
proc statement(self: Compiler, node: ASTNode) =
|
proc statement(self: Compiler, node: ASTNode) =
|
||||||
|
## Compiles all statements
|
||||||
case self.peek().kind:
|
case self.peek().kind:
|
||||||
of exprStmt:
|
of exprStmt:
|
||||||
self.expression(ExprStmt(node).expression)
|
self.expression(ExprStmt(node).expression)
|
||||||
|
@ -336,6 +362,7 @@ proc statement(self: Compiler, node: ASTNode) =
|
||||||
|
|
||||||
|
|
||||||
proc declaration(self: Compiler, node: ASTNode) =
|
proc declaration(self: Compiler, node: ASTNode) =
|
||||||
|
## Compiles all declarations
|
||||||
case node.kind:
|
case node.kind:
|
||||||
of classDecl, funDecl:
|
of classDecl, funDecl:
|
||||||
discard # TODO
|
discard # TODO
|
||||||
|
@ -344,10 +371,12 @@ proc declaration(self: Compiler, node: ASTNode) =
|
||||||
|
|
||||||
|
|
||||||
proc compile*(self: Compiler, ast: seq[ASTNode], file: string): Chunk =
|
proc compile*(self: Compiler, ast: seq[ASTNode], file: string): Chunk =
|
||||||
|
## Compiles a sequence of AST nodes into a chunk
|
||||||
|
## object
|
||||||
self.chunk = newChunk()
|
self.chunk = newChunk()
|
||||||
self.ast = ast
|
self.ast = ast
|
||||||
self.file = file
|
self.file = file
|
||||||
self.locals = @[]
|
self.names = @[]
|
||||||
self.scopeDepth = 0
|
self.scopeDepth = 0
|
||||||
self.currentFunction = nil
|
self.currentFunction = nil
|
||||||
self.current = 0
|
self.current = 0
|
||||||
|
@ -355,5 +384,5 @@ proc compile*(self: Compiler, ast: seq[ASTNode], file: string): Chunk =
|
||||||
self.declaration(self.step())
|
self.declaration(self.step())
|
||||||
if self.ast.len() > 0:
|
if self.ast.len() > 0:
|
||||||
# *Technically* an empty program is a valid program
|
# *Technically* an empty program is a valid program
|
||||||
self.emitByte(OpCode.Return) # Exits the VM's main loop
|
self.emitByte(OpCode.Return) # Exits the VM's main loop when used at the global scope
|
||||||
result = self.chunk
|
result = self.chunk
|
||||||
|
|
|
@ -113,7 +113,6 @@ proc main() =
|
||||||
compileDate = fromUnix(serialized.compileDate).format("d/M/yyyy H:mm:ss")
|
compileDate = fromUnix(serialized.compileDate).format("d/M/yyyy H:mm:ss")
|
||||||
echo &"\t\t- Compilation date & time: {compileDate}"
|
echo &"\t\t- Compilation date & time: {compileDate}"
|
||||||
except:
|
except:
|
||||||
raise
|
|
||||||
echo &"A Nim runtime exception occurred: {getCurrentExceptionMsg()}"
|
echo &"A Nim runtime exception occurred: {getCurrentExceptionMsg()}"
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue