mirror of https://github.com/japl-lang/japl.git
Added comparison operators and logical not
This commit is contained in:
parent
8ed20c0275
commit
cc1b41e7ed
|
@ -151,6 +151,18 @@ proc binary(self: Compiler) =
|
|||
self.emitByte(OP_MOD)
|
||||
of POW:
|
||||
self.emitByte(OP_POW)
|
||||
of NE:
|
||||
self.emitBytes(OP_EQUAL, OP_NOT)
|
||||
of DEQ:
|
||||
self.emitByte(OP_EQUAL)
|
||||
of GT:
|
||||
self.emitByte(OP_GREATER)
|
||||
of GE:
|
||||
self.emitBytes(OP_LESS, OP_NOT)
|
||||
of LT:
|
||||
self.emitByte(OP_LESS)
|
||||
of LE:
|
||||
self.emitBytes(OP_GREATER, OP_NOT)
|
||||
else:
|
||||
return
|
||||
|
||||
|
@ -165,6 +177,8 @@ proc unary(self: Compiler) =
|
|||
case operator:
|
||||
of MINUS:
|
||||
self.emitByte(OP_NEGATE)
|
||||
of NEG:
|
||||
self.emitByte(OP_NOT)
|
||||
else:
|
||||
return
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import tables
|
|||
import meta/tokentype
|
||||
import meta/tokenobject
|
||||
import meta/valueobject
|
||||
import types/objecttype
|
||||
import system
|
||||
import strutils
|
||||
import strformat
|
||||
|
|
|
@ -15,7 +15,11 @@ type
|
|||
OP_MOD,
|
||||
OP_NIL,
|
||||
OP_TRUE,
|
||||
OP_FALSE
|
||||
OP_FALSE,
|
||||
OP_GREATER,
|
||||
OP_LESS,
|
||||
OP_EQUAL,
|
||||
OP_NOT
|
||||
Chunk* = ref object
|
||||
consts*: ValueArray
|
||||
code*: seq[uint8]
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
# Errors for JAPL
|
||||
|
||||
import valueobject
|
||||
|
||||
type
|
||||
JAPLException* = ref object of Obj
|
||||
name*: Obj
|
||||
|
||||
|
||||
proc newTypeError*(): JAPLException =
|
||||
result = JAPLException(name: Obj(kind: ObjectTypes.STRING, str: "TypeError"))
|
||||
|
||||
|
|
@ -1,15 +1,10 @@
|
|||
# Value objects and types representation
|
||||
import ../types/objecttype
|
||||
|
||||
|
||||
type
|
||||
ValueTypes* = enum
|
||||
INTEGER, DOUBLE, BOOL, NIL, OBJECT
|
||||
ObjectTypes* = enum
|
||||
STRING,
|
||||
Obj* = ref object of RootObj
|
||||
case kind*: ObjectTypes
|
||||
of STRING:
|
||||
str*: string
|
||||
Value* = ref object
|
||||
case kind*: ValueTypes
|
||||
of INTEGER:
|
||||
|
@ -34,23 +29,77 @@ proc writeValueArray*(arr: var ValueArray, value: Value) =
|
|||
arr.values.add(value)
|
||||
|
||||
|
||||
proc stringifyObject*(obj: Obj): string =
|
||||
case obj.kind:
|
||||
of STRING:
|
||||
return obj.str
|
||||
proc isNil*(value: Value): bool =
|
||||
return value.kind == NIL
|
||||
|
||||
|
||||
proc stringifyValue*(value: Value): string =
|
||||
proc isBool*(value: Value): bool =
|
||||
return value.kind == BOOL
|
||||
|
||||
|
||||
proc isInt*(value: Value): bool =
|
||||
return value.kind == INTEGER
|
||||
|
||||
|
||||
proc isFloat*(value: Value): bool =
|
||||
return value.kind == DOUBLE
|
||||
|
||||
|
||||
proc isNum*(value: Value): bool =
|
||||
return isInt(value) or isFloat(value)
|
||||
|
||||
|
||||
proc toBool*(value: Value): bool =
|
||||
return value.boolValue
|
||||
|
||||
|
||||
proc toInt*(value: Value): int =
|
||||
return value.intValue
|
||||
|
||||
|
||||
proc toFloat*(value: Value): float =
|
||||
return value.floatValue
|
||||
|
||||
|
||||
proc stringify*(value: Value): string =
|
||||
case value.kind:
|
||||
of INTEGER:
|
||||
result = $value.intValue
|
||||
result = $value.toInt()
|
||||
of DOUBLE:
|
||||
result = $value.floatValue
|
||||
result = $value.toFloat()
|
||||
of BOOL:
|
||||
result = $value.boolValue
|
||||
result = $value.toBool()
|
||||
of NIL:
|
||||
result = "nil"
|
||||
of OBJECT:
|
||||
result = stringifyObject(value.obj)
|
||||
result = stringify(value.obj)
|
||||
|
||||
|
||||
proc isFalsey*(value: Value): bool =
|
||||
case value.kind:
|
||||
of BOOL:
|
||||
return not value.toBool()
|
||||
of OBJECT:
|
||||
return isFalsey(value.obj)
|
||||
of INTEGER:
|
||||
return value.toInt() > 0
|
||||
of DOUBLE:
|
||||
return value.toFloat() > 0.0
|
||||
of NIL:
|
||||
return true
|
||||
|
||||
|
||||
proc valuesEqual*(a: Value, b: Value): bool =
|
||||
if a.kind != b.kind:
|
||||
return false
|
||||
case a.kind:
|
||||
of BOOL:
|
||||
return a.toBool() == b.toBool()
|
||||
of NIL:
|
||||
return true
|
||||
of INTEGER:
|
||||
return a.toInt() == b.toInt()
|
||||
of DOUBLE:
|
||||
return a.toFloat() == b.toFloat()
|
||||
of OBJECT:
|
||||
return valuesEqual(a.obj, b.obj)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import ../meta/valueobject
|
||||
import objecttype
|
||||
|
||||
type JAPLException* = ref object of Obj
|
||||
|
||||
|
||||
func newTypeError*(): Obj =
|
||||
result = Obj(kind: ObjectTypes.EXCEPTION, errName: Obj(kind: STRING, str: "TypeError"))
|
||||
|
||||
func newTypeError*(message: string): JAPLException =
|
||||
result = JAPLException(kind: ObjectTypes.EXCEPTION, errName: Obj(kind: STRING, str: "TypeError"), message: Obj(kind: ObjectTypes.STRING, str: message))
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
type
|
||||
ObjectTypes* = enum
|
||||
STRING, EXCEPTION
|
||||
Obj* = ref object of RootObj
|
||||
case kind*: ObjectTypes
|
||||
of STRING:
|
||||
str*: string
|
||||
of EXCEPTION:
|
||||
errName*: Obj
|
||||
message*: Obj
|
||||
|
||||
|
||||
proc isFalsey*(obj: Obj): bool =
|
||||
case obj.kind:
|
||||
of STRING:
|
||||
return len(obj.str) > 0
|
||||
else:
|
||||
return false
|
||||
|
||||
|
||||
proc stringify*(obj: Obj): string =
|
||||
case obj.kind:
|
||||
of STRING:
|
||||
return obj.str
|
||||
of ObjectTypes.EXCEPTION:
|
||||
return stringify(obj.message)
|
||||
|
||||
|
||||
proc valuesEqual*(a: Obj, b: Obj): bool =
|
||||
if a.kind != b.kind:
|
||||
return false
|
||||
case a.kind:
|
||||
of STRING:
|
||||
return a.str == b.str
|
||||
of EXCEPTION:
|
||||
return a.errName == b.errName and a.message == b.message
|
|
@ -3,6 +3,7 @@ import ../meta/valueobject
|
|||
import strformat
|
||||
|
||||
|
||||
|
||||
proc simpleInstruction(name: string, index: int): int =
|
||||
echo &"\tOpCode at offset: {name}\n"
|
||||
return index + 1
|
||||
|
@ -15,7 +16,7 @@ proc constantLongInstruction(name: string, chunk: Chunk, offset: int): int =
|
|||
copyMem(constant.addr, unsafeAddr(constantArray), sizeof(constantArray))
|
||||
echo &"\tOpCode at offset: {name}, points to {constant}"
|
||||
let obj = chunk.consts.values[constant]
|
||||
echo &"\tValue: {stringifyValue(obj)}\n\tKind: {obj.kind}\n"
|
||||
echo &"\tValue: {stringify(obj)}\n\tKind: {obj.kind}\n"
|
||||
return offset + 4
|
||||
|
||||
|
||||
|
@ -23,7 +24,7 @@ proc constantInstruction(name: string, chunk: Chunk, offset: int): int =
|
|||
var constant = chunk.code[offset + 1]
|
||||
echo &"\tOpCode at offset: {name}, points to index {constant}"
|
||||
let obj = chunk.consts.values[constant]
|
||||
echo &"\tValue: {stringifyValue(obj)}\n\tKind: {obj.kind}\n"
|
||||
echo &"\tValue: {stringify(obj)}\n\tKind: {obj.kind}\n"
|
||||
return offset + 2
|
||||
|
||||
|
||||
|
|
84
nim/vm.nim
84
nim/vm.nim
|
@ -1,6 +1,7 @@
|
|||
import meta/chunk
|
||||
import meta/valueobject
|
||||
import meta/exceptions
|
||||
import types/exceptions
|
||||
import types/objecttype
|
||||
import util/debug
|
||||
import compiler
|
||||
import strutils
|
||||
|
@ -27,15 +28,12 @@ type VM = ref object
|
|||
stack*: seq[Value]
|
||||
stackTop*: int
|
||||
|
||||
proc error*(self: VM, message: string, kind: JAPLException) =
|
||||
echo &"{stringifyObject(kind.name)}: {message}"
|
||||
|
||||
proc error*(self: VM, kind: JAPLException) =
|
||||
echo &"{stringify(kind.errName)}: {stringify(kind.message)}"
|
||||
# Add code to raise an exception here
|
||||
|
||||
|
||||
proc numOperand(operand: Value): bool =
|
||||
result = operand.kind in [DOUBLE, INTEGER]
|
||||
|
||||
|
||||
proc pop*(self: VM): Value =
|
||||
result = self.stack.pop()
|
||||
self.stackTop = self.stackTop - 1
|
||||
|
@ -58,31 +56,37 @@ proc run(self: VM, debug: bool): InterpretResult =
|
|||
copyMem(idx.addr, unsafeAddr(arr), sizeof(arr))
|
||||
self.chunk.consts.values[idx]
|
||||
template BinOp(op, check) =
|
||||
var leftVal {.inject.} = self.pop()
|
||||
var rightVal {.inject.} = self.pop()
|
||||
var leftVal {.inject.} = self.pop()
|
||||
if check(leftVal) and check(rightVal):
|
||||
if leftVal.kind == INTEGER and rightVal.kind == INTEGER:
|
||||
var left: int = leftVal.intValue
|
||||
var right: int = rightVal.intValue
|
||||
var res = `op`(right, left)
|
||||
if res is int:
|
||||
self.push(Value(kind: INTEGER, intValue: int res))
|
||||
if leftVal.isFloat() and rightVal.isInt():
|
||||
var res = `op`(leftVal.toFloat(), float rightVal.toInt())
|
||||
if res is bool:
|
||||
self.push(Value(kind: BOOL, boolValue: bool res))
|
||||
else:
|
||||
self.push(Value(kind: DOUBLE, floatValue: float res))
|
||||
elif leftVal.kind == DOUBLE and rightVal.kind == INTEGER:
|
||||
var left = leftVal.floatValue
|
||||
var right = float rightVal.intValue
|
||||
self.push(Value(kind: DOUBLE, floatValue: `op`(right, left)))
|
||||
elif leftVal.kind == INTEGER and rightVal.kind == DOUBLE:
|
||||
var left = float leftVal.intValue
|
||||
var right = rightVal.floatValue
|
||||
self.push(Value(kind: DOUBLE, floatValue: `op`(right, left)))
|
||||
self.push(Value(kind: DOUBLE, floatValue: float res))
|
||||
elif leftVal.isInt() and rightVal.isFloat():
|
||||
var res = `op`(float leftVal.toInt(), rightVal.toFloat())
|
||||
if res is bool:
|
||||
self.push(Value(kind: BOOL, boolValue: bool res))
|
||||
else:
|
||||
self.push(Value(kind: DOUBLE, floatValue: float res))
|
||||
elif leftVal.isFloat() and rightVal.isFloat():
|
||||
var res = `op`(leftVal.toFloat(), rightVal.toFloat())
|
||||
if res is bool:
|
||||
self.push(Value(kind: BOOL, boolValue: bool res))
|
||||
else:
|
||||
self.push(Value(kind: DOUBLE, floatValue: float res))
|
||||
else:
|
||||
var left = leftVal.floatValue
|
||||
var right = rightVal.floatValue
|
||||
self.push(Value(kind: DOUBLE, floatValue: `op`(right, left)))
|
||||
var tmp = `op`(leftVal.toInt(), rightVal.toInt())
|
||||
if tmp is int:
|
||||
self.push(Value(kind: INTEGER, intValue: int tmp))
|
||||
elif tmp is bool:
|
||||
self.push(Value(kind: BOOL, boolValue: bool tmp))
|
||||
else:
|
||||
self.push(Value(kind: DOUBLE, floatValue: float tmp))
|
||||
else:
|
||||
self.error(&"Unsupported binary operand for objects of type '{toLowerAscii($(leftVal.kind))}' and '{toLowerAscii($(rightVal.kind))}'", newTypeError())
|
||||
self.error(newTypeError(&"Unsupported binary operand for objects of type '{toLowerAscii($(leftVal.kind))}' and '{toLowerAscii($(rightVal.kind))}'"))
|
||||
return RUNTIME_ERROR
|
||||
var instruction: uint8
|
||||
var opcode: OpCode
|
||||
|
@ -93,7 +97,7 @@ proc run(self: VM, debug: bool): InterpretResult =
|
|||
if debug:
|
||||
stdout.write("Current stack status: [")
|
||||
for v in self.stack:
|
||||
stdout.write(stringifyValue(v))
|
||||
stdout.write(stringify(v))
|
||||
stdout.write(", ")
|
||||
stdout.write("]\n")
|
||||
discard disassembleInstruction(self.chunk, self.ip - 1)
|
||||
|
@ -117,25 +121,35 @@ proc run(self: VM, debug: bool): InterpretResult =
|
|||
echo &"Unsupported unary operator '-' for object of type '{toLowerAscii($cur.kind)}'"
|
||||
return RUNTIME_ERROR
|
||||
of OP_ADD:
|
||||
BinOp(`+`, numOperand)
|
||||
BinOp(`+`, isNum)
|
||||
of OP_SUBTRACT:
|
||||
BinOp(`-`, numOperand)
|
||||
BinOp(`-`, isNum)
|
||||
of OP_DIVIDE:
|
||||
BinOp(`/`, numOperand)
|
||||
BinOp(`/`, isNum)
|
||||
of OP_MULTIPLY:
|
||||
BinOp(`*`, numOperand)
|
||||
BinOp(`*`, isNum)
|
||||
of OP_MOD:
|
||||
BinOp(floorMod, numOperand)
|
||||
BinOp(floorMod, isNum)
|
||||
of OP_POW:
|
||||
BinOp(`**`, numOperand)
|
||||
BinOp(`**`, isNum)
|
||||
of OP_TRUE:
|
||||
self.push(Value(kind: BOOL, boolValue: true))
|
||||
of OP_FALSE:
|
||||
self.push(Value(kind: BOOL, boolValue: false))
|
||||
of OP_NIL:
|
||||
self.push(Value(kind: NIL))
|
||||
of OP_NOT:
|
||||
self.push(Value(kind: BOOL, boolValue: isFalsey(self.pop())))
|
||||
of OP_EQUAL:
|
||||
var a = self.pop()
|
||||
var b = self.pop()
|
||||
self.push(Value(kind: BOOL, boolValue: valuesEqual(a, b)))
|
||||
of OP_LESS:
|
||||
BinOp(`<`, isNum)
|
||||
of OP_GREATER:
|
||||
BinOp(`>`, isNum)
|
||||
of OP_RETURN:
|
||||
echo stringifyValue(self.pop())
|
||||
echo stringify(self.pop())
|
||||
return OK
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue