mirror of https://github.com/japl-lang/japl.git
Type system refactoring partially done, the VM is now at a compilable and semi-runnable state
This commit is contained in:
parent
3b67c95736
commit
e87096ce71
|
@ -12,6 +12,7 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import strformat
|
||||
|
||||
|
||||
const FRAMES_MAX* = 400 # TODO: Inspect why the VM crashes if this exceeds 400
|
||||
|
@ -21,4 +22,22 @@ const DEBUG_TRACE_VM* = false # Traces VM execution
|
|||
const DEBUG_TRACE_GC* = true # Traces the garbage collector (TODO)
|
||||
const DEBUG_TRACE_ALLOCATION* = true # Traces memory allocation/deallocation (WIP)
|
||||
const DEBUG_TRACE_COMPILER* = false # Traces the compiler (WIP)
|
||||
const JAPL_VERSION_STRING* = &"JAPL {JAPL_VERSION} ({JAPL_RELEASE}, {CompileDate} {CompileTime})"
|
||||
const HELP_MESSAGE* = """The JAPL runtime interface, Copyright (C) 2020 Mattia Giambirtone
|
||||
This program is free software, see the license distributed with this program or check
|
||||
http://www.apache.org/licenses/LICENSE-2.0 for more info.
|
||||
|
||||
Basic usage
|
||||
-----------
|
||||
|
||||
$ jpl -> Start the REPL
|
||||
|
||||
$ jpl filename.jpl -> Run filename.jpl
|
||||
|
||||
|
||||
Command-line options
|
||||
--------------------
|
||||
|
||||
-h, --help -> Show this help text and exit
|
||||
-v, --version -> Print the JAPL version number and exit
|
||||
"""
|
||||
|
|
25
src/main.nim
25
src/main.nim
|
@ -21,9 +21,10 @@ import os
|
|||
import config
|
||||
import vm
|
||||
|
||||
|
||||
proc repl() =
|
||||
var bytecodeVM = initVM()
|
||||
echo &"JAPL {JAPL_VERSION} ({JAPL_RELEASE}, {CompileDate} {CompileTime})"
|
||||
echo JAPL_VERSION_STRING
|
||||
echo &"[Nim {NimVersion} on {hostOs} ({hostCPU})]"
|
||||
when DEBUG_TRACE_VM:
|
||||
echo "Debugger enabled, expect verbose output\n"
|
||||
|
@ -45,7 +46,7 @@ proc repl() =
|
|||
break
|
||||
if source == "//clear" or source == "// clear":
|
||||
echo "\x1Bc"
|
||||
echo &"JAPL {JAPL_VERSION} ({JAPL_RELEASE}, {CompileDate} {CompileTime})"
|
||||
echo JAPL_VERSION_STRING
|
||||
echo &"[Nim {NimVersion} on {hostOs} ({hostCPU})]"
|
||||
continue
|
||||
elif source != "":
|
||||
|
@ -98,12 +99,26 @@ when isMainModule:
|
|||
file = key
|
||||
of cmdLongOption:
|
||||
case key:
|
||||
of "debug":
|
||||
echo "Debug mode must be enabled via common.nim!"
|
||||
of "help":
|
||||
echo HELP_MESSAGE
|
||||
quit()
|
||||
of "version":
|
||||
echo JAPL_VERSION_STRING
|
||||
quit()
|
||||
else:
|
||||
echo &"Unkown option '{key}'"
|
||||
echo &"error: unkown option '{key}'"
|
||||
quit()
|
||||
of cmdShortOption:
|
||||
case key:
|
||||
of "h":
|
||||
echo HELP_MESSAGE
|
||||
quit()
|
||||
of "v":
|
||||
echo JAPL_VERSION_STRING
|
||||
quit()
|
||||
else:
|
||||
echo &"error: unkown option '{key}'"
|
||||
quit()
|
||||
else:
|
||||
echo "usage: japl [filename]"
|
||||
quit()
|
||||
|
|
|
@ -19,7 +19,7 @@ import ../types/jobject
|
|||
|
||||
type
|
||||
OpCode* {.pure.} = enum
|
||||
## Enum of possible opcodes.
|
||||
## Enum of possible opcodes
|
||||
Constant = 0u8,
|
||||
ConstantLong,
|
||||
Return,
|
||||
|
|
|
@ -20,11 +20,14 @@ import strformat
|
|||
|
||||
|
||||
type
|
||||
Chunk* = ref object
|
||||
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
|
||||
## A piece of bytecode.
|
||||
## Consts represents the constants the code is referring to
|
||||
## Code represents the bytecode
|
||||
## Lines represents which lines the corresponding bytecode was one (1 to 1 correspondence)
|
||||
## Consts represents the constants table the code is referring to
|
||||
## Code is the compiled bytecode
|
||||
## Lines maps bytecode instructions to line numbers (1 to 1 correspondence)
|
||||
consts*: seq[ptr Obj]
|
||||
code*: seq[uint8]
|
||||
lines*: seq[int]
|
||||
|
@ -32,45 +35,52 @@ type
|
|||
## All the possible object types
|
||||
String, Exception, Function,
|
||||
Class, Module, BaseObject,
|
||||
Integer, Float, Bool, Nan,
|
||||
Integer, Float, Bool, NotANumber,
|
||||
Infinity, Nil
|
||||
Obj* = object of RootObj
|
||||
# The object that rules them all
|
||||
## The base object for all
|
||||
## JAPL types. Every object
|
||||
## in JAPL implicitly inherits
|
||||
## from this base type
|
||||
kind*: ObjectType
|
||||
hashValue*: uint32
|
||||
String* = object of Obj # A string object
|
||||
hashValue*: uint64
|
||||
String* = object of Obj
|
||||
## A string object
|
||||
str*: ptr UncheckedArray[char] # TODO -> Unicode support
|
||||
len*: int
|
||||
Integer* = object of Obj
|
||||
# An integer object
|
||||
## An integer object
|
||||
intValue: int # TODO: Bignum arithmetic
|
||||
Bool* = object of Integer
|
||||
## A boolean object
|
||||
boolValue: bool # If the boolean is true or false
|
||||
Nil* = object of Bool
|
||||
## A nil object
|
||||
Float* = object of Integer
|
||||
# A float object
|
||||
## A float object
|
||||
floatValue: float
|
||||
Infinity* = object of Float # Inf is considered a float
|
||||
## An inf object
|
||||
isNegative: bool # This differentiates inf and -inf
|
||||
NotANumber* = object of Float # NaN is as well (IEEE 754)
|
||||
## A nan object
|
||||
Function* = object of Obj
|
||||
## A function objecy
|
||||
name*: ptr String
|
||||
arity*: int
|
||||
optionals*: int
|
||||
defaults*: seq[ptr Obj]
|
||||
chunk*: Chunk
|
||||
JAPLException* = object of Obj
|
||||
errName*: ptr String
|
||||
arity*: int # The number of required parameters
|
||||
optionals*: int # How many optional parameters
|
||||
defaults*: seq[ptr Obj] # List of default arguments, in order
|
||||
chunk*: Chunk # The function's body
|
||||
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
|
||||
|
||||
|
||||
proc newChunk*(): Chunk =
|
||||
## The constructor for the type Chunk
|
||||
result = Chunk(consts: @[], code: @[], lines: @[])
|
||||
|
||||
## Object constructors and allocators
|
||||
|
||||
proc allocateObject*(size: int, kind: ObjectType): ptr Obj =
|
||||
## Wrapper around reallocate to create a new generic JAPL object
|
||||
## Wrapper around memory.reallocate to create a new generic JAPL object
|
||||
result = cast[ptr Obj](reallocate(nil, 0, size))
|
||||
result.kind = kind
|
||||
|
||||
|
@ -81,70 +91,419 @@ template allocateObj*(kind: untyped, objType: ObjectType): untyped =
|
|||
cast[ptr kind](allocateObject(sizeof kind, objType))
|
||||
|
||||
|
||||
proc newChunk*(): Chunk =
|
||||
## Initializes a new, empty chunk
|
||||
result = Chunk(consts: @[], code: @[], lines: @[])
|
||||
|
||||
|
||||
proc objType*(obj: ptr Obj): ObjectType =
|
||||
## Returns the type of the object
|
||||
return obj.kind
|
||||
|
||||
# Stringify
|
||||
|
||||
template unimplementedException(): ref CatchableError =
|
||||
newException(CatchableError, "Unimplemented TODO")
|
||||
## Utilities that bridge nim and JAPL types
|
||||
|
||||
method stringify*(s: ptr Obj): string {.base.} =
|
||||
raise unimplementedException()
|
||||
proc isNil*(obj: ptr Obj): bool =
|
||||
## Returns true if the given obj
|
||||
## is a JAPL nil object
|
||||
result = obj.kind == ObjectType.Nil
|
||||
|
||||
method stringify*(s: ptr String): string =
|
||||
result = ""
|
||||
for i in 0..<s.len:
|
||||
result = result & (&"{s.str[i]}")
|
||||
|
||||
method stringify(self: ptr Integer): string =
|
||||
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, nan or inf
|
||||
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 toBool*(obj: ptr Obj): bool =
|
||||
## Converts a JAPL bool to a nim bool
|
||||
result = cast[ptr Bool](obj).boolValue
|
||||
|
||||
|
||||
proc toInt*(obj: ptr Obj): int =
|
||||
## Converts a JAPL int to a nim int
|
||||
result = cast[ptr Integer](obj).intValue
|
||||
|
||||
|
||||
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)
|
||||
for i in 0..strObj.str.len - 1:
|
||||
result.add(strObj.str[i])
|
||||
|
||||
|
||||
proc asInt*(n: int): ptr Integer =
|
||||
## Converts a nim int into a JAPL int
|
||||
result = allocateObj(Integer, ObjectType.Integer)
|
||||
result.intValue = n
|
||||
|
||||
|
||||
proc asFloat*(n: float): ptr Float =
|
||||
## Converts a nim float into a JAPL float
|
||||
result = allocateObj(Float, ObjectType.Float)
|
||||
result.floatValue = n
|
||||
|
||||
|
||||
proc asBool*(b: bool): ptr Bool =
|
||||
## Converts a nim bool into a JAPL bool
|
||||
result = allocateObj(Bool, ObjectType.Bool)
|
||||
result.boolValue = b
|
||||
|
||||
|
||||
proc asNil*(): ptr Nil =
|
||||
## Creates a nil object
|
||||
result = allocateObj(Nil, ObjectType.Nil)
|
||||
|
||||
|
||||
proc asNan*(): ptr NotANumber =
|
||||
## Creates a nan object
|
||||
result = allocateObj(NotANumber, ObjectType.NotANumber)
|
||||
|
||||
|
||||
proc asInf*(): ptr Infinity =
|
||||
## Creates an inf object
|
||||
result = allocateObj(Infinity, ObjectType.Infinity)
|
||||
|
||||
|
||||
proc newObj*(): ptr Obj =
|
||||
## Allocates a generic JAPL object
|
||||
result = allocateObj(Obj, ObjectType.BaseObject)
|
||||
|
||||
|
||||
## JAPL procs implementations 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 =
|
||||
result = "function"
|
||||
|
||||
proc typeName*(self: ptr String): string =
|
||||
return "string"
|
||||
|
||||
proc typeName*(self: ptr Integer): string =
|
||||
result = "integer"
|
||||
|
||||
proc typeName*(self: ptr Float): string =
|
||||
result = "float"
|
||||
|
||||
proc typeName*(self: ptr Bool): string =
|
||||
result = "bool"
|
||||
|
||||
# Implementations for stringify
|
||||
|
||||
proc stringify*(self: ptr Integer): string =
|
||||
result = $self.intValue
|
||||
|
||||
|
||||
method stringify(self: ptr Float): string =
|
||||
proc stringify*(self: ptr String): string =
|
||||
result = ""
|
||||
for i in 0..<self.len:
|
||||
result = result & (&"{self.str[i]}")
|
||||
|
||||
|
||||
proc stringify*(self: ptr Float): string =
|
||||
result = $self.floatValue
|
||||
|
||||
method stringify*(fn: ptr Function): string =
|
||||
if fn.name != nil:
|
||||
result = "<function " & stringify(fn.name) & ">"
|
||||
|
||||
proc stringify*(self: ptr Bool): string =
|
||||
result = $self.boolValue
|
||||
|
||||
|
||||
proc stringify(self: ptr NotANumber): string =
|
||||
result = "nan"
|
||||
|
||||
|
||||
proc stringify(self: ptr Infinity): string =
|
||||
if self.isNegative:
|
||||
result = "-inf"
|
||||
else:
|
||||
result = "inf"
|
||||
|
||||
proc stringify(self: ptr Nil): string =
|
||||
result = "nil"
|
||||
|
||||
|
||||
proc stringify*(self: ptr JAPLException): string =
|
||||
result = &"{self.errName.stringify()}: {self.message.stringify()}"
|
||||
|
||||
|
||||
proc stringify*(self: ptr Function): string =
|
||||
if self.name != nil:
|
||||
result = &"<function {self.name.stringify()}>"
|
||||
else:
|
||||
result = "<code object>"
|
||||
|
||||
# isFalsey
|
||||
proc stringify*(self: ptr Obj): string =
|
||||
## Returns a string representation of the
|
||||
## given object
|
||||
case self.kind:
|
||||
of ObjectType.BaseObject:
|
||||
result = "<object>"
|
||||
of ObjectType.String:
|
||||
result = cast[ptr String](self).stringify()
|
||||
of ObjectType.Integer:
|
||||
result = cast[ptr Integer](self).stringify()
|
||||
of ObjectType.Float:
|
||||
result = cast[ptr Float](self).stringify()
|
||||
of ObjectType.Bool:
|
||||
result = cast[ptr Bool](self).stringify()
|
||||
of ObjectType.Function:
|
||||
result = cast[ptr Function](self).stringify()
|
||||
of ObjectType.Infinity:
|
||||
result = cast[ptr Infinity](self).stringify()
|
||||
of ObjectType.NotANumber:
|
||||
result = cast[ptr NotANumber](self).stringify()
|
||||
of ObjectType.Nil:
|
||||
result = cast[ptr Nil](self).stringify()
|
||||
else:
|
||||
discard # TODO
|
||||
|
||||
method isFalsey*(self: ptr Obj): bool {.base.} =
|
||||
raise unimplementedException()
|
||||
|
||||
method isFalsey*(self: ptr String): bool {.base.} =
|
||||
# Implementations for isFalsey
|
||||
|
||||
|
||||
proc isFalsey(self: ptr String): bool =
|
||||
result = self.len == 0
|
||||
|
||||
method
|
||||
|
||||
proc isFalsey(self: ptr Function): bool =
|
||||
result = false
|
||||
|
||||
|
||||
# Methods for string objects
|
||||
proc isFalsey(self: ptr Integer): bool =
|
||||
result = self.intValue == 0
|
||||
|
||||
proc hash*(self: ptr String): uint32 =
|
||||
result = 2166136261u32
|
||||
|
||||
proc isFalsey(self: ptr Float): bool =
|
||||
result = self.floatValue == 0.0
|
||||
|
||||
|
||||
proc isFalsey(self: ptr Bool): bool =
|
||||
result = not self.boolValue
|
||||
|
||||
|
||||
proc isFalsey(self: ptr Infinity): bool =
|
||||
result = false
|
||||
|
||||
|
||||
proc isFalsey(self: ptr NotANumber): bool =
|
||||
result = true
|
||||
|
||||
|
||||
proc isFalsey(self: ptr Nil): bool =
|
||||
result = true
|
||||
|
||||
|
||||
proc isFalsey*(self: ptr Obj): bool =
|
||||
## Returns true if the object is
|
||||
## falsey, true otherwise
|
||||
case self.kind:
|
||||
of ObjectType.BaseObject:
|
||||
result = false
|
||||
of ObjectType.String:
|
||||
result = cast[ptr String](self).isFalsey()
|
||||
of ObjectType.Integer:
|
||||
result = cast[ptr Integer](self).isFalsey()
|
||||
of ObjectType.Float:
|
||||
result = cast[ptr Float](self).isFalsey()
|
||||
of ObjectType.Bool:
|
||||
result = cast[ptr Bool](self).isFalsey()
|
||||
of ObjectType.Function:
|
||||
result = cast[ptr Function](self).isFalsey()
|
||||
of ObjectType.Infinity:
|
||||
result = cast[ptr Infinity](self).isFalsey()
|
||||
of ObjectType.NotANumber:
|
||||
result = cast[ptr NotANumber](self).isFalsey()
|
||||
of ObjectType.Nil:
|
||||
result = cast[ptr Nil](self).isFalsey()
|
||||
else:
|
||||
discard # TODO
|
||||
|
||||
# Implementation for hash
|
||||
|
||||
proc hash(self: ptr String): uint64 =
|
||||
result = 2166136261u
|
||||
var i = 0
|
||||
while i < self.len:
|
||||
result = result xor uint32 self.str[i]
|
||||
result = result xor uint64 self.str[i]
|
||||
result *= 16777619
|
||||
i += 1
|
||||
return result
|
||||
|
||||
proc hash(self: ptr Float): uint64 =
|
||||
result = 2166136261u xor uint64 self.floatValue # TODO: Improve this
|
||||
result *= 16777619
|
||||
|
||||
proc eq*(a: ptr String, b: ptr String): bool =
|
||||
if a.len != b.len:
|
||||
|
||||
proc hash(self: ptr Infinity): uint64 =
|
||||
# TODO: Arbitrary hash seems a bad idea
|
||||
if self.isNegative:
|
||||
result = 1u
|
||||
else:
|
||||
result = 0u
|
||||
|
||||
|
||||
proc hash(self: ptr NotANumber): uint64 =
|
||||
# TODO: Hashable?
|
||||
raise newException(NotImplementedError, "unhashable type 'nan'")
|
||||
|
||||
|
||||
proc hash(self: ptr Nil): uint64 =
|
||||
# TODO: Arbitrary hash seems a bad idea
|
||||
result = 2u
|
||||
|
||||
proc hash(self: ptr Function): uint64 =
|
||||
# TODO: Hashable?
|
||||
raise newException(NotImplementedError, "unhashable type 'function'")
|
||||
|
||||
|
||||
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
|
||||
case self.kind:
|
||||
of ObjectType.BaseObject:
|
||||
result = 2166136261u # Constant hash
|
||||
of ObjectType.String:
|
||||
result = cast[ptr String](self).hash()
|
||||
of ObjectType.Integer:
|
||||
result = cast[ptr Integer](self).hash()
|
||||
of ObjectType.Float:
|
||||
result = cast[ptr Float](self).hash()
|
||||
of ObjectType.Bool:
|
||||
result = cast[ptr Bool](self).hash()
|
||||
of ObjectType.Function:
|
||||
result = cast[ptr Function](self).hash()
|
||||
of ObjectType.Infinity:
|
||||
result = cast[ptr Infinity](self).hash()
|
||||
of ObjectType.NotANumber:
|
||||
result = cast[ptr NotANumber](self).hash()
|
||||
of ObjectType.Nil:
|
||||
result = cast[ptr Nil](self).hash()
|
||||
else:
|
||||
discard # TODO
|
||||
|
||||
|
||||
# Implementation for eq
|
||||
|
||||
proc eq(self, other: ptr String): bool =
|
||||
if self.len != other.len:
|
||||
return false
|
||||
elif a.hash != b.hash:
|
||||
elif self.hash != other.hash:
|
||||
return false
|
||||
for i in 0..a.len - 1:
|
||||
if a.str[i] != b.str[i]:
|
||||
for i in 0..self.len - 1:
|
||||
if self.str[i] != other.str[i]:
|
||||
return false
|
||||
return true
|
||||
result = true
|
||||
|
||||
|
||||
proc eq(self, other: ptr Integer): bool =
|
||||
result = self.intValue == other.intValue
|
||||
|
||||
|
||||
proc eq(self, other: ptr Float): bool =
|
||||
result = self.floatValue == other.floatValue
|
||||
|
||||
|
||||
proc eq(self, other: ptr Bool): bool =
|
||||
result = self.boolValue == other.boolValue
|
||||
|
||||
|
||||
proc eq(self, other: ptr Function): bool =
|
||||
result = self.name.stringify() == other.name.stringify()
|
||||
|
||||
|
||||
proc eq(self, other: ptr NotANumber): bool =
|
||||
result = false
|
||||
|
||||
|
||||
proc eq(self, other: ptr Nil): bool =
|
||||
result = true
|
||||
|
||||
|
||||
proc eq(self, other: ptr Infinity): bool =
|
||||
result = self.isNegative == other.isNegative
|
||||
|
||||
|
||||
proc eq*(self, other: ptr Obj): bool =
|
||||
## Compares two objects for equality,
|
||||
## returns true if self equals other
|
||||
## and false otherwise
|
||||
if self.kind != other.kind:
|
||||
return false
|
||||
case self.kind:
|
||||
of ObjectType.BaseObject:
|
||||
result = other.kind == ObjectType.BaseObject
|
||||
of ObjectType.String:
|
||||
var self = cast[ptr String](self)
|
||||
var other = cast[ptr String](other)
|
||||
result = self.eq(other)
|
||||
of ObjectType.Integer:
|
||||
var self = cast[ptr Integer](self)
|
||||
var other = cast[ptr Integer](other)
|
||||
result = self.eq(other)
|
||||
of ObjectType.Float:
|
||||
var self = cast[ptr Float](self)
|
||||
var other = cast[ptr Float](other)
|
||||
result = self.eq(other)
|
||||
of ObjectType.Bool:
|
||||
var self = cast[ptr Bool](self)
|
||||
var other = cast[ptr Bool](other)
|
||||
result = self.eq(other)
|
||||
of ObjectType.Function:
|
||||
var self = cast[ptr Function](self)
|
||||
var other = cast[ptr Function](other)
|
||||
result = self.eq(other)
|
||||
else:
|
||||
discard # TODO
|
||||
|
||||
|
||||
## String constructors and converters
|
||||
|
||||
proc newString*(str: string): ptr String =
|
||||
# TODO -> Unicode
|
||||
result = allocateObj(String, ObjectType.String)
|
||||
|
@ -154,25 +513,35 @@ proc newString*(str: string): ptr String =
|
|||
result.len = len(str)
|
||||
result.hashValue = result.hash()
|
||||
|
||||
|
||||
proc typeName*(s: ptr String): string =
|
||||
return "string"
|
||||
|
||||
|
||||
proc asStr*(s: string): ptr Obj =
|
||||
## Creates a string object
|
||||
## Converts a nim string into a
|
||||
## JAPL string
|
||||
result = newString(s)
|
||||
|
||||
# End of string object methods
|
||||
# End of string object procs
|
||||
|
||||
# Function object methods
|
||||
|
||||
# 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)
|
||||
|
@ -182,33 +551,6 @@ proc newFunction*(name: string = "", chunk: Chunk = newChunk(), arity: int = 0):
|
|||
result.chunk = chunk
|
||||
|
||||
|
||||
proc isFalsey*(fn: ptr Function): bool =
|
||||
return false
|
||||
|
||||
|
||||
proc valuesEqual*(a, b: ptr Function): bool =
|
||||
result = a.name.stringify == b.name.stringify
|
||||
|
||||
|
||||
proc typeName*(self: ptr Function): string =
|
||||
result = "function"
|
||||
|
||||
|
||||
## Generic base methods
|
||||
|
||||
proc isFalsey*(obj: ptr Obj): bool =
|
||||
## Returns true if the given
|
||||
## object is falsey
|
||||
|
||||
result = false
|
||||
|
||||
|
||||
proc typeName*(obj: ptr Obj): string =
|
||||
## This method should return the
|
||||
## name of the object type
|
||||
result = "object"
|
||||
|
||||
|
||||
proc bool*(obj: ptr Obj): bool =
|
||||
## Returns wheter the object should
|
||||
## be considered a falsey obj
|
||||
|
@ -218,16 +560,6 @@ proc bool*(obj: ptr Obj): bool =
|
|||
result = false
|
||||
|
||||
|
||||
proc eq*(a: ptr Obj, b: ptr Obj): bool =
|
||||
## Compares two objects for equality
|
||||
result = a.kind == b.kind
|
||||
|
||||
|
||||
proc hash*(self: ptr Obj): uint32 =
|
||||
# TODO: Make this actually useful
|
||||
result = 2166136261u32
|
||||
|
||||
|
||||
proc add(self, other: ptr Obj): ptr Obj =
|
||||
## Returns the result of self + other
|
||||
## or nil if the operation is unsupported
|
||||
|
@ -281,113 +613,6 @@ proc binaryXor(self, other: ptr Obj): ptr Obj =
|
|||
## or nil if the operation is unsupported
|
||||
result = nil
|
||||
|
||||
|
||||
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.Nan
|
||||
|
||||
|
||||
proc isNum*(obj: ptr Obj): bool =
|
||||
## Returns true if the given obj is
|
||||
## either a JAPL number, nan or inf
|
||||
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 toBool*(obj: ptr Obj): bool =
|
||||
## Converts a JAPL bool to a nim bool
|
||||
result = cast[ptr Bool](obj).boolValue
|
||||
|
||||
|
||||
proc toInt*(obj: ptr Obj): int =
|
||||
## Converts a JAPL int to a nim int
|
||||
result = cast[ptr Integer](obj).intValue
|
||||
|
||||
|
||||
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)
|
||||
for i in 0..strObj.str.len - 1:
|
||||
result.add(strObj.str[i])
|
||||
|
||||
|
||||
proc asInt*(n: int): ptr Integer =
|
||||
## Creates an int object
|
||||
result = allocateObj(Integer, ObjectType.Integer)
|
||||
result.intValue = n
|
||||
|
||||
|
||||
proc asFloat*(n: float): ptr Float =
|
||||
## Creates a float object (double)
|
||||
result = allocateObj(Float, ObjectType.Float)
|
||||
result.floatValue = n
|
||||
|
||||
|
||||
proc asBool*(b: bool): ptr Bool =
|
||||
## Creates a boolean object
|
||||
result = allocateObj(Bool, ObjectType.Bool)
|
||||
result.boolValue = b
|
||||
|
||||
|
||||
proc asNil*(): ptr Nil =
|
||||
## Creates a nil object
|
||||
result = allocateObj(Nil, ObjectType.Nil)
|
||||
|
||||
|
||||
proc asNan*(): ptr NotANumber =
|
||||
## Creates a nil object
|
||||
result = allocateObj(NotANumber, ObjectType.Nan)
|
||||
|
||||
|
||||
proc asInf*(): ptr Infinity =
|
||||
## Creates a nil object
|
||||
result = allocateObj(Infinity, ObjectType.Infinity)
|
||||
|
||||
|
||||
proc asObj*(obj: ptr Obj): ptr Obj =
|
||||
## Creates a generic JAPL object
|
||||
result = allocateObj(Obj, ObjectType.BaseObject)
|
||||
|
||||
proc newIndexError*(message: string): ptr JAPLException =
|
||||
result = allocateObj(JAPLException, ObjectType.Exception)
|
||||
result.errName = newString("IndexError")
|
||||
|
|
|
@ -334,7 +334,7 @@ proc run(self: var VM, repl: bool): InterpretResult =
|
|||
of OpCode.Not:
|
||||
self.push(self.pop().isFalsey().asBool())
|
||||
of OpCode.Equal:
|
||||
discard
|
||||
self.push(self.pop().eq(self.pop()).asBool())
|
||||
of OpCode.Less:
|
||||
discard
|
||||
of OpCode.Greater:
|
||||
|
|
Loading…
Reference in New Issue