mirror of https://github.com/japl-lang/japl.git
333 lines
9.2 KiB
Nim
333 lines
9.2 KiB
Nim
# 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.
|
|
|
|
## Base structure for objects in JAPL, all
|
|
## types inherit from this simple structure
|
|
|
|
import ../memory
|
|
|
|
|
|
type
|
|
Chunk* = ref object
|
|
## 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*: seq[ptr Obj]
|
|
code*: seq[uint8]
|
|
lines*: seq[int]
|
|
ObjectType* {.pure.} = enum
|
|
## All the possible object types
|
|
String, Exception, Function,
|
|
Class, Module, BaseObject,
|
|
Integer, Float, Bool, Nan,
|
|
Infinity, Nil
|
|
Obj* = object of RootObj
|
|
# The object that rules them all
|
|
kind*: ObjectType
|
|
hashValue*: uint32
|
|
String* = object of Obj # A string object
|
|
str*: ptr UncheckedArray[char] # TODO -> Unicode support
|
|
len*: int
|
|
Integer* = object of Obj
|
|
# An integer object
|
|
intValue: int # TODO: Bignum arithmetic
|
|
Bool* = object of Integer
|
|
boolValue: bool # If the boolean is true or false
|
|
Nil* = object of Bool
|
|
Float* = object of Integer
|
|
# A float object
|
|
floatValue: float
|
|
Infinity* = object of Float # Inf is considered a float
|
|
isNegative: bool # This differentiates inf and -inf
|
|
NotANumber* = object of Float # NaN is as well (IEEE 754)
|
|
Function* = object of Obj
|
|
name*: ptr String
|
|
arity*: int
|
|
optionals*: int
|
|
defaults*: seq[ptr Obj]
|
|
chunk*: Chunk
|
|
JAPLException* = object of Obj
|
|
errName*: ptr String
|
|
message*: ptr String
|
|
|
|
|
|
proc `convert`*(a: ptr Obj): ptr Obj =
|
|
## Performs conversions from a JAPL
|
|
## supertype to a subtype
|
|
|
|
case a.kind:
|
|
of ObjectType.String:
|
|
result = cast[ptr String](a)
|
|
of ObjectType.Function:
|
|
result = cast[ptr Function](a)
|
|
of ObjectType.Integer:
|
|
result = cast[ptr Integer](a)
|
|
of ObjectType.Float:
|
|
result = cast[ptr Float](a)
|
|
of ObjectType.Bool:
|
|
result = cast[ptr Bool](a)
|
|
of ObjectType.Nan:
|
|
result = cast[ptr NotANumber](a)
|
|
of ObjectType.Infinity:
|
|
result = cast[ptr Infinity](a)
|
|
of ObjectType.BaseObject:
|
|
result = cast[ptr Obj](a)
|
|
of ObjectType.Class, ObjectType.Module:
|
|
discard # TODO: Implement
|
|
else:
|
|
raise newException(Exception, "Attempted JAPL type conversion with unknown source object")
|
|
|
|
|
|
|
|
proc allocateObject*(size: int, kind: ObjectType): ptr Obj =
|
|
## Wrapper around reallocate to create a new generic JAPL object
|
|
result = cast[ptr Obj](reallocate(nil, 0, size))
|
|
result.kind = kind
|
|
|
|
|
|
template allocateObj*(kind: untyped, objType: ObjectType): untyped =
|
|
## Wrapper around allocateObject to cast a generic object
|
|
## to a more specific type
|
|
cast[ptr kind](allocateObject(sizeof kind, objType))
|
|
|
|
|
|
proc objType*(obj: ptr Obj): ObjectType =
|
|
## Returns the type of the object
|
|
return obj.kind
|
|
|
|
|
|
proc stringify*(obj: ptr Obj): string =
|
|
## Returns a string representation
|
|
## of the object
|
|
if obj.kind != ObjectType.BaseObject: # NOTE: Consider how to reduce the boilerplate
|
|
var newObj = convert obj
|
|
result = newObj.stringify()
|
|
else:
|
|
result = "<object (built-in type)>"
|
|
|
|
|
|
proc isFalsey*(obj: ptr Obj): bool =
|
|
## Returns true if the given
|
|
## object is falsey
|
|
if obj.kind != ObjectType.BaseObject: # NOTE: Consider how to reduce the boilerplate
|
|
var newObj = convert obj
|
|
result = newObj.isFalsey()
|
|
else:
|
|
result = false
|
|
|
|
|
|
proc typeName*(obj: ptr Obj): string =
|
|
## This method should return the
|
|
## name of the object type
|
|
if obj.kind != ObjectType.BaseObject:
|
|
var newObj = convert obj
|
|
result = newObj.typeName()
|
|
else:
|
|
result = "object"
|
|
|
|
|
|
|
|
proc bool*(obj: ptr Obj): bool =
|
|
## Returns wheter the object should
|
|
## be considered a falsey obj
|
|
## or not. Returns true if the
|
|
## object is truthy, or false
|
|
## if it is falsey
|
|
if obj.kind != ObjectType.BaseObject:
|
|
var newObj = convert obj
|
|
result = newObj.bool()
|
|
else:
|
|
result = false
|
|
|
|
|
|
proc eq*(a: ptr Obj, b: ptr Obj): bool =
|
|
## Compares two objects for equality
|
|
if a.kind != ObjectType.BaseObject:
|
|
var newObj = convert(a)
|
|
result = newObj.eq(b)
|
|
else:
|
|
result = a.kind == b.kind
|
|
|
|
|
|
proc hash*(self: ptr Obj): uint32 =
|
|
# TODO: Make this actually useful
|
|
if self.kind == ObjectType.BaseObject:
|
|
result = 2166136261u32
|
|
else:
|
|
result = convert(self).hash()
|
|
|
|
|
|
proc add(self, other: ptr Obj): ptr Obj =
|
|
## Returns the result of self + other
|
|
## or nil if the operation is unsupported
|
|
result = nil # Not defined for base objects!
|
|
|
|
|
|
proc sub(self, other: ptr Obj): ptr Obj =
|
|
## Returns the result of self - other
|
|
## or nil if the operation is unsupported
|
|
result = nil
|
|
|
|
|
|
proc mul(self, other: ptr Obj): ptr Obj =
|
|
## Returns the result of self * other
|
|
## or nil if the operation is unsupported
|
|
result = nil
|
|
|
|
|
|
proc trueDiv(self, other: ptr Obj): ptr Obj =
|
|
## Returns the result of self / other
|
|
## or nil if the operation is unsupported
|
|
result = nil
|
|
|
|
|
|
proc exp(self, other: ptr Obj): ptr Obj =
|
|
## Returns the result of self ** other
|
|
## or nil if the operation is unsupported
|
|
result = nil
|
|
|
|
|
|
proc binaryAnd(self, other: ptr Obj): ptr Obj =
|
|
## Returns the result of self & other
|
|
## or nil if the operation is unsupported
|
|
result = nil
|
|
|
|
|
|
proc binaryOr(self, other: ptr Obj): ptr Obj =
|
|
## Returns the result of self | other
|
|
## or nil if the operation is unsupported
|
|
result = nil
|
|
|
|
|
|
proc binaryNot(self: ptr Obj): ptr Obj =
|
|
## Returns the result of ~self
|
|
## or nil if the operation is unsupported
|
|
result = nil
|
|
|
|
|
|
proc binaryXor(self, other: ptr Obj): ptr Obj =
|
|
## Returns the result of self ^ other
|
|
## or nil if the operation is unsupported
|
|
result = nil
|
|
|
|
|
|
proc 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)
|
|
|
|
|