Added comparison operators and logical not

This commit is contained in:
nocturn9x 2020-08-13 23:39:26 +02:00
parent 8ed20c0275
commit cc1b41e7ed
9 changed files with 177 additions and 69 deletions

View File

@ -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

View File

@ -2,6 +2,7 @@ import tables
import meta/tokentype
import meta/tokenobject
import meta/valueobject
import types/objecttype
import system
import strutils
import strformat

View File

@ -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]

View File

@ -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"))

View File

@ -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)

View File

@ -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))

36
nim/types/objecttype.nim Normal file
View File

@ -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

View File

@ -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

View File

@ -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