From cc1b41e7edf2186500640dd32caab779b8adb756 Mon Sep 17 00:00:00 2001 From: nocturn9x Date: Thu, 13 Aug 2020 23:39:26 +0200 Subject: [PATCH] Added comparison operators and logical not --- nim/compiler.nim | 14 +++++++ nim/lexer.nim | 1 + nim/meta/chunk.nim | 6 ++- nim/meta/exceptions.nim | 13 ------- nim/meta/valueobject.nim | 79 ++++++++++++++++++++++++++++++------- nim/types/exceptions.nim | 8 ++-- nim/types/objecttype.nim | 36 +++++++++++++++++ nim/util/debug.nim | 5 ++- nim/vm.nim | 84 +++++++++++++++++++++++----------------- 9 files changed, 177 insertions(+), 69 deletions(-) delete mode 100644 nim/meta/exceptions.nim create mode 100644 nim/types/objecttype.nim diff --git a/nim/compiler.nim b/nim/compiler.nim index 755bbec..d4c9837 100644 --- a/nim/compiler.nim +++ b/nim/compiler.nim @@ -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 diff --git a/nim/lexer.nim b/nim/lexer.nim index 945b848..0b911d0 100644 --- a/nim/lexer.nim +++ b/nim/lexer.nim @@ -2,6 +2,7 @@ import tables import meta/tokentype import meta/tokenobject import meta/valueobject +import types/objecttype import system import strutils import strformat diff --git a/nim/meta/chunk.nim b/nim/meta/chunk.nim index 4de7754..8ab9bac 100644 --- a/nim/meta/chunk.nim +++ b/nim/meta/chunk.nim @@ -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] diff --git a/nim/meta/exceptions.nim b/nim/meta/exceptions.nim deleted file mode 100644 index ad2b749..0000000 --- a/nim/meta/exceptions.nim +++ /dev/null @@ -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")) - - diff --git a/nim/meta/valueobject.nim b/nim/meta/valueobject.nim index 5702b5f..990df6c 100644 --- a/nim/meta/valueobject.nim +++ b/nim/meta/valueobject.nim @@ -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) diff --git a/nim/types/exceptions.nim b/nim/types/exceptions.nim index 18b6631..d30df61 100644 --- a/nim/types/exceptions.nim +++ b/nim/types/exceptions.nim @@ -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)) diff --git a/nim/types/objecttype.nim b/nim/types/objecttype.nim new file mode 100644 index 0000000..5e2d240 --- /dev/null +++ b/nim/types/objecttype.nim @@ -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 diff --git a/nim/util/debug.nim b/nim/util/debug.nim index e78235a..c6b55b3 100644 --- a/nim/util/debug.nim +++ b/nim/util/debug.nim @@ -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 diff --git a/nim/vm.nim b/nim/vm.nim index 83ffe3f..7efa993 100644 --- a/nim/vm.nim +++ b/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