mirror of https://github.com/japl-lang/japl.git
Fixed a precedence bug with the is operator and added getBoolean to OpCode.Not in the VM
This commit is contained in:
parent
3eeffabe7d
commit
1e353ef1cc
|
@ -63,15 +63,16 @@ type
|
|||
|
||||
Precedence {.pure.} = enum
|
||||
None,
|
||||
Assignment,
|
||||
Assign,
|
||||
Or,
|
||||
And,
|
||||
Equality,
|
||||
Comparison,
|
||||
Is,
|
||||
Eq,
|
||||
Comp,
|
||||
Term,
|
||||
Factor,
|
||||
Unary,
|
||||
Exponentiation,
|
||||
Exp,
|
||||
Call,
|
||||
Primary
|
||||
|
||||
|
@ -253,7 +254,7 @@ proc parsePrecedence(self: Compiler, precedence: Precedence) =
|
|||
if prefixRule == nil: # If there is no prefix rule than an expression is expected
|
||||
self.parser.parseError(self.parser.previous, "Expecting expression")
|
||||
return
|
||||
var canAssign = precedence <= Precedence.Assignment # This is used to detect invalid assignment targets
|
||||
var canAssign = precedence <= Precedence.Assign # This is used to detect invalid assignment targets
|
||||
# such as "hello" = 3;
|
||||
self.prefixRule(canAssign) # otherwise call the prefix rule (e.g. for binary negation)
|
||||
if self.parser.previous.kind == EOF:
|
||||
|
@ -267,13 +268,13 @@ proc parsePrecedence(self: Compiler, precedence: Precedence) =
|
|||
self.infixRule(canAssign)
|
||||
else:
|
||||
self.parser.parseError(self.parser.previous, "Expecting expression, got EOF")
|
||||
if canAssign and self.parser.match(EQ):
|
||||
if canAssign and self.parser.match(TokenType.EQ):
|
||||
self.parser.parseError(self.parser.peek, "Invalid assignment target")
|
||||
|
||||
|
||||
proc expression(self: Compiler) =
|
||||
## Parses expressions
|
||||
self.parsePrecedence(Precedence.Assignment) # The highest-level expression is assignment
|
||||
self.parsePrecedence(Precedence.Assign) # The highest-level expression is assignment
|
||||
|
||||
|
||||
proc binary(self: Compiler, canAssign: bool) =
|
||||
|
@ -338,6 +339,8 @@ proc unary(self: Compiler, canAssign: bool) =
|
|||
self.emitByte(OpCode.Not)
|
||||
of TILDE:
|
||||
self.emitByte(OpCode.Bnot)
|
||||
of PLUS:
|
||||
discard # Unary + does nothing anyway
|
||||
else:
|
||||
return
|
||||
|
||||
|
@ -569,7 +572,7 @@ proc namedVariable(self: Compiler, tok: Token, canAssign: bool) =
|
|||
get = OpCode.GetGlobal
|
||||
set = OpCode.SetGlobal
|
||||
arg = int self.identifierConstant(tok)
|
||||
if self.parser.match(EQ) and canAssign:
|
||||
if self.parser.match(TokenType.EQ) and canAssign:
|
||||
self.expression()
|
||||
self.emitBytes(set, uint8 arg)
|
||||
else:
|
||||
|
@ -593,7 +596,7 @@ proc namedLongVariable(self: Compiler, tok: Token, canAssign: bool) =
|
|||
get = OpCode.GetGlobal
|
||||
set = OpCode.SetGlobal
|
||||
casted = self.identifierLongConstant(tok)
|
||||
if self.parser.match(EQ) and canAssign:
|
||||
if self.parser.match(TokenType.EQ) and canAssign:
|
||||
self.expression()
|
||||
self.emitByte(set)
|
||||
self.emitBytes(casted)
|
||||
|
@ -624,7 +627,7 @@ proc varDeclaration(self: Compiler) =
|
|||
else:
|
||||
useShort = false
|
||||
longName = self.parseLongVariable("Expecting variable name")
|
||||
if self.parser.match(EQ):
|
||||
if self.parser.match(TokenType.EQ):
|
||||
self.expression()
|
||||
else:
|
||||
self.emitByte(OpCode.Nil)
|
||||
|
@ -938,7 +941,7 @@ proc parseFunction(self: Compiler, funType: FunctionType) =
|
|||
return
|
||||
paramNames.add(self.parser.previous.lexeme)
|
||||
self.defineVariable(paramIdx)
|
||||
if self.parser.match(EQ):
|
||||
if self.parser.match(TokenType.EQ):
|
||||
if self.parser.peek.kind == EOF:
|
||||
self.compileError("Unexpected EOF")
|
||||
return
|
||||
|
@ -1086,22 +1089,26 @@ proc freeCompiler*(self: Compiler) =
|
|||
echo &"DEBUG - Compiler: Freed {objFreed} objects out of {objCount} compile-time objects"
|
||||
|
||||
|
||||
# The array of all parse rules
|
||||
# The array of all parse rules.
|
||||
# This array instructs our Pratt parser on how
|
||||
# to parse expressions and statements.
|
||||
# makeRule defines rules for unary and binary
|
||||
# operators as well as the token's precedence
|
||||
var rules: array[TokenType, ParseRule] = [
|
||||
makeRule(nil, binary, Precedence.Term), # PLUS
|
||||
makeRule(unary, binary, Precedence.Term), # MINUS
|
||||
makeRule(nil, binary, Precedence.Factor), # SLASH
|
||||
makeRule(nil, binary, Precedence.Factor), # STAR
|
||||
makeRule(unary, nil, Precedence.None), # NEG
|
||||
makeRule(nil, binary, Precedence.Equality), # NE
|
||||
makeRule(nil, binary, Precedence.Eq), # NE
|
||||
makeRule(nil, nil, Precedence.None), # EQ
|
||||
makeRule(nil, binary, Precedence.Comparison), # DEQ
|
||||
makeRule(nil, binary, Precedence.Comparison), # LT
|
||||
makeRule(nil, binary, Precedence.Comparison), # GE
|
||||
makeRule(nil, binary, Precedence.Comparison), # LE
|
||||
makeRule(nil, binary, Precedence.Comp), # DEQ
|
||||
makeRule(nil, binary, Precedence.Comp), # LT
|
||||
makeRule(nil, binary, Precedence.Comp), # GE
|
||||
makeRule(nil, binary, Precedence.Comp), # LE
|
||||
makeRule(nil, binary, Precedence.Factor), # MOD
|
||||
makeRule(nil, binary, Precedence.Exponentiation), # POW
|
||||
makeRule(nil, binary, Precedence.Comparison), # GT
|
||||
makeRule(nil, binary, Precedence.Exp), # POW
|
||||
makeRule(nil, binary, Precedence.Comp), # GT
|
||||
makeRule(grouping, call, Precedence.Call), # LP
|
||||
makeRule(nil, nil, Precedence.None), # RP
|
||||
makeRule(nil, bracket, Precedence.Call), # LS
|
||||
|
@ -1113,7 +1120,7 @@ var rules: array[TokenType, ParseRule] = [
|
|||
makeRule(nil, nil, Precedence.None), # RS
|
||||
makeRule(number, nil, Precedence.None), # NUMBER
|
||||
makeRule(strVal, nil, Precedence.None), # STR
|
||||
makeRule(nil, nil, Precedence.None), # SEMI
|
||||
makeRule(nil, nil, Precedence.None), # SEMICOLON
|
||||
makeRule(nil, parseAnd, Precedence.And), # AND
|
||||
makeRule(nil, nil, Precedence.None), # CLASS
|
||||
makeRule(nil, nil, Precedence.None), # ELSE
|
||||
|
@ -1142,7 +1149,7 @@ var rules: array[TokenType, ParseRule] = [
|
|||
makeRule(nil, binary, Precedence.Term), # BAND
|
||||
makeRule(nil, binary, Precedence.Term), # BOR
|
||||
makeRule(unary, nil, Precedence.None), # TILDE
|
||||
makeRule(nil, binary, Precedence.Term) # IS
|
||||
makeRule(nil, binary, Precedence.Is) # IS
|
||||
]
|
||||
|
||||
|
||||
|
@ -1183,11 +1190,25 @@ proc compile*(self: Compiler, source: string): ptr Function =
|
|||
|
||||
|
||||
proc initParser*(tokens: seq[Token], file: string): Parser =
|
||||
result = Parser(current: 0, tokens: tokens, hadError: false, panicMode: false, file: file)
|
||||
## Initializes a new Parser obvject and returns a reference
|
||||
## to it
|
||||
# TODO -> Make the parser independent of the compiler. As
|
||||
# of now, the compiler is what drives the parser and while
|
||||
# that might be easier for us it is not an ideal design.
|
||||
# We'll have to devise a standard interface for other people
|
||||
# to try and hook their parsers into JAPL with ease (pretty
|
||||
# much like our lexer now has the sole requirement of
|
||||
# having a lex() procedure that returns a list of tokens)
|
||||
result = new(Parser)
|
||||
result.current = 0
|
||||
result.tokens = tokens
|
||||
result.hadError = false
|
||||
result.panicMode = false
|
||||
result.file = file
|
||||
|
||||
|
||||
proc initCompiler*(context: FunctionType, enclosing: Compiler = nil, parser: Parser = initParser(@[], ""), file: string): Compiler =
|
||||
## Initializes a new compiler object and returns a reference
|
||||
## Initializes a new Compiler object and returns a reference
|
||||
## to it
|
||||
result = new(Compiler)
|
||||
result.parser = parser
|
||||
|
|
|
@ -87,5 +87,4 @@ proc disassembleChunk*(chunk: Chunk, name: string) =
|
|||
var index = 0
|
||||
while index < chunk.code.len:
|
||||
index = disassembleInstruction(chunk, index)
|
||||
echo &"==== Debug session ended - Chunk '{name}' ===="
|
||||
|
||||
echo &"==== Debug session ended - Chunk '{name}' ===="
|
78
src/vm.nim
78
src/vm.nim
|
@ -37,8 +37,11 @@ import types/boolean
|
|||
import types/methods
|
||||
import types/function
|
||||
import types/native
|
||||
when DEBUG_TRACE_VM:
|
||||
import util/debug
|
||||
# We always import it to
|
||||
# avoid the compiler complaining
|
||||
# about functions not existing
|
||||
# in production builds
|
||||
import util/debug
|
||||
|
||||
|
||||
type
|
||||
|
@ -323,6 +326,41 @@ proc readLongConstant(self: CallFrame): ptr Obj =
|
|||
copyMem(idx.addr, unsafeAddr(arr), sizeof(arr))
|
||||
result = self.function.chunk.consts[idx]
|
||||
|
||||
proc showRuntime*(self: VM, frame: CallFrame, iteration: uint64) =
|
||||
## Shows debug information about the current
|
||||
## state of the virtual machine
|
||||
stdout.write(&"Iteration N. {iteration}\nCurrent VM stack status: [")
|
||||
for i, v in self.stack:
|
||||
stdout.write(stringify(v))
|
||||
if i < self.stack.high():
|
||||
stdout.write(", ")
|
||||
stdout.write("]\nCurrent global scope status: {")
|
||||
for i, (k, v) in enumerate(self.globals.pairs()):
|
||||
stdout.write(&"'{k}': {stringify(v)}")
|
||||
if i < self.globals.len() - 1:
|
||||
stdout.write(", ")
|
||||
stdout.write("}\nCurrent frame type: ")
|
||||
if frame.function.name == nil:
|
||||
stdout.write("main\n")
|
||||
else:
|
||||
stdout.write(&"function, '{frame.function.name.stringify()}'\n")
|
||||
echo &"Current frame count: {self.frameCount}"
|
||||
echo &"Current frame length: {frame.len}"
|
||||
stdout.write("Current frame constants table: ")
|
||||
stdout.write("[")
|
||||
for i, e in frame.function.chunk.consts:
|
||||
stdout.write(stringify(e))
|
||||
if i < frame.function.chunk.consts.high():
|
||||
stdout.write(", ")
|
||||
stdout.write("]\nCurrent frame stack status: ")
|
||||
stdout.write("[")
|
||||
for i, e in frame.getView():
|
||||
stdout.write(stringify(e))
|
||||
if i < len(frame) - 1:
|
||||
stdout.write(", ")
|
||||
stdout.write("]\n")
|
||||
discard disassembleInstruction(frame.function.chunk, frame.ip - 1)
|
||||
|
||||
|
||||
proc run(self: VM, repl: bool): InterpretResult =
|
||||
## Chews trough bytecode instructions executing
|
||||
|
@ -331,43 +369,13 @@ proc run(self: VM, repl: bool): InterpretResult =
|
|||
var frame = self.frames[self.frameCount - 1]
|
||||
var opcode: OpCode
|
||||
when DEBUG_TRACE_VM:
|
||||
var iteration: int = 0
|
||||
var iteration: uint64 = 0
|
||||
while true:
|
||||
{.computedgoto.} # See https://nim-lang.org/docs/manual.html#pragmas-computedgoto-pragma
|
||||
opcode = OpCode(frame.readByte())
|
||||
when DEBUG_TRACE_VM: # Insight inside the VM
|
||||
iteration += 1
|
||||
stdout.write(&"Iteration N. {iteration}\nCurrent VM stack status: [")
|
||||
for i, v in self.stack:
|
||||
stdout.write(stringify(v))
|
||||
if i < self.stack.high():
|
||||
stdout.write(", ")
|
||||
stdout.write("]\nCurrent global scope status: {")
|
||||
for i, (k, v) in enumerate(self.globals.pairs()):
|
||||
stdout.write(&"'{k}': {stringify(v)}")
|
||||
if i < self.globals.len() - 1:
|
||||
stdout.write(", ")
|
||||
stdout.write("}\nCurrent frame type: ")
|
||||
if frame.function.name == nil:
|
||||
stdout.write("main\n")
|
||||
else:
|
||||
stdout.write(&"function, '{frame.function.name.stringify()}'\n")
|
||||
echo &"Current frame count: {self.frameCount}"
|
||||
echo &"Current frame length: {frame.len}"
|
||||
stdout.write("Current frame constants table: ")
|
||||
stdout.write("[")
|
||||
for i, e in frame.function.chunk.consts:
|
||||
stdout.write(stringify(e))
|
||||
if i < frame.function.chunk.consts.high():
|
||||
stdout.write(", ")
|
||||
stdout.write("]\nCurrent frame stack status: ")
|
||||
stdout.write("[")
|
||||
for i, e in frame.getView():
|
||||
stdout.write(stringify(e))
|
||||
if i < len(frame) - 1:
|
||||
stdout.write(", ")
|
||||
stdout.write("]\n")
|
||||
discard disassembleInstruction(frame.function.chunk, frame.ip - 1)
|
||||
self.showRuntime(frame, iteration)
|
||||
case opcode: # Main OpCodes dispatcher
|
||||
of OpCode.Constant:
|
||||
self.push(frame.readConstant())
|
||||
|
@ -489,7 +497,7 @@ proc run(self: VM, repl: bool): InterpretResult =
|
|||
of OpCode.Inf:
|
||||
self.push(cast[ptr Infinity](self.cached[3]))
|
||||
of OpCode.Not:
|
||||
self.push(self.pop().isFalsey().asBool())
|
||||
self.push(self.getBoolean(self.pop().isFalsey()))
|
||||
of OpCode.Equal:
|
||||
# Here order doesn't matter, because if a == b
|
||||
# then b == a (at least in *most* languages, sigh)
|
||||
|
|
Loading…
Reference in New Issue