mirror of https://github.com/japl-lang/japl.git
Removed short version of constants for benchmarking purposes and refactored the type system's behavior to return more consistent values
This commit is contained in:
parent
882efd28da
commit
f8ac8d08cb
127
src/compiler.nim
127
src/compiler.nim
|
@ -214,27 +214,18 @@ proc emitBytes(self: Compiler, bytarr: array[3, uint8]) =
|
|||
self.emitByte(bytarr[2])
|
||||
|
||||
|
||||
proc makeConstant(self: Compiler, obj: ptr Obj): uint8 =
|
||||
## Adds a constant (literal) to the current chunk's
|
||||
## constants table
|
||||
result = uint8 self.currentChunk.addConstant(obj)
|
||||
|
||||
|
||||
proc makeLongConstant(self: Compiler, val: ptr Obj): array[3, uint8] =
|
||||
proc makeConstant(self: Compiler, val: ptr Obj): array[3, uint8] =
|
||||
## Does the same as makeConstant(), but encodes the index in the
|
||||
## chunk's constant table as an array (which is later reconstructed
|
||||
## into an integer at runtime) to store more than 256 constants in the table
|
||||
result = self.currentChunk.writeConstant(val)
|
||||
result = self.currentChunk.addConstant(val)
|
||||
|
||||
|
||||
proc emitConstant(self: Compiler, obj: ptr Obj) =
|
||||
## Emits a Constant or ConstantLong instruction along
|
||||
## Emits a Constant instruction along
|
||||
## with its operand
|
||||
if self.currentChunk().consts.len > 255:
|
||||
self.emitByte(OpCode.ConstantLong)
|
||||
self.emitBytes(self.makeLongConstant(obj))
|
||||
else:
|
||||
self.emitBytes(OpCode.Constant, self.makeConstant(obj))
|
||||
self.emitByte(OpCode.Constant)
|
||||
self.emitBytes(self.makeConstant(obj))
|
||||
|
||||
|
||||
proc initParser*(tokens: seq[Token], file: string): Parser
|
||||
|
@ -252,7 +243,7 @@ proc parsePrecedence(self: Compiler, precedence: Precedence) =
|
|||
else:
|
||||
discard self.parser.advance()
|
||||
var prefixRule = getRule(self.parser.previous.kind).prefix
|
||||
if prefixRule == nil: # If there is no prefix rule than an expression is expected
|
||||
if prefixRule == nil: # If there is no prefix rule then an expression is expected
|
||||
self.parser.parseError(self.parser.previous, "Expecting expression")
|
||||
return
|
||||
var canAssign = precedence <= Precedence.Assign # This is used to detect invalid assignment targets
|
||||
|
@ -463,17 +454,11 @@ proc grouping(self: Compiler, canAssign: bool) =
|
|||
self.parser.consume(TokenType.RP, "Expecting ')' after parentheszed expression")
|
||||
|
||||
|
||||
proc identifierConstant(self: Compiler, tok: Token): uint8 =
|
||||
proc identifierConstant(self: Compiler, tok: Token): array[3, uint8] =
|
||||
## Emits instructions for identifiers
|
||||
return self.makeConstant(self.markObject(asStr(tok.lexeme)))
|
||||
|
||||
|
||||
proc identifierLongConstant(self: Compiler, tok: Token): array[3, uint8] =
|
||||
## Same as identifierConstant, but this is used when the constant table is longer
|
||||
## than 255 elements
|
||||
return self.makeLongConstant(self.markObject(asStr(tok.lexeme)))
|
||||
|
||||
|
||||
proc addLocal(self: Compiler, name: Token) =
|
||||
## Stores a local variable. Local name resolution
|
||||
## happens at compile time rather than runtime,
|
||||
|
@ -499,24 +484,13 @@ proc declareVariable(self: Compiler) =
|
|||
self.addLocal(name)
|
||||
|
||||
|
||||
proc parseVariable(self: Compiler, message: string): uint8 =
|
||||
proc parseVariable(self: Compiler, message: string): array[3, uint8] =
|
||||
## Parses variables and declares them
|
||||
self.parser.consume(TokenType.ID, message)
|
||||
self.declareVariable()
|
||||
if self.scopeDepth > 0:
|
||||
return uint8 0
|
||||
return self.identifierConstant(self.parser.previous())
|
||||
|
||||
|
||||
proc parseLongVariable(self: Compiler, message: string): array[3, uint8] =
|
||||
## Parses variables and declares them. This is used in place
|
||||
## of parseVariable when there's more than 255 constants
|
||||
## in the chunk table
|
||||
self.parser.consume(TokenType.ID, message)
|
||||
self.declareVariable()
|
||||
if self.scopeDepth > 0:
|
||||
return [uint8 0, uint8 0, uint8 0]
|
||||
return self.identifierLongConstant(self.parser.previous())
|
||||
return self.identifierConstant(self.parser.previous())
|
||||
|
||||
|
||||
proc markInitialized(self: Compiler) =
|
||||
|
@ -527,17 +501,6 @@ proc markInitialized(self: Compiler) =
|
|||
self.locals[self.localCount - 1].depth = self.scopeDepth
|
||||
|
||||
|
||||
proc defineVariable(self: Compiler, idx: uint8) =
|
||||
## Defines a variable, emitting appropriate
|
||||
## instructions if we're in the local scope
|
||||
## or marking the last local as initialized
|
||||
## otherwise
|
||||
if self.scopeDepth > 0:
|
||||
self.markInitialized()
|
||||
return
|
||||
self.emitBytes(OpCode.DefineGlobal, idx)
|
||||
|
||||
|
||||
proc defineVariable(self: Compiler, idx: array[3, uint8]) =
|
||||
## Same as defineVariable, but this is used when
|
||||
## there's more than 255 locals in the chunk's table
|
||||
|
@ -563,30 +526,7 @@ proc resolveLocal(self: Compiler, name: Token): int =
|
|||
|
||||
proc namedVariable(self: Compiler, tok: Token, canAssign: bool) =
|
||||
## Handles local and global variables assignment, as well
|
||||
## as variable resolution.
|
||||
var
|
||||
arg = self.resolveLocal(tok)
|
||||
get: OpCode
|
||||
set: OpCode
|
||||
if arg != -1:
|
||||
get = OpCode.GetLocal
|
||||
set = OpCode.SetLocal
|
||||
else:
|
||||
get = OpCode.GetGlobal
|
||||
set = OpCode.SetGlobal
|
||||
arg = int self.identifierConstant(tok)
|
||||
if self.parser.match(TokenType.EQ) and canAssign:
|
||||
self.expression()
|
||||
self.emitBytes(set, uint8 arg)
|
||||
else:
|
||||
self.emitBytes(get, uint8 arg)
|
||||
|
||||
|
||||
proc namedLongVariable(self: Compiler, tok: Token, canAssign: bool) =
|
||||
## Handles local and global variables assignment, as well
|
||||
## as variable resolution. This is only called when the constants
|
||||
## table's length exceeds 255
|
||||
|
||||
## as variable resolution
|
||||
var
|
||||
arg = self.resolveLocal(tok)
|
||||
casted = cast[array[3, uint8]](arg)
|
||||
|
@ -598,7 +538,7 @@ proc namedLongVariable(self: Compiler, tok: Token, canAssign: bool) =
|
|||
else:
|
||||
get = OpCode.GetGlobal
|
||||
set = OpCode.SetGlobal
|
||||
casted = self.identifierLongConstant(tok)
|
||||
casted = self.identifierConstant(tok)
|
||||
if self.parser.match(TokenType.EQ) and canAssign:
|
||||
self.expression()
|
||||
self.emitByte(set)
|
||||
|
@ -608,37 +548,21 @@ proc namedLongVariable(self: Compiler, tok: Token, canAssign: bool) =
|
|||
self.emitBytes(casted)
|
||||
|
||||
|
||||
|
||||
proc variable(self: Compiler, canAssign: bool) =
|
||||
## Emits the code to declare a variable,
|
||||
## both locally and globally
|
||||
if self.locals.len < 255:
|
||||
self.namedVariable(self.parser.previous(), canAssign)
|
||||
else:
|
||||
self.namedLongVariable(self.parser.previous(), canAssign)
|
||||
self.namedVariable(self.parser.previous(), canAssign)
|
||||
|
||||
|
||||
proc varDeclaration(self: Compiler) =
|
||||
## Parses a variable declaration, taking into account
|
||||
## the possibility that the chunk table could already
|
||||
## be bigger than 255 elements
|
||||
var shortName: uint8
|
||||
var longName: array[3, uint8]
|
||||
var useShort: bool = true
|
||||
if self.currentChunk.consts.len < 255:
|
||||
shortName = self.parseVariable("Expecting variable name")
|
||||
else:
|
||||
useShort = false
|
||||
longName = self.parseLongVariable("Expecting variable name")
|
||||
## Parses a variable declaration
|
||||
var name: array[3, uint8] = self.parseVariable("Expecting variable name")
|
||||
if self.parser.match(TokenType.EQ):
|
||||
self.expression()
|
||||
else:
|
||||
self.emitByte(OpCode.Nil)
|
||||
self.parser.consume(TokenType.SEMICOLON, "Missing semicolon after var declaration")
|
||||
if useShort:
|
||||
self.defineVariable(shortName)
|
||||
else:
|
||||
self.defineVariable(longName)
|
||||
self.defineVariable(name)
|
||||
|
||||
|
||||
proc expressionStatement(self: Compiler) =
|
||||
|
@ -661,13 +585,9 @@ proc delStatement(self: Compiler) =
|
|||
else:
|
||||
code = OpCode.DeleteLocal
|
||||
self.localCount = self.localCount - 1
|
||||
if self.currentChunk.consts.len < 255:
|
||||
var name = self.identifierConstant(self.parser.previous())
|
||||
self.emitBytes(code, name)
|
||||
else:
|
||||
var name = self.identifierLongConstant(self.parser.previous())
|
||||
self.emitBytes(code, name[0])
|
||||
self.emitBytes(name[1], name[2])
|
||||
var name = self.identifierConstant(self.parser.previous())
|
||||
self.emitBytes(code, name[0])
|
||||
self.emitBytes(name[1], name[2])
|
||||
self.parser.consume(TokenType.SEMICOLON, "Missing semicolon after del statement")
|
||||
|
||||
|
||||
|
@ -696,7 +616,6 @@ proc endScope(self: Compiler) =
|
|||
while self.localCount > 0 and self.locals[self.localCount - 1].depth > self.scopeDepth:
|
||||
self.emitByte(OpCode.Pop)
|
||||
self.localCount = self.localCount - 1
|
||||
|
||||
if start >= self.localCount:
|
||||
self.locals.delete(self.localCount, start)
|
||||
|
||||
|
@ -915,7 +834,7 @@ proc endCompiler(self: Compiler): ptr Function =
|
|||
## if no return statement is supplied
|
||||
self.emitByte(OpCode.Nil)
|
||||
self.emitByte(OpCode.Return)
|
||||
return self.function # TODO: self.markObject?
|
||||
return self.function
|
||||
|
||||
|
||||
proc parseFunction(self: Compiler, funType: FunctionType) =
|
||||
|
@ -963,12 +882,8 @@ proc parseFunction(self: Compiler, funType: FunctionType) =
|
|||
self.parseBlock()
|
||||
var fun = self.endCompiler()
|
||||
self = self.enclosing
|
||||
if self.currentChunk.consts.len < 255:
|
||||
self.emitBytes(OpCode.Constant, self.makeConstant(fun))
|
||||
else:
|
||||
self.emitByte(OpCode.ConstantLong)
|
||||
self.emitBytes(self.makeLongConstant(fun))
|
||||
|
||||
self.emitByte(OpCode.Constant)
|
||||
self.emitBytes(self.makeConstant(fun))
|
||||
|
||||
|
||||
proc parseLambda(self: Compiler, canAssign: bool) =
|
||||
|
|
|
@ -22,6 +22,7 @@ import config
|
|||
import vm
|
||||
|
||||
|
||||
|
||||
proc repl() =
|
||||
var bytecodeVM = initVM()
|
||||
echo JAPL_VERSION_STRING
|
||||
|
@ -31,11 +32,11 @@ proc repl() =
|
|||
echo "==== Runtime Constants ====\n"
|
||||
echo &"- FRAMES_MAX -> {FRAMES_MAX}"
|
||||
echo "==== Debugger started ====\n"
|
||||
var source: string = ""
|
||||
var source = ""
|
||||
while true:
|
||||
try:
|
||||
stdout.write("=> ")
|
||||
source = readLine(stdin)
|
||||
source = stdin.readLine()
|
||||
except IOError:
|
||||
echo ""
|
||||
bytecodeVM.freeVM()
|
||||
|
|
|
@ -162,7 +162,7 @@ proc parseIdentifier(self: Lexer) =
|
|||
## Parses identifiers, note that
|
||||
## multi-character tokens such as
|
||||
## UTF runes are not supported
|
||||
while self.peek().isAlphaNumeric():
|
||||
while self.peek().isAlphaNumeric() or self.peek() in {'_', }:
|
||||
discard self.step()
|
||||
var text: string = self.source[self.start..<self.current]
|
||||
if text in RESERVED:
|
||||
|
@ -247,3 +247,19 @@ proc lex*(self: Lexer): seq[Token] =
|
|||
self.tokens.add(Token(kind: TokenType.EOF, lexeme: "EOF", line: self.line))
|
||||
return self.tokens
|
||||
|
||||
|
||||
when isMainModule:
|
||||
echo("JAPL Lexer REPL")
|
||||
while true:
|
||||
try:
|
||||
stdout.write(">> ")
|
||||
var lexer = initLexer(readLine(stdin), "stdin")
|
||||
stdout.write("Lexer output: [")
|
||||
var lexed = lexer.lex()
|
||||
for i, el in lexed:
|
||||
stdout.write($el[])
|
||||
if i < lexed.high():
|
||||
stdout.write(", ")
|
||||
stdout.write("]\n")
|
||||
except IOError:
|
||||
break
|
||||
|
|
|
@ -19,7 +19,7 @@ import ../types/baseObject
|
|||
|
||||
|
||||
type
|
||||
Chunk* = ref object # TODO: This shouldn't be here, but Function needs it. Consider refactoring
|
||||
Chunk* = ref object
|
||||
## A piece of bytecode.
|
||||
## Consts represents the constants table the code is referring to
|
||||
## Code is the compiled bytecode
|
||||
|
@ -30,7 +30,6 @@ type
|
|||
OpCode* {.pure.} = enum
|
||||
## Enum of possible opcodes
|
||||
Constant = 0u8,
|
||||
ConstantLong,
|
||||
Return,
|
||||
Negate,
|
||||
Add,
|
||||
|
@ -84,10 +83,8 @@ const simpleInstructions* = {OpCode.Return, OpCode.Add, OpCode.Multiply,
|
|||
OpCode.Slice, OpCode.Pop, OpCode.Negate,
|
||||
OpCode.Is, OpCode.As}
|
||||
const constantInstructions* = {OpCode.Constant, OpCode.DefineGlobal,
|
||||
OpCode.GetGlobal, OpCode.SetGlobal,
|
||||
OpCode.DeleteGlobal}
|
||||
|
||||
const constantLongInstructions* = {OpCode.ConstantLong}
|
||||
OpCode.GetGlobal, OpCode.SetGlobal,
|
||||
OpCode.DeleteGlobal}
|
||||
const byteInstructions* = {OpCode.SetLocal, OpCode.GetLocal, OpCode.DeleteLocal,
|
||||
OpCode.Call}
|
||||
const jumpInstructions* = {OpCode.JumpIfFalse, OpCode.Jump, OpCode.Loop}
|
||||
|
@ -117,15 +114,10 @@ proc freeChunk*(self: Chunk) =
|
|||
self.lines = @[]
|
||||
|
||||
|
||||
proc addConstant*(self: Chunk, constant: ptr Obj): int =
|
||||
## Adds a constant to a chunk. Returns its index.
|
||||
proc addConstant*(self: Chunk, constant: ptr Obj): array[3, uint8] =
|
||||
## Writes a constant to a chunk. Returns its index casted to an array
|
||||
self.consts.add(constant)
|
||||
return self.consts.high() # The index of the constant
|
||||
|
||||
|
||||
proc writeConstant*(self: Chunk, constant: ptr Obj): array[3, uint8] =
|
||||
## Writes a constant to a chunk. Returns its index casted to an array.
|
||||
## TODO newdoc
|
||||
let index = self.addConstant(constant)
|
||||
let index = self.consts.high()
|
||||
result = cast[array[3, uint8]](index)
|
||||
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
import types/baseObject
|
||||
import types/numbers
|
||||
import types/methods
|
||||
import types/typeutils
|
||||
import types/japlString
|
||||
import types/exception
|
||||
import types/native
|
||||
|
|
|
@ -12,49 +12,52 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Implementation of JAPL exceptions (WIP)
|
||||
|
||||
import baseObject
|
||||
import japlString
|
||||
|
||||
import strformat
|
||||
|
||||
|
||||
type
|
||||
JAPLException* = object of Obj # TODO: Create exceptions subclasses
|
||||
## The base exception object
|
||||
errName*: ptr String # TODO: Ditch error name in favor of inheritance-based types
|
||||
message*: ptr String
|
||||
# TODO -> Use ptr String again once
|
||||
# the recursive dependency is fixed
|
||||
errName*: string # TODO: Ditch error name in favor of inheritance-based builtin types
|
||||
message*: string
|
||||
|
||||
|
||||
|
||||
proc newIndexError*(message: string): ptr JAPLException =
|
||||
result = allocateObj(JAPLException, ObjectType.Exception)
|
||||
result.errName = "IndexError".asStr()
|
||||
result.message = message.asStr()
|
||||
result.errName = "IndexError"
|
||||
result.message = message
|
||||
|
||||
|
||||
proc newReferenceError*(message: string): ptr JAPLException =
|
||||
result = allocateObj(JAPLException, ObjectType.Exception)
|
||||
result.errName = "ReferenceError".asStr()
|
||||
result.message = message.asStr()
|
||||
result.errName = "ReferenceError"
|
||||
result.message = message
|
||||
|
||||
|
||||
proc newInterruptedError*(message: string): ptr JAPLException =
|
||||
result = allocateObj(JAPLException, ObjectType.Exception)
|
||||
result.errName = "InterruptedError".asStr()
|
||||
result.message = message.asStr()
|
||||
result.errName = "InterruptedError"
|
||||
result.message = message
|
||||
|
||||
|
||||
proc newRecursionError*(message: string): ptr JAPLException =
|
||||
result = allocateObj(JAPLException, ObjectType.Exception)
|
||||
result.errName = "RecursionError".asStr()
|
||||
result.message = message.asStr()
|
||||
result.errName = "RecursionError"
|
||||
result.message = message
|
||||
|
||||
|
||||
proc newTypeError*(message: string): ptr JAPLException =
|
||||
result = allocateObj(JAPLException, ObjectType.Exception)
|
||||
result.errName = "TypeError".asStr()
|
||||
result.message = message.asStr()
|
||||
result.errName = "TypeError"
|
||||
result.message = message
|
||||
|
||||
|
||||
proc stringify*(self: ptr JAPLException): string =
|
||||
result = self.errName.toStr() & ": " & self.message.toStr()
|
||||
|
||||
result = &"{self.errName}: {self.message}"
|
|
@ -18,7 +18,11 @@
|
|||
import baseObject
|
||||
import numbers
|
||||
import ../memory
|
||||
import typeutils
|
||||
import exception
|
||||
|
||||
import strutils
|
||||
import strformat
|
||||
|
||||
|
||||
type
|
||||
|
@ -36,13 +40,12 @@ proc toStr*(obj: ptr Obj): string =
|
|||
|
||||
|
||||
proc hash*(self: ptr String): uint64 =
|
||||
## Implements the FNV-1a hashing algorithm
|
||||
## for strings
|
||||
result = 2166136261u
|
||||
var i = 0
|
||||
while i < self.len:
|
||||
result = result xor uint64 self.str[i]
|
||||
for i in countup(0, self.len):
|
||||
result = result xor uint64(self.str[i])
|
||||
result *= 16777619
|
||||
i += 1
|
||||
return result
|
||||
|
||||
|
||||
proc asStr*(s: string): ptr String =
|
||||
|
@ -53,7 +56,10 @@ proc asStr*(s: string): ptr String =
|
|||
for i in 0..len(s) - 1:
|
||||
result.str[i] = s[i]
|
||||
result.len = len(s)
|
||||
result.hashValue = result.hash()
|
||||
if result.len > 0:
|
||||
result.hashValue = result.hash()
|
||||
else:
|
||||
result.hashValue = 0u
|
||||
result.isHashable = true
|
||||
|
||||
|
||||
|
@ -82,19 +88,69 @@ proc eq*(self, other: ptr String): bool =
|
|||
result = true
|
||||
|
||||
|
||||
proc sum*(self: ptr String, other: ptr Obj): ptr String =
|
||||
if other.kind == ObjectType.String:
|
||||
proc sum*(self: ptr String, other: ptr Obj): returnType =
|
||||
result.kind = returnTypes.Object
|
||||
if other.isStr():
|
||||
var other = cast[ptr String](other)
|
||||
var selfStr = self.toStr()
|
||||
var otherStr = other.toStr()
|
||||
result = (selfStr & otherStr).asStr()
|
||||
result.result = (selfStr & otherStr).asStr()
|
||||
else:
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc mul*(self: ptr String, other: ptr Obj): ptr Obj =
|
||||
proc mul*(self: ptr String, other: ptr Obj): returnType =
|
||||
result.kind = returnTypes.Object
|
||||
case other.kind:
|
||||
of ObjectType.Integer:
|
||||
result = self.toStr().repeat(cast[ptr Integer](other).toInt()).asStr()
|
||||
result.result = self.toStr().repeat(cast[ptr Integer](other).toInt()).asStr()
|
||||
else:
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc getItem*(self: ptr String, other: ptr Obj): returnType =
|
||||
result.kind = returnTypes.Object
|
||||
## Handles getItem expressions
|
||||
var str = self.toStr()
|
||||
if not other.isInt():
|
||||
result.kind = returnTypes.Exception
|
||||
result.result = newTypeError("string indeces must be integers")
|
||||
else:
|
||||
var index: int = other.toInt()
|
||||
if index < 0:
|
||||
index = len(str) + other.toInt()
|
||||
if index < 0: # If even now it is less than 0 then it is out of bounds
|
||||
result.kind = returnTypes.Exception
|
||||
result.result = newIndexError("string index out of bounds")
|
||||
elif index - 1 > len(str) - 1:
|
||||
result.kind = returnTypes.Exception
|
||||
result.result = newIndexError("string index out of bounds")
|
||||
else:
|
||||
result.result = asStr(&"{str[index]}")
|
||||
|
||||
|
||||
proc Slice*(self: ptr String, a: ptr Obj, b: ptr Obj): returnType =
|
||||
## Handles slice expressions
|
||||
var startIndex = b.toInt()
|
||||
var endIndex = a.toInt()
|
||||
result.kind = returnTypes.Object
|
||||
if a.isNil():
|
||||
endIndex = self.len
|
||||
if b.isNil():
|
||||
startIndex = 0
|
||||
if not b.isInt() or not a.isInt():
|
||||
result.kind = returnTypes.Exception
|
||||
result.result = newTypeError("string indeces must be integers")
|
||||
elif startIndex < 0:
|
||||
startIndex = (self.len + startIndex)
|
||||
if startIndex < 0:
|
||||
startIndex = (self.len + endIndex)
|
||||
elif startIndex > self.str.high():
|
||||
result.result = asStr("")
|
||||
return result
|
||||
if endIndex > self.str.high():
|
||||
endIndex = self.len
|
||||
if startIndex > endIndex:
|
||||
result.result = asStr("")
|
||||
return result
|
||||
result.result = self.toStr()[startIndex..<endIndex].asStr()
|
||||
|
|
|
@ -23,6 +23,7 @@ import boolean
|
|||
import japlNil
|
||||
import numbers
|
||||
import native
|
||||
import typeutils
|
||||
|
||||
import parseutils
|
||||
|
||||
|
@ -195,7 +196,7 @@ proc eq*(self, other: ptr Obj): bool =
|
|||
discard
|
||||
|
||||
|
||||
proc negate*(self: ptr Obj): ptr Obj =
|
||||
proc negate*(self: ptr Obj): returnType =
|
||||
## Returns the result of -self or
|
||||
## raises an error if the operation
|
||||
## is unsupported
|
||||
|
@ -210,7 +211,7 @@ proc negate*(self: ptr Obj): ptr Obj =
|
|||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc sum*(self, other: ptr Obj): ptr Obj =
|
||||
proc sum*(self, other: ptr Obj): returnType =
|
||||
## Returns the result of self + other
|
||||
## or raises NotImplementedError if the operation is unsupported
|
||||
case self.kind:
|
||||
|
@ -232,7 +233,7 @@ proc sum*(self, other: ptr Obj): ptr Obj =
|
|||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc sub*(self, other: ptr Obj): ptr Obj =
|
||||
proc sub*(self, other: ptr Obj): returnType =
|
||||
## Returns the result of self - other
|
||||
## or raises NotImplementedError if the operation is unsupported
|
||||
case self.kind:
|
||||
|
@ -248,7 +249,7 @@ proc sub*(self, other: ptr Obj): ptr Obj =
|
|||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc mul*(self, other: ptr Obj): ptr Obj =
|
||||
proc mul*(self, other: ptr Obj): returnType =
|
||||
## Returns the result of self * other
|
||||
## or raises NotImplementedError if the operation is unsupported
|
||||
case self.kind:
|
||||
|
@ -266,7 +267,7 @@ proc mul*(self, other: ptr Obj): ptr Obj =
|
|||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc trueDiv*(self, other: ptr Obj): ptr Obj =
|
||||
proc trueDiv*(self, other: ptr Obj): returnType =
|
||||
## Returns the result of self / other
|
||||
## or raises NotImplementedError if the operation is unsupported
|
||||
case self.kind:
|
||||
|
@ -282,7 +283,7 @@ proc trueDiv*(self, other: ptr Obj): ptr Obj =
|
|||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc pow*(self, other: ptr Obj): ptr Obj =
|
||||
proc pow*(self, other: ptr Obj): returnType =
|
||||
## Returns the result of self ** other (exponentiation)
|
||||
## or raises NotImplementedError if the operation is unsupported
|
||||
case self.kind:
|
||||
|
@ -298,7 +299,7 @@ proc pow*(self, other: ptr Obj): ptr Obj =
|
|||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc divMod*(self, other: ptr Obj): ptr Obj =
|
||||
proc divMod*(self, other: ptr Obj): returnType =
|
||||
## Returns the result of self % other
|
||||
## or raises NotImplementedError if the operation is unsupported
|
||||
case self.kind:
|
||||
|
@ -314,7 +315,7 @@ proc divMod*(self, other: ptr Obj): ptr Obj =
|
|||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc binaryAnd*(self, other: ptr Obj): ptr Obj =
|
||||
proc binaryAnd*(self, other: ptr Obj): returnType =
|
||||
## Returns the result of self & other
|
||||
## or raises NotImplementedError if the operation is unsupported
|
||||
case self.kind:
|
||||
|
@ -325,7 +326,7 @@ proc binaryAnd*(self, other: ptr Obj): ptr Obj =
|
|||
|
||||
|
||||
|
||||
proc binaryOr*(self, other: ptr Obj): ptr Obj =
|
||||
proc binaryOr*(self, other: ptr Obj): returnType =
|
||||
## Returns the result of self | other
|
||||
## or raises NotImplementedError if the operation is unsupported
|
||||
case self.kind:
|
||||
|
@ -335,7 +336,7 @@ proc binaryOr*(self, other: ptr Obj): ptr Obj =
|
|||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc binaryXor*(self, other: ptr Obj): ptr Obj =
|
||||
proc binaryXor*(self, other: ptr Obj): returnType =
|
||||
## Returns the result of self ^ other
|
||||
## or raises NotImplementedError if the operation is unsupported
|
||||
case self.kind:
|
||||
|
@ -345,7 +346,7 @@ proc binaryXor*(self, other: ptr Obj): ptr Obj =
|
|||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc binaryShr*(self, other: ptr Obj): ptr Obj =
|
||||
proc binaryShr*(self, other: ptr Obj): returnType =
|
||||
## Returns the result of self >> other
|
||||
## or raises NotImplementedError if the operation is unsupported
|
||||
case self.kind:
|
||||
|
@ -355,7 +356,7 @@ proc binaryShr*(self, other: ptr Obj): ptr Obj =
|
|||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc binaryShl*(self, other: ptr Obj): ptr Obj =
|
||||
proc binaryShl*(self, other: ptr Obj): returnType =
|
||||
## Returns the result of self << other
|
||||
## or raises NotImplementedError if the operation is unsupported
|
||||
case self.kind:
|
||||
|
@ -365,7 +366,7 @@ proc binaryShl*(self, other: ptr Obj): ptr Obj =
|
|||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc binaryNot*(self: ptr Obj): ptr Obj =
|
||||
proc binaryNot*(self: ptr Obj): returnType =
|
||||
## Returns the result of self ~other
|
||||
## or raises NotImplementedError if the operation is unsupported
|
||||
case self.kind:
|
||||
|
@ -405,26 +406,27 @@ proc gt*(self: ptr Obj, other: ptr Obj): bool =
|
|||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc objAs*(self: ptr Obj, other: ObjectType): ptr Obj =
|
||||
proc objAs*(self: ptr Obj, other: ObjectType): returnType =
|
||||
## Returns the result of self as other
|
||||
## (casts self to other's type)
|
||||
|
||||
# TODO -> This is in beta!
|
||||
# TODO -> This is in beta, move to specific types module!
|
||||
result.kind = returnTypes.Object
|
||||
case other:
|
||||
of ObjectType.String:
|
||||
result = stringify(self).asStr()
|
||||
result.result = stringify(self).asStr()
|
||||
of ObjectType.Integer:
|
||||
case self.kind:
|
||||
of ObjectType.String:
|
||||
var intVal: int
|
||||
discard parseInt(cast[ptr String](self).toStr(), intVal)
|
||||
result = intVal.asInt()
|
||||
result.result = intVal.asInt()
|
||||
of ObjectType.Float:
|
||||
var floatVal: float64
|
||||
discard (parseFloat(cast[ptr Float](self).stringify(), floatVal))
|
||||
result = int(floatVal).asInt()
|
||||
result.result = int(floatVal).asInt()
|
||||
of ObjectType.Integer:
|
||||
result = self
|
||||
result.result = self
|
||||
else:
|
||||
raise newException(NotImplementedError, "")
|
||||
of ObjectType.Float:
|
||||
|
@ -432,13 +434,13 @@ proc objAs*(self: ptr Obj, other: ObjectType): ptr Obj =
|
|||
of ObjectType.String:
|
||||
var floatVal: float64
|
||||
discard parseFloat(cast[ptr String](self).toStr(), floatVal)
|
||||
result = floatVal.asFloat()
|
||||
result.result = floatVal.asFloat()
|
||||
of ObjectType.Integer:
|
||||
var intVal: int
|
||||
discard parseInt(cast[ptr Integer](self).stringify(), intVal)
|
||||
result = float(intVal).asFloat()
|
||||
result.result = float(intVal).asFloat()
|
||||
of ObjectType.Float:
|
||||
result = self
|
||||
result.result = self
|
||||
else:
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
@ -446,68 +448,20 @@ proc objAs*(self: ptr Obj, other: ObjectType): ptr Obj =
|
|||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
## Utilities to inspect JAPL objects
|
||||
proc getItem*(self: ptr Obj, other: ptr Obj): returnType =
|
||||
## Implements get expressions with square brackets
|
||||
result.kind = returnTypes.Object
|
||||
case self.kind:
|
||||
of ObjectType.String:
|
||||
result = cast[ptr String](self).getItem(other)
|
||||
else:
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc objType*(obj: ptr Obj): ObjectType =
|
||||
## Returns the type of the object
|
||||
result = obj.kind
|
||||
|
||||
|
||||
proc isCallable*(obj: ptr Obj): bool =
|
||||
## Returns true if the given object
|
||||
## is callable, false otherwise
|
||||
result = obj.kind in {ObjectType.Function, ObjectType.Class, ObjectType.Native}
|
||||
|
||||
|
||||
proc isNil*(obj: ptr Obj): bool =
|
||||
## Returns true if the given obj
|
||||
## is a JAPL nil object
|
||||
result = obj.kind == ObjectType.Nil
|
||||
|
||||
|
||||
proc isBool*(obj: ptr Obj): bool =
|
||||
## Returns true if the given obj
|
||||
## is a JAPL bool
|
||||
result = obj.kind == ObjectType.Bool
|
||||
|
||||
|
||||
proc isInt*(obj: ptr Obj): bool =
|
||||
## Returns true if the given obj
|
||||
## is a JAPL integer
|
||||
result = obj.kind == ObjectType.Integer
|
||||
|
||||
|
||||
proc isFloat*(obj: ptr Obj): bool =
|
||||
## Returns true if the given obj
|
||||
## is a JAPL float
|
||||
result = obj.kind == ObjectType.Float
|
||||
|
||||
|
||||
proc isInf*(obj: ptr Obj): bool =
|
||||
## Returns true if the given obj
|
||||
## is a JAPL inf object
|
||||
result = obj.kind == ObjectType.Infinity
|
||||
|
||||
|
||||
proc isNan*(obj: ptr Obj): bool =
|
||||
## Returns true if the given obj
|
||||
## is a JAPL nan object
|
||||
result = obj.kind == ObjectType.NotANumber
|
||||
|
||||
|
||||
proc isNum*(obj: ptr Obj): bool =
|
||||
## Returns true if the given obj is
|
||||
## either a JAPL number, infinity or nan.
|
||||
## Note to JavaScript developers: No, in JAPL
|
||||
## nan is not a number. Here we consider it like
|
||||
## a number because internally it's easier to
|
||||
## represent it like that for methods that perform
|
||||
## binary operations on numbers, since 2 * nan is
|
||||
## valid JAPL code and will yield nan
|
||||
result = isInt(obj) or isFloat(obj) or isInf(obj) or isNan(obj)
|
||||
|
||||
|
||||
proc isStr*(obj: ptr Obj): bool =
|
||||
## Returns true if the given object is a JAPL string
|
||||
result = obj.kind == ObjectType.String
|
||||
proc Slice*(self: ptr Obj, a: ptr Obj, b: ptr Obj): returnType =
|
||||
## Implements get expressions with square brackets
|
||||
case self.kind:
|
||||
of ObjectType.String:
|
||||
result = cast[ptr String](self).Slice(a, b)
|
||||
else:
|
||||
raise newException(NotImplementedError, "")
|
||||
|
|
|
@ -12,11 +12,14 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Implementation of numerical types
|
||||
|
||||
import baseObject
|
||||
import typeutils
|
||||
|
||||
import bitops
|
||||
import math
|
||||
|
||||
|
||||
# Custom operators for exponentiation
|
||||
proc `**`(a, b: int): int = pow(a.float, b.float).int
|
||||
proc `**`(a, b: float): float = pow(a, b)
|
||||
|
@ -95,7 +98,7 @@ proc eq*(self, other: ptr NotANumber): bool =
|
|||
result = false # As per IEEE 754 spec, nan != nan
|
||||
|
||||
|
||||
proc sum*(self: ptr NotANumber, other: ptr Obj): ptr NotANumber =
|
||||
proc sum*(self: ptr NotANumber, other: ptr Obj): returnType =
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
|
@ -118,12 +121,12 @@ proc hash*(self: ptr Infinity): uint64 =
|
|||
result = 0u
|
||||
|
||||
|
||||
proc negate*(self: ptr Infinity): ptr Infinity =
|
||||
result = self
|
||||
proc negate*(self: ptr Infinity): returnType =
|
||||
result.result = nil
|
||||
if self.isNegative:
|
||||
result.isNegative = false
|
||||
result.kind = returnTypes.Inf
|
||||
else:
|
||||
result.isNegative = true
|
||||
result.kind = returnTypes.nInf
|
||||
|
||||
|
||||
proc eq*(self, other: ptr Infinity): bool =
|
||||
|
@ -178,26 +181,30 @@ proc gt*(self: ptr Infinity, other: ptr Obj): bool =
|
|||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc sum*(self: ptr Infinity, other: ptr Obj): ptr Infinity =
|
||||
result = asInf()
|
||||
proc sum*(self: ptr Infinity, other: ptr Obj): returnType =
|
||||
result.result = nil
|
||||
result.kind = returnTypes.Inf
|
||||
case other.kind:
|
||||
of ObjectType.Infinity:
|
||||
var other = cast[ptr Infinity](other)
|
||||
if self.isNegative or other.isNegative:
|
||||
result.isNegative = true
|
||||
result.kind = returnTypes.nInf
|
||||
of ObjectType.Integer, ObjectType.Float:
|
||||
discard
|
||||
else:
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc sub*(self: ptr Infinity, other: ptr Obj): ptr Infinity =
|
||||
result = asInf()
|
||||
proc sub*(self: ptr Infinity, other: ptr Obj): returnType =
|
||||
result.result = nil
|
||||
result.kind = returnTypes.Inf
|
||||
case other.kind:
|
||||
of ObjectType.Infinity:
|
||||
var other = cast[ptr Infinity](other)
|
||||
if self.isNegative or other.isNegative:
|
||||
result.isNegative = true
|
||||
result.kind = returnTypes.nInf
|
||||
elif not self.isNegative and not other.isNegative:
|
||||
result.kind = returnTypes.NotANumber
|
||||
of ObjectType.Integer, ObjectType.Float:
|
||||
discard
|
||||
else:
|
||||
|
@ -221,8 +228,9 @@ proc eq*(self, other: ptr Float): bool =
|
|||
result = self.floatValue == other.floatValue
|
||||
|
||||
|
||||
proc negate*(self: ptr Float): ptr Float =
|
||||
result = (-self.toFloat()).asFloat()
|
||||
proc negate*(self: ptr Float): returnType =
|
||||
result.kind = returnTypes.Object
|
||||
result.result = (-self.toFloat()).asFloat()
|
||||
|
||||
|
||||
|
||||
|
@ -242,8 +250,9 @@ proc eq*(self, other: ptr Integer): bool =
|
|||
result = self.intValue == other.intValue
|
||||
|
||||
|
||||
proc negate*(self: ptr Integer): ptr Integer =
|
||||
result = (-self.toInt()).asInt()
|
||||
proc negate*(self: ptr Integer): returnType =
|
||||
result.kind = returnTypes.Object
|
||||
result.result = (-self.toInt()).asInt()
|
||||
|
||||
|
||||
proc hash*(self: ptr Integer): uint64 =
|
||||
|
@ -314,343 +323,391 @@ proc gt*(self: ptr Float, other: ptr Obj): bool =
|
|||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc sum*(self: ptr Integer, other: ptr Obj): ptr Obj = # This can yield a float!
|
||||
proc sum*(self: ptr Integer, other: ptr Obj): returnType =
|
||||
case other.kind:
|
||||
of ObjectType.Integer:
|
||||
result = (self.toInt() + cast[ptr Integer](other).toInt()).asInt()
|
||||
result.kind = returnTypes.Object
|
||||
result.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()
|
||||
result.kind = returnTypes.Inf
|
||||
result.result = nil
|
||||
elif res == -system.Inf:
|
||||
let negInf = asInf()
|
||||
negInf.isNegative = true
|
||||
result = negInf
|
||||
result.kind = returnTypes.nInf
|
||||
result.result = nil
|
||||
else:
|
||||
result = res.asFloat()
|
||||
result.kind = returnTypes.Object
|
||||
result.result = res.asFloat()
|
||||
of ObjectType.Infinity:
|
||||
result = cast[ptr Infinity](other)
|
||||
result.kind = returnTypes.Inf
|
||||
result.result = nil
|
||||
else:
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc sum*(self: ptr Float, other: ptr Obj): ptr Obj =
|
||||
proc sum*(self: ptr Float, other: ptr Obj): returnType =
|
||||
case other.kind:
|
||||
of ObjectType.Integer:
|
||||
result = (self.toFloat() + float cast[ptr Integer](other).toInt()).asFloat()
|
||||
result.kind = returnTypes.Object
|
||||
result.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()
|
||||
result.kind = returnTypes.Inf
|
||||
result.result = nil
|
||||
elif res == -system.Inf:
|
||||
let negInf = asInf()
|
||||
negInf.isNegative = true
|
||||
result = negInf
|
||||
result.kind = returnTypes.nInf
|
||||
result.result = nil
|
||||
else:
|
||||
result = res.asFloat()
|
||||
result.kind = returnTypes.Object
|
||||
result.result = res.asFloat()
|
||||
of ObjectType.Infinity:
|
||||
result = cast[ptr Infinity](other)
|
||||
result.kind = returnTypes.Inf
|
||||
result.result = nil
|
||||
else:
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc sub*(self: ptr Integer, other: ptr Obj): ptr Obj = # This can yield a float!
|
||||
proc sub*(self: ptr Integer, other: ptr Obj): returnType =
|
||||
case other.kind:
|
||||
of ObjectType.Integer:
|
||||
result = (self.toInt() - cast[ptr Integer](other).toInt()).asInt()
|
||||
result.kind = returnTypes.Object
|
||||
result.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()
|
||||
result.kind = returnTypes.Inf
|
||||
result.result = nil
|
||||
elif res == -system.Inf:
|
||||
let negInf = asInf()
|
||||
negInf.isNegative = true
|
||||
result = negInf
|
||||
result.kind = returnTypes.nInf
|
||||
result.result = nil
|
||||
else:
|
||||
result = res.asFloat()
|
||||
result.kind = returnTypes.Object
|
||||
result.result = res.asFloat()
|
||||
of ObjectType.Infinity:
|
||||
result = cast[ptr Infinity](other)
|
||||
result.kind = returnTypes.nInf
|
||||
result.result = nil
|
||||
else:
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc sub*(self: ptr NotANumber, other: ptr Obj): ptr NotANumber =
|
||||
proc sub*(self: ptr NotANumber, other: ptr Obj): returnType =
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc sub*(self: ptr Float, other: ptr Obj): ptr Obj =
|
||||
proc sub*(self: ptr Float, other: ptr Obj): returnType =
|
||||
case other.kind:
|
||||
of ObjectType.Integer:
|
||||
result = (self.toFloat() - float cast[ptr Integer](other).toInt()).asFloat()
|
||||
result.kind = returnTypes.Object
|
||||
result.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()
|
||||
result.kind = returnTypes.Inf
|
||||
result.result = nil
|
||||
elif res == -system.Inf:
|
||||
let negInf = asInf()
|
||||
negInf.isNegative = true
|
||||
result = negInf
|
||||
result.kind = returnTypes.nInf
|
||||
result.result = nil
|
||||
else:
|
||||
result = res.asFloat()
|
||||
result.kind = returnTypes.Object
|
||||
result.result = res.asFloat()
|
||||
of ObjectType.Infinity:
|
||||
result = cast[ptr Infinity](other)
|
||||
result.kind = returnTypes.nInf
|
||||
result.result = nil
|
||||
else:
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc mul*(self: ptr Infinity, other: ptr Obj): ptr Infinity =
|
||||
result = asInf()
|
||||
proc mul*(self: ptr Infinity, other: ptr Obj): returnType =
|
||||
result.result = nil
|
||||
result.kind = returnTypes.Inf
|
||||
case other.kind:
|
||||
of ObjectType.Infinity:
|
||||
var other = cast[ptr Infinity](other)
|
||||
if self.isNegative or other.isNegative:
|
||||
result.isNegative = true
|
||||
result.kind = returnTypes.nInf
|
||||
of ObjectType.Integer, ObjectType.Float:
|
||||
discard
|
||||
else:
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc mul*(self: ptr NotANumber, other: ptr Obj): ptr NotANumber =
|
||||
proc mul*(self: ptr NotANumber, other: ptr Obj): returnType =
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
|
||||
proc mul*(self: ptr Integer, other: ptr Obj): ptr Obj = # This can yield a float!
|
||||
proc mul*(self: ptr Integer, other: ptr Obj): returnType =
|
||||
case other.kind:
|
||||
of ObjectType.Integer:
|
||||
result = (self.toInt() * cast[ptr Integer](other).toInt()).asInt()
|
||||
result.kind = returnTypes.Object
|
||||
result.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()
|
||||
result.kind = returnTypes.Inf
|
||||
result.result = nil
|
||||
elif res == -system.Inf:
|
||||
let negInf = asInf()
|
||||
negInf.isNegative = true
|
||||
result = negInf
|
||||
result.kind = returnTypes.nInf
|
||||
result.result = nil
|
||||
else:
|
||||
result = res.asFloat()
|
||||
result.kind = returnTypes.Object
|
||||
result.result = res.asFloat()
|
||||
of ObjectType.Infinity:
|
||||
result = cast[ptr Infinity](other)
|
||||
|
||||
result.kind = returnTypes.nInf
|
||||
result.result = nil
|
||||
else:
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc mul*(self: ptr Float, other: ptr Obj): ptr Obj =
|
||||
|
||||
proc mul*(self: ptr Float, other: ptr Obj): returnType =
|
||||
case other.kind:
|
||||
of ObjectType.Integer:
|
||||
result = (self.toFloat() * float cast[ptr Integer](other).toInt()).asFloat()
|
||||
result.kind = returnTypes.Object
|
||||
result.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()
|
||||
result.kind = returnTypes.Inf
|
||||
result.result = nil
|
||||
elif res == -system.Inf:
|
||||
let negInf = asInf()
|
||||
negInf.isNegative = true
|
||||
result = negInf
|
||||
result.kind = returnTypes.nInf
|
||||
result.result = nil
|
||||
else:
|
||||
result = res.asFloat()
|
||||
result.kind = returnTypes.Object
|
||||
result.result = res.asFloat()
|
||||
of ObjectType.Infinity:
|
||||
result = cast[ptr Infinity](other)
|
||||
result.kind = returnTypes.nInf
|
||||
result.result = nil
|
||||
else:
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc trueDiv*(self: ptr Infinity, other: ptr Obj): ptr Infinity =
|
||||
result = asInf()
|
||||
proc trueDiv*(self: ptr Infinity, other: ptr Obj): returnType =
|
||||
result.result = nil
|
||||
result.kind = returnTypes.Inf
|
||||
case other.kind:
|
||||
of ObjectType.Infinity:
|
||||
var other = cast[ptr Infinity](other)
|
||||
if self.isNegative or other.isNegative:
|
||||
result.isNegative = true
|
||||
result.kind = returnTypes.nInf
|
||||
of ObjectType.Integer, ObjectType.Float:
|
||||
discard
|
||||
else:
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc trueDiv*(self: ptr NotANumber, other: ptr Obj): ptr NotANumber =
|
||||
proc trueDiv*(self: ptr NotANumber, other: ptr Obj): returnType =
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
|
||||
proc trueDiv*(self: ptr Integer, other: ptr Obj): ptr Obj =
|
||||
proc trueDiv*(self: ptr Integer, other: ptr Obj): returnType =
|
||||
case other.kind:
|
||||
of ObjectType.Integer:
|
||||
result = ((float self.toInt()) / (float cast[ptr Integer](other).toInt())).asFloat() # so that 4 / 2 == 2.0
|
||||
result.kind = returnTypes.Object
|
||||
result.result = (float(self.toInt()) / float(cast[ptr Integer](other).toInt())).asFloat() # So that 4 / 2 == 2.0
|
||||
of ObjectType.Float:
|
||||
let res = ((float self.toInt()) / cast[ptr Float](other).toFloat())
|
||||
if res == system.Inf:
|
||||
result = asInf()
|
||||
result.kind = returnTypes.Inf
|
||||
result.result = nil
|
||||
elif res == -system.Inf:
|
||||
let negInf = asInf()
|
||||
negInf.isNegative = true
|
||||
result = negInf
|
||||
result.kind = returnTypes.nInf
|
||||
result.result = nil
|
||||
else:
|
||||
result = res.asFloat()
|
||||
result.kind = returnTypes.Object
|
||||
result.result = res.asFloat()
|
||||
of ObjectType.Infinity:
|
||||
result = cast[ptr Infinity](other)
|
||||
result.kind = returnTypes.nInf
|
||||
result.result = nil
|
||||
else:
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc trueDiv*(self: ptr Float, other: ptr Obj): ptr Obj =
|
||||
proc trueDiv*(self: ptr Float, other: ptr Obj): returnType =
|
||||
case other.kind:
|
||||
of ObjectType.Integer:
|
||||
result = (self.toFloat() / float cast[ptr Integer](other).toInt()).asFloat()
|
||||
result.kind = returnTypes.Object
|
||||
result.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()
|
||||
result.kind = returnTypes.Inf
|
||||
result.result = nil
|
||||
elif res == -system.Inf:
|
||||
let negInf = asInf()
|
||||
negInf.isNegative = true
|
||||
result = negInf
|
||||
result.kind = returnTypes.nInf
|
||||
result.result = nil
|
||||
else:
|
||||
result = res.asFloat()
|
||||
result.kind = returnTypes.Object
|
||||
result.result = res.asFloat()
|
||||
of ObjectType.Infinity:
|
||||
result = cast[ptr Infinity](other)
|
||||
result.kind = returnTypes.nInf
|
||||
result.result = nil
|
||||
else:
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc pow*(self: ptr Infinity, other: ptr Obj): ptr Infinity =
|
||||
result = asInf()
|
||||
proc pow*(self: ptr Infinity, other: ptr Obj): returnType =
|
||||
result.result = nil
|
||||
result.kind = returnTypes.Inf
|
||||
case other.kind:
|
||||
of ObjectType.Infinity:
|
||||
var other = cast[ptr Infinity](other)
|
||||
if self.isNegative or other.isNegative:
|
||||
result.isNegative = true
|
||||
result.kind = returnTypes.nInf
|
||||
of ObjectType.Integer, ObjectType.Float:
|
||||
discard
|
||||
else:
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc pow*(self: ptr NotANumber, other: ptr Obj): ptr NotANumber =
|
||||
proc pow*(self: ptr NotANumber, other: ptr Obj): returnType =
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
|
||||
proc pow*(self: ptr Integer, other: ptr Obj): ptr Obj = # This can yield a float!
|
||||
proc pow*(self: ptr Integer, other: ptr Obj): returnType =
|
||||
case other.kind:
|
||||
of ObjectType.Integer:
|
||||
result = (self.toInt() ** cast[ptr Integer](other).toInt()).asInt()
|
||||
result.kind = returnTypes.Object
|
||||
result.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()
|
||||
result.kind = returnTypes.Inf
|
||||
result.result = nil
|
||||
elif res == -system.Inf:
|
||||
let negInf = asInf()
|
||||
negInf.isNegative = true
|
||||
result = negInf
|
||||
result.kind = returnTypes.nInf
|
||||
result.result = nil
|
||||
else:
|
||||
result = res.asFloat()
|
||||
result.kind = returnTypes.Object
|
||||
result.result = res.asFloat()
|
||||
of ObjectType.Infinity:
|
||||
result = cast[ptr Infinity](other)
|
||||
result.kind = returnTypes.nInf
|
||||
result.result = nil
|
||||
else:
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc pow*(self: ptr Float, other: ptr Obj): ptr Obj =
|
||||
|
||||
proc pow*(self: ptr Float, other: ptr Obj): returnType =
|
||||
case other.kind:
|
||||
of ObjectType.Integer:
|
||||
result = (self.toFloat() ** float cast[ptr Integer](other).toInt()).asFloat()
|
||||
result.kind = returnTypes.Object
|
||||
result.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()
|
||||
result.kind = returnTypes.Inf
|
||||
result.result = nil
|
||||
elif res == -system.Inf:
|
||||
let negInf = asInf()
|
||||
negInf.isNegative = true
|
||||
result = negInf
|
||||
result.kind = returnTypes.nInf
|
||||
result.result = nil
|
||||
else:
|
||||
result = res.asFloat()
|
||||
result.kind = returnTypes.Object
|
||||
result.result = res.asFloat()
|
||||
of ObjectType.Infinity:
|
||||
result = cast[ptr Infinity](other)
|
||||
result.kind = returnTypes.nInf
|
||||
result.result = nil
|
||||
else:
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc divMod*(self: ptr Infinity, other: ptr Obj): ptr Infinity =
|
||||
result = asInf()
|
||||
|
||||
proc divMod*(self: ptr Infinity, other: ptr Obj): returnType =
|
||||
result.result = nil
|
||||
result.kind = returnTypes.Inf
|
||||
case other.kind:
|
||||
of ObjectType.Infinity:
|
||||
var other = cast[ptr Infinity](other)
|
||||
if self.isNegative or other.isNegative:
|
||||
result.isNegative = true
|
||||
result.kind = returnTypes.nInf
|
||||
of ObjectType.Integer, ObjectType.Float:
|
||||
discard
|
||||
else:
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc divMod*(self: ptr NotANumber, other: ptr Obj): ptr NotANumber =
|
||||
proc divMod*(self: ptr NotANumber, other: ptr Obj): returnType =
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc divMod*(self: ptr Integer, other: ptr Obj): ptr Obj = # This can yield a float!
|
||||
proc divMod*(self: ptr Integer, other: ptr Obj): returnType =
|
||||
case other.kind:
|
||||
of ObjectType.Integer:
|
||||
result = (self.toInt() mod cast[ptr Integer](other).toInt()).asInt()
|
||||
result.kind = returnTypes.Object
|
||||
result.result = (self.toInt() mod cast[ptr Integer](other).toInt()).asInt()
|
||||
of ObjectType.Float:
|
||||
let res = ((float self.toInt()) mod cast[ptr Float](other).toFloat())
|
||||
if res == system.Inf:
|
||||
result = asInf()
|
||||
result.kind = returnTypes.Inf
|
||||
result.result = nil
|
||||
elif res == -system.Inf:
|
||||
let negInf = asInf()
|
||||
negInf.isNegative = true
|
||||
result = negInf
|
||||
result.kind = returnTypes.nInf
|
||||
result.result = nil
|
||||
else:
|
||||
result = res.asFloat()
|
||||
result.kind = returnTypes.Object
|
||||
result.result = res.asFloat()
|
||||
of ObjectType.Infinity:
|
||||
result = cast[ptr Infinity](other)
|
||||
result.kind = returnTypes.nInf
|
||||
result.result = nil
|
||||
else:
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc divMod*(self: ptr Float, other: ptr Obj): ptr Obj =
|
||||
proc divMod*(self: ptr Float, other: ptr Obj): returnType =
|
||||
case other.kind:
|
||||
of ObjectType.Integer:
|
||||
result = (self.toFloat() mod float cast[ptr Integer](other).toInt()).asFloat()
|
||||
result.kind = returnTypes.Object
|
||||
result.result = (self.toFloat() mod float cast[ptr Integer](other).toInt()).asFloat()
|
||||
of ObjectType.Float:
|
||||
let res = (self.toFloat() mod cast[ptr Float](other).toFloat())
|
||||
if res == system.Inf:
|
||||
result = asInf()
|
||||
result.kind = returnTypes.Inf
|
||||
result.result = nil
|
||||
elif res == -system.Inf:
|
||||
let negInf = asInf()
|
||||
negInf.isNegative = true
|
||||
result = negInf
|
||||
result.kind = returnTypes.nInf
|
||||
result.result = nil
|
||||
else:
|
||||
result = res.asFloat()
|
||||
result.kind = returnTypes.Object
|
||||
result.result = res.asFloat()
|
||||
of ObjectType.Infinity:
|
||||
result = cast[ptr Infinity](other)
|
||||
result.kind = returnTypes.nInf
|
||||
result.result = nil
|
||||
else:
|
||||
raise newException(NotImplementedError, "")
|
||||
|
||||
|
||||
proc binaryAnd*(self, other: ptr Integer): ptr Integer =
|
||||
result = bitand(self.toInt(), other.toInt()).asInt()
|
||||
proc binaryAnd*(self, other: ptr Integer): returnType =
|
||||
result.kind = returnTypes.Object
|
||||
result.result = bitand(self.toInt(), other.toInt()).asInt()
|
||||
|
||||
|
||||
proc binaryOr*(self, other: ptr Integer): ptr Integer =
|
||||
result = bitor(self.toInt(), other.toInt()).asInt()
|
||||
proc binaryOr*(self, other: ptr Integer): returnType =
|
||||
result.kind = returnTypes.Object
|
||||
result.result = bitor(self.toInt(), other.toInt()).asInt()
|
||||
|
||||
|
||||
proc binaryNot*(self: ptr Integer): ptr Integer =
|
||||
result = bitnot(self.toInt()).asInt()
|
||||
proc binaryNot*(self: ptr Integer): returnType =
|
||||
result.kind = returnTypes.Object
|
||||
result.result = bitnot(self.toInt()).asInt()
|
||||
|
||||
|
||||
proc binaryXor*(self, other: ptr Integer): ptr Integer =
|
||||
result = bitxor(self.toInt(), other.toInt()).asInt()
|
||||
proc binaryXor*(self, other: ptr Integer): returnType =
|
||||
result.kind = returnTypes.Object
|
||||
result.result = bitxor(self.toInt(), other.toInt()).asInt()
|
||||
|
||||
|
||||
|
||||
proc binaryShr*(self, other: ptr Integer): ptr Integer =
|
||||
result = (self.toInt() shr other.toInt()).asInt()
|
||||
proc binaryShr*(self, other: ptr Integer): returnType =
|
||||
result.kind = returnTypes.Object
|
||||
result.result = (self.toInt() shr other.toInt()).asInt()
|
||||
|
||||
|
||||
|
||||
proc binaryShl*(self, other: ptr Integer): ptr Integer =
|
||||
result = (self.toInt() shr other.toInt()).asInt()
|
||||
proc binaryShl*(self, other: ptr Integer): returnType =
|
||||
result.kind = returnTypes.Object
|
||||
result.result = (self.toInt() shr other.toInt()).asInt()
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
# Copyright 2020 Mattia Giambirtone
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
## Utilities to inspect JAPL objects
|
||||
|
||||
import baseObject
|
||||
|
||||
|
||||
type
|
||||
returnTypes* {.pure.} = enum
|
||||
## Pretty much like retNative, we use
|
||||
## this handy enum to differentiate
|
||||
## what is what, perform better error
|
||||
## handling and (most importantly)
|
||||
## maintain our invariant that singletons
|
||||
## always refer to the same object
|
||||
False,
|
||||
True,
|
||||
Inf,
|
||||
nInf,
|
||||
Nil,
|
||||
NotANumber,
|
||||
Object,
|
||||
Exception
|
||||
returnType* = tuple[kind: returnTypes, result: ptr Obj]
|
||||
|
||||
|
||||
|
||||
proc objType*(obj: ptr Obj): ObjectType =
|
||||
## Returns the type of the object
|
||||
result = obj.kind
|
||||
|
||||
|
||||
proc isCallable*(obj: ptr Obj): bool =
|
||||
## Returns true if the given object
|
||||
## is callable, false otherwise
|
||||
result = obj.kind in {ObjectType.Function, ObjectType.Class, ObjectType.Native}
|
||||
|
||||
|
||||
proc isNil*(obj: ptr Obj): bool =
|
||||
## Returns true if the given obj
|
||||
## is a JAPL nil object
|
||||
result = obj.kind == ObjectType.Nil
|
||||
|
||||
|
||||
proc isBool*(obj: ptr Obj): bool =
|
||||
## Returns true if the given obj
|
||||
## is a JAPL bool
|
||||
result = obj.kind == ObjectType.Bool
|
||||
|
||||
|
||||
proc isInt*(obj: ptr Obj): bool =
|
||||
## Returns true if the given obj
|
||||
## is a JAPL integer
|
||||
result = obj.kind == ObjectType.Integer
|
||||
|
||||
|
||||
proc isFloat*(obj: ptr Obj): bool =
|
||||
## Returns true if the given obj
|
||||
## is a JAPL float
|
||||
result = obj.kind == ObjectType.Float
|
||||
|
||||
|
||||
proc isInf*(obj: ptr Obj): bool =
|
||||
## Returns true if the given obj
|
||||
## is a JAPL inf object
|
||||
result = obj.kind == ObjectType.Infinity
|
||||
|
||||
|
||||
proc isNan*(obj: ptr Obj): bool =
|
||||
## Returns true if the given obj
|
||||
## is a JAPL nan object
|
||||
result = obj.kind == ObjectType.NotANumber
|
||||
|
||||
|
||||
proc isNum*(obj: ptr Obj): bool =
|
||||
## Returns true if the given obj is
|
||||
## either a JAPL number, infinity or nan.
|
||||
## Note to JavaScript developers: No, in JAPL
|
||||
## nan is not a number. Here we consider it like
|
||||
## a number because internally it's easier to
|
||||
## represent it like that for methods that perform
|
||||
## binary operations on numbers, since 2 * nan is
|
||||
## valid JAPL code and will yield nan
|
||||
result = isInt(obj) or isFloat(obj) or isInf(obj) or isNan(obj)
|
||||
|
||||
|
||||
proc isStr*(obj: ptr Obj): bool =
|
||||
## Returns true if the given object is a JAPL string
|
||||
result = obj.kind == ObjectType.String
|
|
@ -34,7 +34,7 @@ proc byteInstruction(name: string, chunk: Chunk, offset: int): int =
|
|||
return offset + 2
|
||||
|
||||
|
||||
proc constantLongInstruction(name: string, chunk: Chunk, offset: int): int =
|
||||
proc constantInstruction(name: string, chunk: Chunk, offset: int): int =
|
||||
# Rebuild the index
|
||||
var constantArray: array[3, uint8] = [chunk.code[offset + 1], chunk.code[offset + 2], chunk.code[offset + 3]]
|
||||
var constant: int
|
||||
|
@ -45,14 +45,6 @@ proc constantLongInstruction(name: string, chunk: Chunk, offset: int): int =
|
|||
return offset + 4
|
||||
|
||||
|
||||
proc constantInstruction(name: string, chunk: Chunk, offset: int): int =
|
||||
var constant = chunk.code[offset + 1]
|
||||
echo &"\tInstruction at IP: {name}, points to index {constant}"
|
||||
let obj = chunk.consts[constant]
|
||||
echo &"\tOperand: {stringify(obj)}\n\tValue kind: {obj.kind}\n"
|
||||
return offset + 2
|
||||
|
||||
|
||||
proc jumpInstruction(name: string, chunk: Chunk, offset: int): int =
|
||||
var jumpArray: array[2, uint8] = [chunk.code[offset + 1], chunk.code[offset + 2]]
|
||||
var jump: int
|
||||
|
@ -70,8 +62,6 @@ proc disassembleInstruction*(chunk: Chunk, offset: int): int =
|
|||
result = simpleInstruction($opcode, offset)
|
||||
of constantInstructions:
|
||||
result = constantInstruction($opcode, chunk, offset)
|
||||
of constantLongInstructions:
|
||||
result = constantLongInstruction($opcode, chunk, offset)
|
||||
of byteInstructions:
|
||||
result = byteInstruction($opcode, chunk, offset)
|
||||
of jumpInstructions:
|
||||
|
|
172
src/vm.nim
172
src/vm.nim
|
@ -35,6 +35,7 @@ import types/exception
|
|||
import types/numbers
|
||||
import types/boolean
|
||||
import types/methods
|
||||
import types/typeutils
|
||||
import types/function
|
||||
import types/native
|
||||
# We always import it to
|
||||
|
@ -102,7 +103,7 @@ proc error*(self: VM, error: ptr JAPLException) =
|
|||
|
||||
# Exceptions are objects too and they need to
|
||||
# be freed like any other entity in JAPL
|
||||
self.objects.add(error)
|
||||
self.objects.add(error) # TODO -> Move this somewhere else to mark exceptions even before they are raised
|
||||
var previous = "" # All this stuff seems overkill, but it makes the traceback look nicer
|
||||
var repCount = 0 # and if we are here we are far beyond a point where performance matters anyway
|
||||
var mainReached = false
|
||||
|
@ -145,81 +146,34 @@ proc push*(self: VM, obj: ptr Obj) =
|
|||
self.stackTop += 1
|
||||
|
||||
|
||||
proc push*(self: VM, ret: returnType) =
|
||||
## Pushes a return value from a builtin
|
||||
## method onto the stack and handles errors
|
||||
case ret.kind:
|
||||
of returnTypes.Object:
|
||||
self.push(ret.result)
|
||||
of returnTypes.Exception:
|
||||
self.error(cast[ptr JAPLException](ret.result))
|
||||
of returnTypes.True:
|
||||
self.push(self.cached[0])
|
||||
of returnTypes.False:
|
||||
self.push(self.cached[1])
|
||||
of returnTypes.Nil:
|
||||
self.push(self.cached[2])
|
||||
of returnTypes.Inf:
|
||||
self.push(self.cached[3])
|
||||
of returnTypes.nInf:
|
||||
self.push(self.cached[4])
|
||||
of returnTypes.NotANumber:
|
||||
self.push(self.cached[5])
|
||||
|
||||
|
||||
proc peek*(self: VM, distance: int): ptr Obj =
|
||||
## Peeks an object (at a given distance from the
|
||||
## current index) from the stack
|
||||
return self.stack[self.stackTop - distance - 1]
|
||||
|
||||
|
||||
# TODO: Move this to types/
|
||||
proc slice(self: VM): bool =
|
||||
## Handles single-operator slice expressions
|
||||
## (consider moving this to an appropriate
|
||||
## slice method)
|
||||
var idx = self.pop()
|
||||
var peeked = self.pop()
|
||||
case peeked.kind:
|
||||
of ObjectType.String:
|
||||
var str = peeked.toStr()
|
||||
if not idx.isInt():
|
||||
self.error(newTypeError("string indeces must be integers"))
|
||||
return false
|
||||
else:
|
||||
var index: int = idx.toInt()
|
||||
if index < 0:
|
||||
index = len(str) + idx.toInt()
|
||||
if index < 0: # If even now it is less than 0 than it is out of bounds
|
||||
self.error(newIndexError("string index out of bounds"))
|
||||
return false
|
||||
elif index - 1 > len(str) - 1:
|
||||
self.error(newIndexError("string index out of bounds"))
|
||||
return false
|
||||
else:
|
||||
self.push(asStr(&"{str[index]}"))
|
||||
return true
|
||||
else:
|
||||
self.error(newTypeError(&"unsupported slicing for object of type '{peeked.typeName()}'"))
|
||||
return false
|
||||
|
||||
# TODO: Move this to types/
|
||||
proc sliceRange(self: VM): bool =
|
||||
## Handles slices when there's both a start
|
||||
## and an end index (even implicit ones)
|
||||
var sliceEnd = self.pop()
|
||||
var sliceStart = self.pop()
|
||||
var popped = self.pop()
|
||||
case popped.kind:
|
||||
of ObjectType.String:
|
||||
var str = popped.toStr()
|
||||
if sliceEnd.isNil():
|
||||
sliceEnd = len(str).asInt()
|
||||
if sliceStart.isNil():
|
||||
sliceStart = asInt(0)
|
||||
elif not sliceStart.isInt() or not sliceEnd.isInt():
|
||||
self.error(newTypeError("string indexes must be integers"))
|
||||
return false
|
||||
else:
|
||||
var startIndex = sliceStart.toInt()
|
||||
var endIndex = sliceEnd.toInt()
|
||||
if startIndex < 0:
|
||||
sliceStart = (len(str) + sliceStart.toInt()).asInt()
|
||||
if startIndex < 0:
|
||||
sliceStart = (len(str) + sliceEnd.toInt()).asInt()
|
||||
elif startIndex - 1 > len(str) - 1:
|
||||
self.push(asStr(""))
|
||||
return true
|
||||
if endIndex - 1 > len(str) - 1:
|
||||
sliceEnd = len(str).asInt()
|
||||
if startIndex > endIndex:
|
||||
self.push(asStr(""))
|
||||
return true
|
||||
self.push(asStr(str[sliceStart.toInt()..<sliceEnd.toInt()]))
|
||||
return true
|
||||
else:
|
||||
self.error(newTypeError(&"unsupported slicing for object of type '{popped.typeName()}'"))
|
||||
return false
|
||||
|
||||
|
||||
proc call(self: VM, function: ptr Function, argCount: int): bool =
|
||||
## Sets up the call frame and performs error checking
|
||||
## when calling callables
|
||||
|
@ -315,18 +269,13 @@ proc readShort(self: CallFrame): uint16 =
|
|||
proc readConstant(self: CallFrame): ptr Obj =
|
||||
## Reads a constant from the given
|
||||
## frame's constant table
|
||||
result = self.function.chunk.consts[uint8 self.readByte()]
|
||||
|
||||
|
||||
proc readLongConstant(self: CallFrame): ptr Obj =
|
||||
## Reads a long constant from the
|
||||
## given frame's constant table
|
||||
var arr = [self.readByte(), self.readByte(), self.readByte()]
|
||||
var idx: int
|
||||
copyMem(idx.addr, unsafeAddr(arr), sizeof(arr))
|
||||
copyMem(idx.addr, arr.addr, 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
|
||||
|
@ -368,24 +317,18 @@ proc run(self: VM, repl: bool): InterpretResult =
|
|||
## them one at a time: this is the runtime's
|
||||
## main loop
|
||||
var frame = self.frames[self.frameCount - 1]
|
||||
var opcode: OpCode
|
||||
when DEBUG_TRACE_VM:
|
||||
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
|
||||
self.showRuntime(frame, iteration)
|
||||
case opcode: # Main OpCodes dispatcher
|
||||
case OpCode(frame.readByte()): # Main OpCodes dispatcher
|
||||
of OpCode.Constant:
|
||||
# Loads a constant from the chunk's constant
|
||||
# table
|
||||
self.push(frame.readConstant())
|
||||
of OpCode.ConstantLong:
|
||||
# Loads a constant from the chunk's constant
|
||||
# table when its size exceeds 256
|
||||
self.push(frame.readLongConstant())
|
||||
of OpCode.Negate:
|
||||
# Performs unary negation
|
||||
let operand = self.pop()
|
||||
|
@ -560,29 +503,31 @@ proc run(self: VM, repl: bool): InterpretResult =
|
|||
of OpCode.GetItem:
|
||||
# Implements expressions such as a[b]
|
||||
# TODO: More generic method
|
||||
if not self.slice():
|
||||
var right = self.pop()
|
||||
var left = self.pop()
|
||||
try:
|
||||
self.push(left.getItem(right))
|
||||
except NotImplementedError:
|
||||
self.error(newTypeError(&"object of type '{left.typeName()}' does not support getItem expressions"))
|
||||
return RuntimeError
|
||||
of OpCode.Slice:
|
||||
# Implements expressions such as a[b:c]
|
||||
# TODO: More generic method
|
||||
if not self.sliceRange():
|
||||
var right = self.pop()
|
||||
var left = self.pop()
|
||||
var operand = self.pop()
|
||||
try:
|
||||
self.push(operand.Slice(right, left))
|
||||
except NotImplementedError:
|
||||
self.error(newTypeError(&"object of type '{operand.typeName()}' does not support slicing"))
|
||||
return RuntimeError
|
||||
of OpCode.DefineGlobal:
|
||||
# Defines a global variable
|
||||
var name: string
|
||||
if frame.function.chunk.consts.len > 255:
|
||||
name = frame.readLongConstant().toStr()
|
||||
else:
|
||||
name = frame.readConstant().toStr()
|
||||
var name = frame.readConstant().toStr()
|
||||
self.globals[name] = self.peek(0)
|
||||
discard self.pop()
|
||||
of OpCode.GetGlobal:
|
||||
# Retrieves a global variable
|
||||
var constant: string
|
||||
if frame.function.chunk.consts.len > 255:
|
||||
constant = frame.readLongConstant().toStr()
|
||||
else:
|
||||
constant = frame.readConstant().toStr()
|
||||
var constant = frame.readConstant().toStr()
|
||||
if constant notin self.globals:
|
||||
self.error(newReferenceError(&"undefined name '{constant}'"))
|
||||
return RuntimeError
|
||||
|
@ -590,11 +535,7 @@ proc run(self: VM, repl: bool): InterpretResult =
|
|||
self.push(self.globals[constant])
|
||||
of OpCode.SetGlobal:
|
||||
# Changes the value of an already defined global variable
|
||||
var constant: string
|
||||
if frame.function.chunk.consts.len > 255:
|
||||
constant = frame.readLongConstant().toStr()
|
||||
else:
|
||||
constant = frame.readConstant().toStr()
|
||||
var constant = frame.readConstant().toStr()
|
||||
if constant notin self.globals:
|
||||
self.error(newReferenceError(&"assignment to undeclared name '{constant}'"))
|
||||
return RuntimeError
|
||||
|
@ -603,11 +544,7 @@ proc run(self: VM, repl: bool): InterpretResult =
|
|||
of OpCode.DeleteGlobal:
|
||||
# Deletes a global variable
|
||||
# TODO: Inspect potential issues with the GC
|
||||
var constant: string
|
||||
if frame.function.chunk.consts.len > 255:
|
||||
constant = frame.readLongConstant().toStr()
|
||||
else:
|
||||
constant = frame.readConstant().toStr()
|
||||
var constant = frame.readConstant().toStr()
|
||||
if constant notin self.globals:
|
||||
self.error(newReferenceError(&"undefined name '{constant}'"))
|
||||
return RuntimeError
|
||||
|
@ -615,29 +552,14 @@ proc run(self: VM, repl: bool): InterpretResult =
|
|||
self.globals.del(constant)
|
||||
of OpCode.GetLocal:
|
||||
# Retrieves a local variable
|
||||
var slot: int
|
||||
if frame.len > 255:
|
||||
slot = frame.readBytes()
|
||||
else:
|
||||
slot = int frame.readByte()
|
||||
self.push(frame[slot])
|
||||
self.push(frame[frame.readBytes()])
|
||||
of OpCode.SetLocal:
|
||||
# Changes the value of an already defined local variable
|
||||
var slot: int
|
||||
if frame.len > 255:
|
||||
slot = frame.readBytes()
|
||||
else:
|
||||
slot = int frame.readByte()
|
||||
frame[slot] = self.peek(0)
|
||||
frame[frame.readBytes()] = self.peek(0)
|
||||
of OpCode.DeleteLocal:
|
||||
# Deletes a global variable
|
||||
# TODO: Inspect potential issues with the GC
|
||||
var slot: int
|
||||
if frame.len > 255:
|
||||
slot = frame.readBytes()
|
||||
else:
|
||||
slot = int frame.readByte()
|
||||
frame.delete(slot)
|
||||
frame.delete(frame.readBytes())
|
||||
of OpCode.Pop:
|
||||
# Pops an item off the stack
|
||||
self.lastPop = self.pop()
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
// Test for constants
|
||||
|
||||
var v_1 = 1;
|
||||
var v_2 = 1;
|
||||
var v_3 = 1;
|
||||
var v_4 = 1;
|
||||
var v_5 = 1;
|
||||
var v_6 = 1;
|
||||
var v_7 = 1;
|
||||
var v_8 = 1;
|
||||
var v_9 = 1;
|
||||
var v_10 = 1;
|
||||
var v_11 = 1;
|
||||
var v_12 = 1;
|
||||
var v_13 = 1;
|
||||
var v_14 = 1;
|
||||
var v_15 = 1;
|
||||
var v_16 = 1;
|
||||
var v_17 = 1;
|
||||
var v_18 = 1;
|
||||
var v_19 = 1;
|
||||
var v_20 = 1;
|
||||
var v_21 = 1;
|
||||
var v_22 = 1;
|
||||
var v_23 = 1;
|
||||
var v_24 = 1;
|
||||
var v_25 = 1;
|
||||
var v_26 = 1;
|
||||
var v_27 = 1;
|
||||
var v_28 = 1;
|
||||
var v_29 = 1;
|
||||
var v_30 = 1;
|
||||
var v_31 = 1;
|
||||
var v_32 = 1;
|
||||
var v_33 = 1;
|
||||
var v_34 = 1;
|
||||
var v_35 = 1;
|
||||
var v_36 = 1;
|
||||
var v_37 = 1;
|
||||
var v_38 = 1;
|
||||
var v_39 = 1;
|
||||
var v_40 = 1;
|
||||
var v_41 = 1;
|
||||
var v_42 = 1;
|
||||
var v_43 = 1;
|
||||
var v_44 = 1;
|
||||
var v_45 = 1;
|
||||
var v_46 = 1;
|
||||
var v_47 = 1;
|
||||
var v_48 = 1;
|
||||
var v_49 = 1;
|
||||
var v_50 = 1;
|
||||
var v_51 = 1;
|
||||
var v_52 = 1;
|
||||
var v_53 = 1;
|
||||
var v_54 = 1;
|
||||
var v_55 = 1;
|
||||
var v_56 = 1;
|
||||
var v_57 = 1;
|
||||
var v_58 = 1;
|
||||
var v_59 = 1;
|
||||
var v_60 = 1;
|
||||
var v_61 = 1;
|
||||
var v_62 = 1;
|
||||
var v_63 = 1;
|
||||
var v_64 = 1;
|
||||
var v_65 = 1;
|
||||
var v_66 = 1;
|
||||
var v_67 = 1;
|
||||
var v_68 = 1;
|
||||
var v_69 = 1;
|
||||
var v_70 = 1;
|
||||
var v_71 = 1;
|
||||
var v_72 = 1;
|
||||
var v_73 = 1;
|
||||
var v_74 = 1;
|
||||
var v_75 = 1;
|
||||
var v_76 = 1;
|
||||
var v_77 = 1;
|
||||
var v_78 = 1;
|
||||
var v_79 = 1;
|
||||
var v_80 = 1;
|
||||
var v_81 = 1;
|
||||
var v_82 = 1;
|
||||
var v_83 = 1;
|
||||
var v_84 = 1;
|
||||
var v_85 = 1;
|
||||
var v_86 = 1;
|
||||
var v_87 = 1;
|
||||
var v_88 = 1;
|
||||
var v_89 = 1;
|
||||
var v_90 = 1;
|
||||
var v_91 = 1;
|
||||
var v_92 = 1;
|
||||
var v_93 = 1;
|
||||
var v_94 = 1;
|
||||
var v_95 = 1;
|
||||
var v_96 = 1;
|
||||
var v_97 = 1;
|
||||
var v_98 = 1;
|
||||
var v_99 = 1;
|
||||
var v_100 = 1;
|
||||
var v_101 = 1;
|
||||
var v_102 = 1;
|
||||
var v_103 = 1;
|
||||
var v_104 = 1;
|
||||
var v_105 = 1;
|
||||
var v_106 = 1;
|
||||
var v_107 = 1;
|
||||
var v_108 = 1;
|
||||
var v_109 = 1;
|
||||
var v_110 = 1;
|
||||
var v_111 = 1;
|
||||
var v_112 = 1;
|
||||
var v_113 = 1;
|
||||
var v_114 = 1;
|
||||
var v_115 = 1;
|
||||
var v_116 = 1;
|
||||
var v_117 = 1;
|
||||
var v_118 = 1;
|
||||
var v_119 = 1;
|
||||
var v_120 = 1;
|
||||
var v_121 = 1;
|
||||
var v_122 = 1;
|
||||
var v_123 = 1;
|
||||
var v_124 = 1;
|
||||
var v_125 = 1;
|
||||
var v_126 = 1;
|
||||
var v_127 = 1;
|
||||
var v_128 = 1;
|
Loading…
Reference in New Issue