mirror of https://github.com/japl-lang/japl.git
Base structure for some methods ready
This commit is contained in:
parent
09e543d786
commit
ef08fee976
|
@ -20,7 +20,7 @@ const ARRAY_GROW_FACTOR = 2 # How much extra memory to allocate for dynamic ar
|
|||
const FRAMES_MAX* = 400 # TODO: Inspect why the VM crashes if this exceeds 400
|
||||
const JAPL_VERSION* = "0.2.0"
|
||||
const JAPL_RELEASE* = "alpha"
|
||||
const DEBUG_TRACE_VM* = false # Traces VM execution
|
||||
const DEBUG_TRACE_VM* = false # Traces VM execution
|
||||
const DEBUG_TRACE_GC* = false # Traces the garbage collector (TODO)
|
||||
const DEBUG_TRACE_ALLOCATION* = false # Traces memory allocation/deallocation (WIP)
|
||||
const DEBUG_TRACE_COMPILER* = false # Traces the compiler
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
|
||||
import segfaults
|
||||
import config
|
||||
# import config
|
||||
# when DEBUG_TRACE_ALLOCATION:
|
||||
# import util/debug # TODO: Recursive dependency
|
||||
|
||||
|
|
|
@ -12,14 +12,16 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
## JAPL' type system
|
||||
## The JAPL's type system. In JAPL, all entities are
|
||||
## objects, and are always references to a memory location
|
||||
## somewhere in the heap
|
||||
|
||||
import ../memory
|
||||
import strformat
|
||||
|
||||
|
||||
type
|
||||
NotImplementedError = object of CatchableError
|
||||
NotImplementedError* = object of CatchableError
|
||||
## Raised when a given operation is unsupported
|
||||
## on a given type
|
||||
Chunk* = ref object # TODO: This shouldn't be here, but Function needs it. Consider refactoring
|
||||
|
@ -43,6 +45,7 @@ type
|
|||
## from this base type
|
||||
kind*: ObjectType
|
||||
hashValue*: uint64
|
||||
isHashable: bool # This is false for unhashable objects
|
||||
String* = object of Obj
|
||||
## A string object
|
||||
str*: ptr UncheckedArray[char] # TODO -> Unicode support
|
||||
|
@ -95,7 +98,8 @@ proc newChunk*(): Chunk =
|
|||
result = Chunk(consts: @[], code: @[], lines: @[])
|
||||
|
||||
|
||||
## Utilities that bridge nim and JAPL types
|
||||
## Utilities that bridge nim and JAPL types or to inspect
|
||||
## JAPL objects
|
||||
|
||||
|
||||
proc objType*(obj: ptr Obj): ObjectType =
|
||||
|
@ -170,7 +174,7 @@ proc toFloat*(obj: ptr Obj): float =
|
|||
## Converts a JAPL float to a nim float
|
||||
result = cast[ptr Float](obj).floatValue
|
||||
|
||||
# TODO ambiguous naming: conflict with toString(obj: obj) that does JAPL->JAPL
|
||||
|
||||
proc toStr*(obj: ptr Obj): string =
|
||||
## Converts a JAPL string into a nim string
|
||||
var strObj = cast[ptr String](obj)
|
||||
|
@ -182,26 +186,28 @@ proc asInt*(n: int): ptr Integer =
|
|||
## Converts a nim int into a JAPL int
|
||||
result = allocateObj(Integer, ObjectType.Integer)
|
||||
result.intValue = n
|
||||
result.isHashable = true
|
||||
|
||||
|
||||
proc asFloat*(n: float): ptr Float =
|
||||
## Converts a nim float into a JAPL float
|
||||
result = allocateObj(Float, ObjectType.Float)
|
||||
result.floatValue = n
|
||||
result.isHashable = true
|
||||
|
||||
|
||||
proc asBool*(b: bool): ptr Bool =
|
||||
## Converts a nim bool into a JAPL bool
|
||||
result = allocateObj(Bool, ObjectType.Bool)
|
||||
result.boolValue = b
|
||||
result.isHashable = true
|
||||
|
||||
|
||||
proc asNil*(): ptr Nil =
|
||||
proc asNil*(): ptr Nil =
|
||||
## Creates a nil object
|
||||
result = allocateObj(Nil, ObjectType.Nil)
|
||||
|
||||
|
||||
proc asNan*(): ptr NotANumber =
|
||||
proc asNan*(): ptr NotANumber =
|
||||
## Creates a nan object
|
||||
result = allocateObj(NotANumber, ObjectType.NotANumber)
|
||||
|
||||
|
@ -216,29 +222,124 @@ proc newObj*(): ptr Obj =
|
|||
result = allocateObj(Obj, ObjectType.BaseObject)
|
||||
|
||||
|
||||
## JAPL procs implementations below
|
||||
proc newString*(str: string): ptr String
|
||||
proc asStr*(s: string): ptr String # Forward declarations
|
||||
|
||||
|
||||
|
||||
# Functions constructors and procedures
|
||||
|
||||
type
|
||||
FunctionType* {.pure.} = enum
|
||||
## All code in JAPL is compiled
|
||||
## as if it was inside some sort
|
||||
## of function. To differentiate
|
||||
## between actual functions and
|
||||
## the top-level code, this tiny
|
||||
## enum is used to tell the two
|
||||
## contexts apart when compiling
|
||||
Func, Script
|
||||
|
||||
|
||||
proc newFunction*(name: string = "", chunk: Chunk = newChunk(), arity: int = 0): ptr Function =
|
||||
## Allocates a new function object with the given
|
||||
## bytecode chunk and arity. If the name is an empty string
|
||||
## (the default), the function will be an
|
||||
## anonymous code object
|
||||
# TODO: Add lambdas
|
||||
# TODO: Add support for optional parameters
|
||||
result = allocateObj(Function, ObjectType.Function)
|
||||
if name.len > 1:
|
||||
result.name = newString(name)
|
||||
else:
|
||||
result.name = nil
|
||||
result.arity = arity
|
||||
result.chunk = chunk
|
||||
result.isHashable = false
|
||||
|
||||
|
||||
proc newIndexError*(message: string): ptr JAPLException =
|
||||
result = allocateObj(JAPLException, ObjectType.Exception)
|
||||
result.errName = "IndexError".asStr()
|
||||
result.message = message.asStr()
|
||||
|
||||
|
||||
proc newReferenceError*(message: string): ptr JAPLException =
|
||||
result = allocateObj(JAPLException, ObjectType.Exception)
|
||||
result.errName = "ReferenceError".asStr()
|
||||
result.message = message.asStr()
|
||||
|
||||
|
||||
proc newInterruptedError*(message: string): ptr JAPLException =
|
||||
result = allocateObj(JAPLException, ObjectType.Exception)
|
||||
result.errName = "InterruptedError".asStr()
|
||||
result.message = message.asStr()
|
||||
|
||||
|
||||
proc newRecursionError*(message: string): ptr JAPLException =
|
||||
result = allocateObj(JAPLException, ObjectType.Exception)
|
||||
result.errName = "RecursionError".asStr()
|
||||
result.message = message.asStr()
|
||||
|
||||
|
||||
|
||||
proc newTypeError*(message: string): ptr JAPLException =
|
||||
result = allocateObj(JAPLException, ObjectType.Exception)
|
||||
result.errName = "TypeError".asStr()
|
||||
result.message = message.asStr()
|
||||
|
||||
|
||||
## Exposed object methods used in the JAPL runtime
|
||||
## are defined and implemented below
|
||||
|
||||
|
||||
# Implementations for typeName
|
||||
|
||||
proc typeName*(self: ptr Obj): string =
|
||||
## Returns the name of the object type
|
||||
result = "object"
|
||||
|
||||
proc typeName*(self: ptr Function): string =
|
||||
proc typeName(self: ptr Function): string =
|
||||
result = "function"
|
||||
|
||||
proc typeName*(self: ptr String): string =
|
||||
|
||||
proc typeName(self: ptr String): string =
|
||||
return "string"
|
||||
|
||||
proc typeName*(self: ptr Integer): string =
|
||||
|
||||
proc typeName(self: ptr Integer): string =
|
||||
result = "integer"
|
||||
|
||||
proc typeName*(self: ptr Float): string =
|
||||
|
||||
proc typeName(self: ptr Float): string =
|
||||
result = "float"
|
||||
|
||||
proc typeName*(self: ptr Bool): string =
|
||||
|
||||
proc typeName(self: ptr Bool): string =
|
||||
result = "bool"
|
||||
|
||||
|
||||
proc typeName*(self: ptr Obj): string =
|
||||
## Returns the name of the object's type
|
||||
case self.kind:
|
||||
of ObjectType.BaseObject:
|
||||
result = "object"
|
||||
of ObjectType.String:
|
||||
result = cast[ptr String](self).typeName()
|
||||
of ObjectType.Integer:
|
||||
result = cast[ptr Integer](self).typeName()
|
||||
of ObjectType.Float:
|
||||
result = cast[ptr Float](self).typeName()
|
||||
of ObjectType.Bool:
|
||||
result = cast[ptr Bool](self).typeName()
|
||||
of ObjectType.Function:
|
||||
result = cast[ptr Function](self).typeName()
|
||||
of ObjectType.Infinity:
|
||||
result = cast[ptr Infinity](self).typeName()
|
||||
of ObjectType.NotANumber:
|
||||
result = cast[ptr NotANumber](self).typeName()
|
||||
of ObjectType.Nil:
|
||||
result = cast[ptr Nil](self).typeName()
|
||||
else:
|
||||
discard # TODO
|
||||
|
||||
|
||||
# Implementations for stringify
|
||||
|
||||
proc stringify*(self: ptr Integer): string =
|
||||
|
@ -269,6 +370,7 @@ proc stringify(self: ptr Infinity): string =
|
|||
else:
|
||||
result = "inf"
|
||||
|
||||
|
||||
proc stringify(self: ptr Nil): string =
|
||||
result = "nil"
|
||||
|
||||
|
@ -283,7 +385,8 @@ proc stringify*(self: ptr Function): string =
|
|||
else:
|
||||
result = "<code object>"
|
||||
|
||||
proc stringify*(self: ptr Obj): string =
|
||||
|
||||
proc stringify*(self: ptr Obj): string =
|
||||
## Returns a string representation of the
|
||||
## given object
|
||||
case self.kind:
|
||||
|
@ -328,11 +431,11 @@ proc isFalsey(self: ptr Float): bool =
|
|||
result = self.floatValue == 0.0
|
||||
|
||||
|
||||
proc isFalsey(self: ptr Bool): bool =
|
||||
proc isFalsey(self: ptr Bool): bool =
|
||||
result = not self.boolValue
|
||||
|
||||
|
||||
proc isFalsey(self: ptr Infinity): bool =
|
||||
proc isFalsey(self: ptr Infinity): bool =
|
||||
result = false
|
||||
|
||||
|
||||
|
@ -402,7 +505,7 @@ proc hash(self: ptr Nil): uint64 =
|
|||
# TODO: Arbitrary hash seems a bad idea
|
||||
result = 2u
|
||||
|
||||
proc hash(self: ptr Function): uint64 =
|
||||
proc hash(self: ptr Function): uint64 =
|
||||
# TODO: Hashable?
|
||||
raise newException(NotImplementedError, "unhashable type 'function'")
|
||||
|
||||
|
@ -411,6 +514,8 @@ proc hash*(self: ptr Obj): uint64 =
|
|||
## Returns the hash of the object using
|
||||
## the FNV-1a algorithm (or a predefined value).
|
||||
## Raises an error if the object is not hashable
|
||||
if not self.isHashable:
|
||||
raise newException(NotImplementedError, &"unhashable type '{self.typeName}'")
|
||||
case self.kind:
|
||||
of ObjectType.BaseObject:
|
||||
result = 2166136261u # Constant hash
|
||||
|
@ -508,143 +613,140 @@ proc eq*(self, other: ptr Obj): bool =
|
|||
discard # TODO
|
||||
|
||||
|
||||
## String constructors and converters
|
||||
proc sum(self: ptr String, other: ptr Obj): ptr String =
|
||||
if other.kind == ObjectType.String:
|
||||
var other = cast[ptr String](other)
|
||||
var selfStr = self.toStr()
|
||||
var otherStr = other.toStr()
|
||||
result = (selfStr & otherStr).asStr()
|
||||
else:
|
||||
raise newException(NotImplementedError, &"unsupported binary operator '+' for objects of type '{self.typeName()}' and '{other.typeName()}'")
|
||||
|
||||
|
||||
proc sum(self: ptr Integer, other: ptr Obj): ptr Obj = # This can yield a float!
|
||||
case other.kind:
|
||||
of ObjectType.Integer:
|
||||
result = (self.toInt() + cast[ptr Integer](other).toInt()).asInt()
|
||||
of ObjectType.Float:
|
||||
let res = ((float self.toInt()) + cast[ptr Float](other).toFloat())
|
||||
if res == system.Inf:
|
||||
result = asInf()
|
||||
elif res == -system.Inf:
|
||||
let negInf = asInf()
|
||||
negInf.isNegative = true
|
||||
result = negInf
|
||||
else:
|
||||
result = res.asFloat()
|
||||
of ObjectType.NotANumber:
|
||||
result = asNan()
|
||||
of ObjectType.Infinity:
|
||||
result = cast[ptr Infinity](other)
|
||||
else:
|
||||
raise newException(NotImplementedError, &"unsupported binary operator '+' for objects of type '{self.typeName()}' and '{other.typeName()}'")
|
||||
|
||||
|
||||
proc sum(self: ptr Float, other: ptr Obj): ptr Obj =
|
||||
case other.kind:
|
||||
of ObjectType.Integer:
|
||||
result = (self.toFloat() + float cast[ptr Integer](other).toInt()).asFloat()
|
||||
of ObjectType.Float:
|
||||
let res = (self.toFloat() + cast[ptr Float](other).toFloat())
|
||||
if res == system.Inf:
|
||||
result = asInf()
|
||||
elif res == -system.Inf:
|
||||
let negInf = asInf()
|
||||
negInf.isNegative = true
|
||||
result = negInf
|
||||
else:
|
||||
result = res.asFloat()
|
||||
of ObjectType.NotANumber:
|
||||
result = asNan()
|
||||
of ObjectType.Infinity:
|
||||
result = cast[ptr Infinity](other)
|
||||
else:
|
||||
raise newException(NotImplementedError, &"unsupported binary operator '+' for objects of type '{self.typeName()}' and '{other.typeName()}'")
|
||||
|
||||
|
||||
proc sum*(self, other: ptr Obj): ptr Obj =
|
||||
## Returns the result of self + other
|
||||
## or raises NotImplementedError if the operation is unsupported
|
||||
case self.kind:
|
||||
of ObjectType.String: # Here we don't cast other (yet)
|
||||
# because binary operators can mix types together,
|
||||
# like in "hello" * 5, or 3.5 * 8. Casting that
|
||||
# later allows for finer error reporting and keeps
|
||||
# these methods as generic as possible
|
||||
result = cast[ptr String](self).sum(other)
|
||||
of ObjectType.Integer:
|
||||
result = cast[ptr Integer](self).sum(other)
|
||||
of ObjectType.Float:
|
||||
result = cast[ptr Float](self).sum(other)
|
||||
else:
|
||||
raise newException(NotImplementedError, &"unsupported binary operator '+' for objects of type '{self.typeName()}' and '{other.typeName()}'")
|
||||
|
||||
|
||||
proc sub(self, other: ptr Obj): ptr Obj =
|
||||
## Returns the result of self - other
|
||||
## or raises NotImplementedError if the operation is unsupported
|
||||
result = nil
|
||||
|
||||
|
||||
proc mul(self, other: ptr Obj): ptr Obj =
|
||||
## Returns the result of self * other
|
||||
## or raises NotImplementedError if the operation is unsupported
|
||||
result = nil
|
||||
|
||||
|
||||
proc trueDiv(self, other: ptr Obj): ptr Obj =
|
||||
## Returns the result of self / other
|
||||
## or raises NotImplementedError if the operation is unsupported
|
||||
result = nil
|
||||
|
||||
|
||||
proc exp(self, other: ptr Obj): ptr Obj =
|
||||
## Returns the result of self ** other
|
||||
## or raises NotImplementedError if the operation is unsupported
|
||||
result = nil
|
||||
|
||||
|
||||
proc binaryAnd(self, other: ptr Obj): ptr Obj =
|
||||
## Returns the result of self & other
|
||||
## or raises NotImplementedError if the operation is unsupported
|
||||
result = nil
|
||||
|
||||
|
||||
proc binaryOr(self, other: ptr Obj): ptr Obj =
|
||||
## Returns the result of self | other
|
||||
## or raises NotImplementedError if the operation is unsupported
|
||||
result = nil
|
||||
|
||||
|
||||
proc binaryNot(self: ptr Obj): ptr Obj =
|
||||
## Returns the result of ~self
|
||||
## or raises NotImplementedError if the operation is unsupported
|
||||
result = nil
|
||||
|
||||
|
||||
proc binaryXor(self, other: ptr Obj): ptr Obj =
|
||||
## Returns the result of self ^ other
|
||||
## or raises NotImplementedError if the operation is unsupported
|
||||
result = nil
|
||||
|
||||
|
||||
proc newString*(str: string): ptr String =
|
||||
# TODO -> Unicode
|
||||
# TODO -> Move this into asStr
|
||||
result = allocateObj(String, ObjectType.String)
|
||||
result.str = allocate(UncheckedArray[char], char, len(str))
|
||||
for i in 0..len(str) - 1:
|
||||
result.str[i] = str[i]
|
||||
result.len = len(str)
|
||||
result.hashValue = result.hash()
|
||||
result.isHashable = true
|
||||
|
||||
proc asStr*(s: string): ptr Obj =
|
||||
## Converts a nim string into a
|
||||
|
||||
proc asStr*(s: string): ptr String =
|
||||
|
||||
## Converts a nim string into a
|
||||
## JAPL string
|
||||
result = newString(s)
|
||||
|
||||
# End of string object procs
|
||||
|
||||
|
||||
# Functions constructors and procedures
|
||||
|
||||
type
|
||||
FunctionType* {.pure.} = enum
|
||||
## All code in JAPL is compiled
|
||||
## as if it was inside some sort
|
||||
## of function. To differentiate
|
||||
## between actual functions and
|
||||
## the top-level code, this tiny
|
||||
## enum is used to tell the two
|
||||
## contexts apart when compiling
|
||||
Func, Script
|
||||
|
||||
|
||||
proc newFunction*(name: string = "", chunk: Chunk = newChunk(), arity: int = 0): ptr Function =
|
||||
## Allocates a new function object with the given
|
||||
## bytecode chunk and arity. If the name is an empty string
|
||||
## (the default), the function will be an
|
||||
## anonymous code object
|
||||
# TODO: Add lambdas
|
||||
# TODO: Add support for optional parameters
|
||||
result = allocateObj(Function, ObjectType.Function)
|
||||
if name.len > 1:
|
||||
result.name = newString(name)
|
||||
else:
|
||||
result.name = nil
|
||||
result.arity = arity
|
||||
result.chunk = chunk
|
||||
|
||||
|
||||
proc bool*(obj: ptr Obj): bool =
|
||||
## Returns wheter the object should
|
||||
## be considered a falsey obj
|
||||
## or not. Returns true if the
|
||||
## object is truthy, or false
|
||||
## if it is falsey
|
||||
result = false
|
||||
|
||||
|
||||
proc add(self, other: ptr Obj): ptr Obj =
|
||||
## Returns the result of self + other
|
||||
## or nil if the operation is unsupported
|
||||
result = nil # Not defined for base objects!
|
||||
|
||||
|
||||
proc sub(self, other: ptr Obj): ptr Obj =
|
||||
## Returns the result of self - other
|
||||
## or nil if the operation is unsupported
|
||||
result = nil
|
||||
|
||||
|
||||
proc mul(self, other: ptr Obj): ptr Obj =
|
||||
## Returns the result of self * other
|
||||
## or nil if the operation is unsupported
|
||||
result = nil
|
||||
|
||||
|
||||
proc trueDiv(self, other: ptr Obj): ptr Obj =
|
||||
## Returns the result of self / other
|
||||
## or nil if the operation is unsupported
|
||||
result = nil
|
||||
|
||||
|
||||
proc exp(self, other: ptr Obj): ptr Obj =
|
||||
## Returns the result of self ** other
|
||||
## or nil if the operation is unsupported
|
||||
result = nil
|
||||
|
||||
|
||||
proc binaryAnd(self, other: ptr Obj): ptr Obj =
|
||||
## Returns the result of self & other
|
||||
## or nil if the operation is unsupported
|
||||
result = nil
|
||||
|
||||
|
||||
proc binaryOr(self, other: ptr Obj): ptr Obj =
|
||||
## Returns the result of self | other
|
||||
## or nil if the operation is unsupported
|
||||
result = nil
|
||||
|
||||
|
||||
proc binaryNot(self: ptr Obj): ptr Obj =
|
||||
## Returns the result of ~self
|
||||
## or nil if the operation is unsupported
|
||||
result = nil
|
||||
|
||||
|
||||
proc binaryXor(self, other: ptr Obj): ptr Obj =
|
||||
## Returns the result of self ^ other
|
||||
## or nil if the operation is unsupported
|
||||
result = nil
|
||||
|
||||
proc newIndexError*(message: string): ptr JAPLException =
|
||||
result = allocateObj(JAPLException, ObjectType.Exception)
|
||||
result.errName = newString("IndexError")
|
||||
result.message = newString(message)
|
||||
|
||||
|
||||
proc newReferenceError*(message: string): ptr JAPLException =
|
||||
result = allocateObj(JAPLException, ObjectType.Exception)
|
||||
result.errName = newString("ReferenceError")
|
||||
result.message = newString(message)
|
||||
|
||||
|
||||
proc newInterruptedError*(message: string): ptr JAPLException =
|
||||
result = allocateObj(JAPLException, ObjectType.Exception)
|
||||
result.errName = newString("InterruptedError")
|
||||
result.message = newString(message)
|
||||
|
||||
|
||||
proc newRecursionError*(message: string): ptr JAPLException =
|
||||
result = allocateObj(JAPLException, ObjectType.Exception)
|
||||
result.errName = newString("RecursionError")
|
||||
result.message = newString(message)
|
||||
|
||||
|
||||
|
||||
proc newTypeError*(message: string): ptr JAPLException =
|
||||
result = allocateObj(JAPLException, ObjectType.Exception)
|
||||
result.errName = newString("TypeError")
|
||||
result.message = newString(message)
|
||||
|
|
52
src/vm.nim
52
src/vm.nim
|
@ -225,7 +225,7 @@ proc callObject(self: var VM, callee: ptr Obj, argCount: uint8): bool =
|
|||
else: # TODO: Classes
|
||||
discard # Unreachable
|
||||
else:
|
||||
self.error(newTypeError(&"object of type '{callee.typeName}' is not callable"))
|
||||
self.error(newTypeError(&"object of type '{callee.typeName()}' is not callable"))
|
||||
return false
|
||||
|
||||
|
||||
|
@ -289,22 +289,27 @@ proc run(self: var VM, repl: bool): InterpretResult =
|
|||
stdout.write(&"Current frame count: {self.frameCount}\n")
|
||||
stdout.write("Current frame stack status: ")
|
||||
stdout.write("[")
|
||||
for e in self.stack[frame.slot..self.stackTop - 1]:
|
||||
for e in frame.getView():
|
||||
stdout.write(stringify(e))
|
||||
stdout.write(", ")
|
||||
stdout.write("]\n")
|
||||
discard disassembleInstruction(frame.function.chunk, frame.ip - 1)
|
||||
case opcode: # Main OpCodes dispatcher
|
||||
of OpCode.Constant:
|
||||
var constant: ptr Obj = readConstant()
|
||||
self.push(constant)
|
||||
self.push(readConstant())
|
||||
of OpCode.ConstantLong:
|
||||
var constant: ptr Obj = readLongConstant()
|
||||
self.push(constant)
|
||||
self.push(readLongConstant())
|
||||
of OpCode.Negate: # TODO: Call appropriate methods
|
||||
discard
|
||||
# self.push(self.pop().negate())
|
||||
of OpCode.Add:
|
||||
discard
|
||||
var left = self.pop()
|
||||
var right = self.pop()
|
||||
try:
|
||||
self.push(right.sum(left))
|
||||
except NotImplementedError:
|
||||
self.error(newTypeError(getCurrentExceptionMsg()))
|
||||
return RuntimeError
|
||||
of OpCode.Shl:
|
||||
discard
|
||||
of OpCode.Shr:
|
||||
|
@ -340,7 +345,11 @@ proc run(self: var VM, repl: bool): InterpretResult =
|
|||
of OpCode.Not:
|
||||
self.push(self.pop().isFalsey().asBool())
|
||||
of OpCode.Equal:
|
||||
# Here order doesn't matter, because if a == b
|
||||
# then b == a (at least in *most* languages, sigh)
|
||||
self.push(self.pop().eq(self.pop()).asBool())
|
||||
# Doesn't this chain of calls look beautifully
|
||||
# intuitive?
|
||||
of OpCode.Less:
|
||||
discard
|
||||
of OpCode.Greater:
|
||||
|
@ -353,11 +362,9 @@ proc run(self: var VM, repl: bool): InterpretResult =
|
|||
return RuntimeError
|
||||
of OpCode.DefineGlobal:
|
||||
if frame.function.chunk.consts.len > 255:
|
||||
var constant = readLongConstant().toStr()
|
||||
self.globals[constant] = self.peek(0)
|
||||
self.globals[readLongConstant().toStr()] = self.peek(0)
|
||||
else:
|
||||
var constant = readConstant().toStr()
|
||||
self.globals[constant] = self.peek(0)
|
||||
self.globals[readConstant().toStr()] = self.peek(0)
|
||||
discard self.pop() # This will help when we have a custom GC
|
||||
of OpCode.GetGlobal:
|
||||
if frame.function.chunk.consts.len > 255:
|
||||
|
@ -407,18 +414,14 @@ proc run(self: var VM, repl: bool): InterpretResult =
|
|||
self.globals.del(constant)
|
||||
of OpCode.GetLocal:
|
||||
if frame.len > 255:
|
||||
var slot = readBytes()
|
||||
self.push(frame[slot])
|
||||
self.push(frame[readBytes()])
|
||||
else:
|
||||
var slot = readByte()
|
||||
self.push(frame[int slot])
|
||||
self.push(frame[int readByte()])
|
||||
of OpCode.SetLocal:
|
||||
if frame.len > 255:
|
||||
var slot = readBytes()
|
||||
frame[slot] = self.peek(0)
|
||||
frame[readBytes()] = self.peek(0)
|
||||
else:
|
||||
var slot = readByte()
|
||||
frame[int slot] = self.peek(0)
|
||||
frame[int readByte()] = self.peek(0)
|
||||
of OpCode.DeleteLocal:
|
||||
# Unused due to GC potential issues
|
||||
if frame.len > 255:
|
||||
|
@ -430,15 +433,12 @@ proc run(self: var VM, repl: bool): InterpretResult =
|
|||
of OpCode.Pop:
|
||||
self.lastPop = self.pop()
|
||||
of OpCode.JumpIfFalse:
|
||||
var offset = readShort()
|
||||
if isFalsey(self.peek(0)):
|
||||
frame.ip += int offset
|
||||
frame.ip += int readShort()
|
||||
of OpCode.Jump:
|
||||
var offset = readShort()
|
||||
frame.ip += int offset
|
||||
frame.ip += int readShort()
|
||||
of OpCode.Loop:
|
||||
var offset = readShort()
|
||||
frame.ip -= int offset
|
||||
frame.ip -= int readShort()
|
||||
of OpCode.Call:
|
||||
var argCount = readByte()
|
||||
if not self.callObject(self.peek(int argCount), argCount):
|
||||
|
@ -450,7 +450,7 @@ proc run(self: var VM, repl: bool): InterpretResult =
|
|||
var retResult = self.pop()
|
||||
if repl:
|
||||
if not self.lastPop.isNil() and self.frameCount == 1: # This is to avoid
|
||||
# useless output with recursive calls
|
||||
# This avoids unwanted output with recursive calls
|
||||
echo stringify(self.lastPop)
|
||||
self.lastPop = asNil()
|
||||
self.frameCount -= 1
|
||||
|
|
Loading…
Reference in New Issue