diff --git a/build.py b/build.py index c8688b6..dbcacdf 100755 --- a/build.py +++ b/build.py @@ -196,7 +196,7 @@ def build(path: str, flags: Optional[Dict[str, str]] = {}, options: Optional[Dic logging.debug("Running tests") start = time() # TODO: Find a better way of running the test suite - process = run_command(f"{tests_path}", mode="run", shell=True, stderr=PIPE) + process = run_command(f"{tests_path} {'--stdout' if verbose else ''}", mode="run", shell=True, stderr=PIPE) if status != 0: logging.error(f"Command '{command}' exited with non-0 exit code {status}, output below:\n{stderr.decode()}") else: diff --git a/src/compiler.nim b/src/compiler.nim index 5e287ea..20727fc 100644 --- a/src/compiler.nim +++ b/src/compiler.nim @@ -312,11 +312,11 @@ proc binary(self: Compiler, canAssign: bool) = of TokenType.GT: self.emitByte(OpCode.Greater) of TokenType.GE: - self.emitBytes(OpCode.Less, OpCode.Not) + self.emitByte(OpCode.GreaterOrEqual) of TokenType.LT: self.emitByte(OpCode.Less) of TokenType.LE: - self.emitBytes(OpCode.Greater, OpCode.Not) + self.emitByte(OpCode.LessOrEqual) of TokenType.CARET: self.emitByte(OpCode.Xor) of TokenType.SHL: @@ -329,6 +329,8 @@ proc binary(self: Compiler, canAssign: bool) = self.emitByte(OpCode.Band) of TokenType.IS: self.emitByte(OpCode.Is) + of TokenType.ISNOT: + self.emitBytes(OpCode.Is, Opcode.Not) of TokenType.AS: self.emitByte(OpCode.As) else: @@ -991,7 +993,7 @@ proc statement(self: Compiler) = ## Parses statements if self.parser.match(TokenType.FOR): self.forStatement() - elif self.parser.match(IF): + elif self.parser.match(TokenType.IF): self.ifStatement() elif self.parser.match(TokenType.WHILE): self.whileStatement() @@ -1102,7 +1104,8 @@ var rules: array[TokenType, ParseRule] = [ makeRule(unary, nil, Precedence.None), # TILDE makeRule(nil, binary, Precedence.Is), # IS makeRule(nil, binary, Precedence.As), # AS - makeRule(parseLambda, nil, Precedence.None) # LAMBDA + makeRule(parseLambda, nil, Precedence.None), # LAMBDA + makeRule(nil, binary, Precedence.Is), # ISNOT ] diff --git a/src/lexer.nim b/src/lexer.nim index 163d33b..e59c655 100644 --- a/src/lexer.nim +++ b/src/lexer.nim @@ -58,7 +58,7 @@ const RESERVED = to_table({ "continue": TokenType.CONTINUE, "inf": TokenType.INF, "nan": TokenType.NAN, "is": TokenType.IS, "not": TokenType.NEG, "as": TokenType.AS, - "lambda": TokenType.LAMBDA}) + "lambda": TokenType.LAMBDA, "isnot": TokenType.ISNOT}) type Lexer* = ref object source*: string diff --git a/src/meta/opcode.nim b/src/meta/opcode.nim index da3d421..ac53388 100644 --- a/src/meta/opcode.nim +++ b/src/meta/opcode.nim @@ -44,6 +44,8 @@ type Greater, Less, Equal, + GreaterOrEqual, + LessOrEqual, Not, GetItem, Slice, @@ -81,7 +83,7 @@ const simpleInstructions* = {OpCode.Return, OpCode.Add, OpCode.Multiply, OpCode.Xor, OpCode.Not, OpCode.Equal, OpCode.Greater, OpCode.Less, OpCode.GetItem, OpCode.Slice, OpCode.Pop, OpCode.Negate, - OpCode.Is, OpCode.As} + OpCode.Is, OpCode.As, GreaterOrEqual, LessOrEqual} const constantInstructions* = {OpCode.Constant, OpCode.DefineGlobal, OpCode.GetGlobal, OpCode.SetGlobal, OpCode.DeleteGlobal} diff --git a/src/meta/token.nim b/src/meta/token.nim index 1a291a7..4c206ee 100644 --- a/src/meta/token.nim +++ b/src/meta/token.nim @@ -29,7 +29,8 @@ type WHILE, DEL, BREAK, EOF, COLON, CONTINUE, CARET, SHL, SHR, NAN, INF, BAND, - BOR, TILDE, IS, AS, LAMBDA + BOR, TILDE, IS, AS, LAMBDA, + ISNOT Token* = object kind*: TokenType lexeme*: string diff --git a/src/types/function.nim b/src/types/function.nim index 8375a8e..976fc4a 100644 --- a/src/types/function.nim +++ b/src/types/function.nim @@ -31,7 +31,7 @@ proc newFunction*(name: string = "", chunk: Chunk, arity: int = 0): ptr Function ## anonymous code object # TODO: Add support for optional parameters result = allocateObj(Function, ObjectType.Function) - if name.len > 1: + if name.len >= 1: result.name = name.asStr() else: result.name = nil diff --git a/src/types/methods.nim b/src/types/methods.nim index 606bd8e..bb7f0de 100644 --- a/src/types/methods.nim +++ b/src/types/methods.nim @@ -391,7 +391,7 @@ proc binaryNot*(self: ptr Obj): returnType = raise newException(NotImplementedError, "") -proc lt*(self: ptr Obj, other: ptr Obj): bool = +proc lt*(self: ptr Obj, other: ptr Obj): tuple[result: bool, obj: ptr Obj] = ## Returns the result of self < other or ## raises an error if the operation ## is unsupported @@ -406,7 +406,7 @@ proc lt*(self: ptr Obj, other: ptr Obj): bool = raise newException(NotImplementedError, "") -proc gt*(self: ptr Obj, other: ptr Obj): bool = +proc gt*(self: ptr Obj, other: ptr Obj): tuple[result: bool, obj: ptr Obj] = ## Returns the result of self > other or ## raises an error if the operation ## is unsupported diff --git a/src/types/numbers.nim b/src/types/numbers.nim index f242c06..7a7a5c5 100644 --- a/src/types/numbers.nim +++ b/src/types/numbers.nim @@ -133,50 +133,50 @@ proc eq*(self, other: ptr Infinity): bool = result = self.isNegative == other.isNegative -proc lt*(self: ptr Infinity, other: ptr Obj): bool = +proc lt*(self: ptr Infinity, other: ptr Obj): tuple[result: bool, obj: ptr Obj] = case other.kind: of ObjectType.Integer: let other = cast[ptr Integer](other) - if self.isNegative and other.intValue > 0: - result = true + if self.isNegative: + result = (result: true, obj: other) else: - result = false + result = (result: false, obj: nil) of ObjectType.Float: let other = cast[ptr Float](other) - if self.isNegative and other.floatValue > 0.0: - result = true + if self.isNegative: + result = (result: true, obj: other) else: - result = false + result = (result: false, obj: nil) of ObjectType.Infinity: let other = cast[ptr Infinity](other) if other.isNegative and not self.isNegative: - result = false + result = (result: true, obj: other) else: - result = false + result = (result: false, obj: nil) else: raise newException(NotImplementedError, "") -proc gt*(self: ptr Infinity, other: ptr Obj): bool = +proc gt*(self: ptr Infinity, other: ptr Obj): tuple[result: bool, obj: ptr Obj] = case other.kind: of ObjectType.Integer: let other = cast[ptr Integer](other) - if self.isNegative and other.intValue > 0: - result = false + if self.isNegative: + result = (result: false, obj: nil) else: - result = true + result = (result: true, obj: other) of ObjectType.Float: let other = cast[ptr Float](other) - if self.isNegative and other.floatValue > 0.0: - result = false + if self.isNegative: + result = (result: false, obj: nil) else: - result = true + result = (result: true, obj: other) of ObjectType.Infinity: let other = cast[ptr Infinity](other) if other.isNegative and not self.isNegative: - result = true + result = (result: false, obj: nil) else: - result = false + result = (result: true, obj: other) else: raise newException(NotImplementedError, "") @@ -216,7 +216,7 @@ proc stringify*(self: ptr Float): string = proc isFalsey*(self: ptr Float): bool = - result = self.floatValue == 0.0 + result = false proc hash*(self: ptr Float): uint64 = @@ -243,7 +243,7 @@ proc stringify*(self: ptr Integer): string = proc isFalsey*(self: ptr Integer): bool = - result = self.intValue == 0 + result = false proc eq*(self, other: ptr Integer): bool = @@ -259,66 +259,90 @@ proc hash*(self: ptr Integer): uint64 = result = uint64 self.intValue -proc lt*(self: ptr Integer, other: ptr Obj): bool = +proc lt*(self: ptr Integer, other: ptr Obj): tuple[result: bool, obj: ptr Obj] = case other.kind: of ObjectType.Integer: - result = self.intValue < cast[ptr Integer](other).intValue + if self.intValue < cast[ptr Integer](other).intValue: + result = (result: true, obj: other) + else: + result = (result: false, obj: nil) of ObjectType.Float: - result = (float self.intValue) < cast[ptr Float](other).floatValue + if (float self.intValue) < cast[ptr Float](other).floatValue: + result = (result: true, obj: other) + else: + result = (result: false, obj: nil) of ObjectType.Infinity: let other = cast[ptr Infinity](other) if other.isNegative: - result = false + result = (result: true, obj: other) else: - result = true + result = (result: false, obj: nil) else: raise newException(NotImplementedError, "") -proc lt*(self: ptr Float, other: ptr Obj): bool = +proc lt*(self: ptr Float, other: ptr Obj): tuple[result: bool, obj: ptr Obj] = case other.kind: of ObjectType.Integer: - result = self.floatValue < (float cast[ptr Integer](other).intValue) + if self.floatValue < (float cast[ptr Integer](other).intValue): + result = (result: true, obj: other) + else: + result = (result: false, obj: nil) of ObjectType.Float: - result = self.floatValue < cast[ptr Float](other).floatValue + if self.floatValue < cast[ptr Float](other).floatValue: + result = (result: true, obj: other) + else: + result = (result: false, obj: nil) of ObjectType.Infinity: let other = cast[ptr Infinity](other) if other.isNegative: - result = false + result = (result: true, obj: other) else: - result = true + result = (result: false, obj: nil) else: raise newException(NotImplementedError, "") -proc gt*(self: ptr Integer, other: ptr Obj): bool = +proc gt*(self: ptr Integer, other: ptr Obj): tuple[result: bool, obj: ptr Obj] = case other.kind: of ObjectType.Integer: - result = self.intValue > cast[ptr Integer](other).intValue + if self.intValue > cast[ptr Integer](other).intValue: + result = (result: true, obj: other) + else: + result = (result: false, obj: nil) of ObjectType.Float: - result = (float self.intValue) > cast[ptr Float](other).floatValue + if (float self.intValue) > cast[ptr Float](other).floatValue: + result = (result: true, obj: other) + else: + result = (result: false, obj: nil) of ObjectType.Infinity: let other = cast[ptr Infinity](other) if other.isNegative: - result = true + result = (result: true, obj: other) else: - result = false + result = (result: false, obj: nil) else: raise newException(NotImplementedError, "") -proc gt*(self: ptr Float, other: ptr Obj): bool = +proc gt*(self: ptr Float, other: ptr Obj): tuple[result: bool, obj: ptr Obj] = case other.kind: of ObjectType.Integer: - result = self.floatValue > (float cast[ptr Integer](other).intValue) + if self.floatValue > (float cast[ptr Integer](other).intValue): + result = (result: true, obj: other) + else: + result = (result: false, obj: nil) of ObjectType.Float: - result = self.floatValue > cast[ptr Float](other).floatValue + if self.floatValue > cast[ptr Float](other).floatValue: + result = (result: true, obj: other) + else: + result = (result: false, obj: nil) of ObjectType.Infinity: let other = cast[ptr Infinity](other) if other.isNegative: - result = true + result = (result: true, obj: other) else: - result = false + result = (result: false, obj: nil) else: raise newException(NotImplementedError, "") diff --git a/src/vm.nim b/src/vm.nim index f440d91..64c17a8 100644 --- a/src/vm.nim +++ b/src/vm.nim @@ -505,8 +505,13 @@ proc run(self: VM): InterpretResult = # Binary less (<) var right = self.pop() var left = self.pop() + var comp: tuple[result: bool, obj: ptr Obj] try: - self.push(self.getBoolean(left.lt(right))) + comp = left.lt(right) + if system.`==`(comp.obj, nil): + self.push(self.getBoolean(comp.result)) + else: + self.push(comp.obj) except NotImplementedError: self.error(newTypeError(&"unsupported binary operator '<' for objects of type '{left.typeName()}' and '{right.typeName()}'")) return RuntimeError @@ -514,18 +519,55 @@ proc run(self: VM): InterpretResult = # Binary greater (>) var right = self.pop() var left = self.pop() + var comp: tuple[result: bool, obj: ptr Obj] try: - self.push(self.getBoolean(left.gt(right))) + comp = left.gt(right) + if system.`==`(comp.obj, nil): + self.push(self.getBoolean(comp.result)) + else: + self.push(comp.obj) + except NotImplementedError: + self.error(newTypeError(&"unsupported binary operator '>' for objects of type '{left.typeName()}' and '{right.typeName()}'")) + return RuntimeError + of OpCode.LessOrEqual: + var right = self.pop() + var left = self.pop() + var comp: tuple[result: bool, obj: ptr Obj] + try: + comp = left.lt(right) + if not comp.result and left == right: + comp.result = true + if system.`==`(comp.obj, nil): + self.push(self.getBoolean(comp.result)) + else: + self.push(comp.obj) + except NotImplementedError: + self.error(newTypeError(&"unsupported binary operator '<' for objects of type '{left.typeName()}' and '{right.typeName()}'")) + return RuntimeError + of OpCode.GreaterOrEqual: + var right = self.pop() + var left = self.pop() + var comp: tuple[result: bool, obj: ptr Obj] + try: + comp = left.gt(right) + if not comp.result and left == right: + comp.result = true + if system.`==`(comp.obj, nil): + self.push(self.getBoolean(comp.result)) + else: + self.push(comp.obj) except NotImplementedError: self.error(newTypeError(&"unsupported binary operator '>' for objects of type '{left.typeName()}' and '{right.typeName()}'")) return RuntimeError of OpCode.Is: # Implements object identity (i.e. same pointer) # This is implemented internally for obvious - # reasons and works on any pair of objects + # reasons and works on any pair of objects, which + # is why we call nim's system.== operator and NOT + # our custom one var right = self.pop() var left = self.pop() - self.push(self.getBoolean(left == right)) + self.push(self.getBoolean(system.`==`(left, right))) of OpCode.As: # Implements type casting (TODO: Only allow classes) var right = self.pop() @@ -629,6 +671,7 @@ proc run(self: VM): InterpretResult = # Handles returning values from the callee to the caller # and sets up the stack to proceed with execution var retResult = self.pop() + # Pops the function's frame discard self.frames.pop() if self.frames.len() == 0: discard self.pop() diff --git a/tests/japl/booleans.jpl b/tests/japl/booleans.jpl index ac15abd..d62115b 100644 --- a/tests/japl/booleans.jpl +++ b/tests/japl/booleans.jpl @@ -9,17 +9,17 @@ print(false and 3 or 4);//stdout:4 print(true and 3 or 4);//stdout:3 print(true and 2);//stdout:2 print(false or 5);//stdout:5 -print(0 or 4);//stdout:4 -print(0 and true);//stdout:0 +print(nil or 4);//stdout:4 +print(0 or true);//stdout:0 print("" and true);//stdout:'' print("" or true);//stdout:true print(1 or 2 or 3 or 4);//stdout:1 print(1 and 2 and 3 and 4);//stdout:4 print(1 and 2 or 3 and 4);//stdout:2 print(1 and false or 3 and 4);//stdout:4 -print(not 0);//stdout:true +print(not false);//stdout:true print(not 1);//stdout:false print(not 1 and not 2);//stdout:false -print(not (1 and 0));//stdout:true +print(not (1 and false));//stdout:true [end] [end] diff --git a/tests/japl/is.jpl b/tests/japl/is.jpl index 1900ce3..92bc584 100644 --- a/tests/japl/is.jpl +++ b/tests/japl/is.jpl @@ -1,5 +1,4 @@ [Test: is] -[skip] [source:mixed] var x = 4; var y = x;