Fixed identity operator bug and added isnot for consistency. Changed behavior for gt/lt comparisons to account for more complex expressions

This commit is contained in:
nocturn9x 2021-02-26 12:27:10 +01:00
parent 607d88f0c0
commit 5d8c31fb07
11 changed files with 132 additions and 60 deletions

View File

@ -196,7 +196,7 @@ def build(path: str, flags: Optional[Dict[str, str]] = {}, options: Optional[Dic
logging.debug("Running tests") logging.debug("Running tests")
start = time() start = time()
# TODO: Find a better way of running the test suite # 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: if status != 0:
logging.error(f"Command '{command}' exited with non-0 exit code {status}, output below:\n{stderr.decode()}") logging.error(f"Command '{command}' exited with non-0 exit code {status}, output below:\n{stderr.decode()}")
else: else:

View File

@ -312,11 +312,11 @@ proc binary(self: Compiler, canAssign: bool) =
of TokenType.GT: of TokenType.GT:
self.emitByte(OpCode.Greater) self.emitByte(OpCode.Greater)
of TokenType.GE: of TokenType.GE:
self.emitBytes(OpCode.Less, OpCode.Not) self.emitByte(OpCode.GreaterOrEqual)
of TokenType.LT: of TokenType.LT:
self.emitByte(OpCode.Less) self.emitByte(OpCode.Less)
of TokenType.LE: of TokenType.LE:
self.emitBytes(OpCode.Greater, OpCode.Not) self.emitByte(OpCode.LessOrEqual)
of TokenType.CARET: of TokenType.CARET:
self.emitByte(OpCode.Xor) self.emitByte(OpCode.Xor)
of TokenType.SHL: of TokenType.SHL:
@ -329,6 +329,8 @@ proc binary(self: Compiler, canAssign: bool) =
self.emitByte(OpCode.Band) self.emitByte(OpCode.Band)
of TokenType.IS: of TokenType.IS:
self.emitByte(OpCode.Is) self.emitByte(OpCode.Is)
of TokenType.ISNOT:
self.emitBytes(OpCode.Is, Opcode.Not)
of TokenType.AS: of TokenType.AS:
self.emitByte(OpCode.As) self.emitByte(OpCode.As)
else: else:
@ -991,7 +993,7 @@ proc statement(self: Compiler) =
## Parses statements ## Parses statements
if self.parser.match(TokenType.FOR): if self.parser.match(TokenType.FOR):
self.forStatement() self.forStatement()
elif self.parser.match(IF): elif self.parser.match(TokenType.IF):
self.ifStatement() self.ifStatement()
elif self.parser.match(TokenType.WHILE): elif self.parser.match(TokenType.WHILE):
self.whileStatement() self.whileStatement()
@ -1102,7 +1104,8 @@ var rules: array[TokenType, ParseRule] = [
makeRule(unary, nil, Precedence.None), # TILDE makeRule(unary, nil, Precedence.None), # TILDE
makeRule(nil, binary, Precedence.Is), # IS makeRule(nil, binary, Precedence.Is), # IS
makeRule(nil, binary, Precedence.As), # AS makeRule(nil, binary, Precedence.As), # AS
makeRule(parseLambda, nil, Precedence.None) # LAMBDA makeRule(parseLambda, nil, Precedence.None), # LAMBDA
makeRule(nil, binary, Precedence.Is), # ISNOT
] ]

View File

@ -58,7 +58,7 @@ const RESERVED = to_table({
"continue": TokenType.CONTINUE, "inf": TokenType.INF, "continue": TokenType.CONTINUE, "inf": TokenType.INF,
"nan": TokenType.NAN, "is": TokenType.IS, "nan": TokenType.NAN, "is": TokenType.IS,
"not": TokenType.NEG, "as": TokenType.AS, "not": TokenType.NEG, "as": TokenType.AS,
"lambda": TokenType.LAMBDA}) "lambda": TokenType.LAMBDA, "isnot": TokenType.ISNOT})
type type
Lexer* = ref object Lexer* = ref object
source*: string source*: string

View File

@ -44,6 +44,8 @@ type
Greater, Greater,
Less, Less,
Equal, Equal,
GreaterOrEqual,
LessOrEqual,
Not, Not,
GetItem, GetItem,
Slice, Slice,
@ -81,7 +83,7 @@ const simpleInstructions* = {OpCode.Return, OpCode.Add, OpCode.Multiply,
OpCode.Xor, OpCode.Not, OpCode.Equal, OpCode.Xor, OpCode.Not, OpCode.Equal,
OpCode.Greater, OpCode.Less, OpCode.GetItem, OpCode.Greater, OpCode.Less, OpCode.GetItem,
OpCode.Slice, OpCode.Pop, OpCode.Negate, OpCode.Slice, OpCode.Pop, OpCode.Negate,
OpCode.Is, OpCode.As} OpCode.Is, OpCode.As, GreaterOrEqual, LessOrEqual}
const constantInstructions* = {OpCode.Constant, OpCode.DefineGlobal, const constantInstructions* = {OpCode.Constant, OpCode.DefineGlobal,
OpCode.GetGlobal, OpCode.SetGlobal, OpCode.GetGlobal, OpCode.SetGlobal,
OpCode.DeleteGlobal} OpCode.DeleteGlobal}

View File

@ -29,7 +29,8 @@ type
WHILE, DEL, BREAK, EOF, WHILE, DEL, BREAK, EOF,
COLON, CONTINUE, CARET, COLON, CONTINUE, CARET,
SHL, SHR, NAN, INF, BAND, SHL, SHR, NAN, INF, BAND,
BOR, TILDE, IS, AS, LAMBDA BOR, TILDE, IS, AS, LAMBDA,
ISNOT
Token* = object Token* = object
kind*: TokenType kind*: TokenType
lexeme*: string lexeme*: string

View File

@ -31,7 +31,7 @@ proc newFunction*(name: string = "", chunk: Chunk, arity: int = 0): ptr Function
## anonymous code object ## anonymous code object
# TODO: Add support for optional parameters # TODO: Add support for optional parameters
result = allocateObj(Function, ObjectType.Function) result = allocateObj(Function, ObjectType.Function)
if name.len > 1: if name.len >= 1:
result.name = name.asStr() result.name = name.asStr()
else: else:
result.name = nil result.name = nil

View File

@ -391,7 +391,7 @@ proc binaryNot*(self: ptr Obj): returnType =
raise newException(NotImplementedError, "") 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 ## Returns the result of self < other or
## raises an error if the operation ## raises an error if the operation
## is unsupported ## is unsupported
@ -406,7 +406,7 @@ proc lt*(self: ptr Obj, other: ptr Obj): bool =
raise newException(NotImplementedError, "") 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 ## Returns the result of self > other or
## raises an error if the operation ## raises an error if the operation
## is unsupported ## is unsupported

View File

@ -133,50 +133,50 @@ proc eq*(self, other: ptr Infinity): bool =
result = self.isNegative == other.isNegative 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: case other.kind:
of ObjectType.Integer: of ObjectType.Integer:
let other = cast[ptr Integer](other) let other = cast[ptr Integer](other)
if self.isNegative and other.intValue > 0: if self.isNegative:
result = true result = (result: true, obj: other)
else: else:
result = false result = (result: false, obj: nil)
of ObjectType.Float: of ObjectType.Float:
let other = cast[ptr Float](other) let other = cast[ptr Float](other)
if self.isNegative and other.floatValue > 0.0: if self.isNegative:
result = true result = (result: true, obj: other)
else: else:
result = false result = (result: false, obj: nil)
of ObjectType.Infinity: of ObjectType.Infinity:
let other = cast[ptr Infinity](other) let other = cast[ptr Infinity](other)
if other.isNegative and not self.isNegative: if other.isNegative and not self.isNegative:
result = false result = (result: true, obj: other)
else: else:
result = false result = (result: false, obj: nil)
else: else:
raise newException(NotImplementedError, "") 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: case other.kind:
of ObjectType.Integer: of ObjectType.Integer:
let other = cast[ptr Integer](other) let other = cast[ptr Integer](other)
if self.isNegative and other.intValue > 0: if self.isNegative:
result = false result = (result: false, obj: nil)
else: else:
result = true result = (result: true, obj: other)
of ObjectType.Float: of ObjectType.Float:
let other = cast[ptr Float](other) let other = cast[ptr Float](other)
if self.isNegative and other.floatValue > 0.0: if self.isNegative:
result = false result = (result: false, obj: nil)
else: else:
result = true result = (result: true, obj: other)
of ObjectType.Infinity: of ObjectType.Infinity:
let other = cast[ptr Infinity](other) let other = cast[ptr Infinity](other)
if other.isNegative and not self.isNegative: if other.isNegative and not self.isNegative:
result = true result = (result: false, obj: nil)
else: else:
result = false result = (result: true, obj: other)
else: else:
raise newException(NotImplementedError, "") raise newException(NotImplementedError, "")
@ -216,7 +216,7 @@ proc stringify*(self: ptr Float): string =
proc isFalsey*(self: ptr Float): bool = proc isFalsey*(self: ptr Float): bool =
result = self.floatValue == 0.0 result = false
proc hash*(self: ptr Float): uint64 = proc hash*(self: ptr Float): uint64 =
@ -243,7 +243,7 @@ proc stringify*(self: ptr Integer): string =
proc isFalsey*(self: ptr Integer): bool = proc isFalsey*(self: ptr Integer): bool =
result = self.intValue == 0 result = false
proc eq*(self, other: ptr Integer): bool = proc eq*(self, other: ptr Integer): bool =
@ -259,66 +259,90 @@ proc hash*(self: ptr Integer): uint64 =
result = uint64 self.intValue 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: case other.kind:
of ObjectType.Integer: 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: 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: of ObjectType.Infinity:
let other = cast[ptr Infinity](other) let other = cast[ptr Infinity](other)
if other.isNegative: if other.isNegative:
result = false result = (result: true, obj: other)
else: else:
result = true result = (result: false, obj: nil)
else: else:
raise newException(NotImplementedError, "") 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: case other.kind:
of ObjectType.Integer: 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: 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: of ObjectType.Infinity:
let other = cast[ptr Infinity](other) let other = cast[ptr Infinity](other)
if other.isNegative: if other.isNegative:
result = false result = (result: true, obj: other)
else: else:
result = true result = (result: false, obj: nil)
else: else:
raise newException(NotImplementedError, "") 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: case other.kind:
of ObjectType.Integer: 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: 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: of ObjectType.Infinity:
let other = cast[ptr Infinity](other) let other = cast[ptr Infinity](other)
if other.isNegative: if other.isNegative:
result = true result = (result: true, obj: other)
else: else:
result = false result = (result: false, obj: nil)
else: else:
raise newException(NotImplementedError, "") 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: case other.kind:
of ObjectType.Integer: 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: 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: of ObjectType.Infinity:
let other = cast[ptr Infinity](other) let other = cast[ptr Infinity](other)
if other.isNegative: if other.isNegative:
result = true result = (result: true, obj: other)
else: else:
result = false result = (result: false, obj: nil)
else: else:
raise newException(NotImplementedError, "") raise newException(NotImplementedError, "")

View File

@ -505,8 +505,13 @@ proc run(self: VM): InterpretResult =
# Binary less (<) # Binary less (<)
var right = self.pop() var right = self.pop()
var left = self.pop() var left = self.pop()
var comp: tuple[result: bool, obj: ptr Obj]
try: 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: except NotImplementedError:
self.error(newTypeError(&"unsupported binary operator '<' for objects of type '{left.typeName()}' and '{right.typeName()}'")) self.error(newTypeError(&"unsupported binary operator '<' for objects of type '{left.typeName()}' and '{right.typeName()}'"))
return RuntimeError return RuntimeError
@ -514,18 +519,55 @@ proc run(self: VM): InterpretResult =
# Binary greater (>) # Binary greater (>)
var right = self.pop() var right = self.pop()
var left = self.pop() var left = self.pop()
var comp: tuple[result: bool, obj: ptr Obj]
try: 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: except NotImplementedError:
self.error(newTypeError(&"unsupported binary operator '>' for objects of type '{left.typeName()}' and '{right.typeName()}'")) self.error(newTypeError(&"unsupported binary operator '>' for objects of type '{left.typeName()}' and '{right.typeName()}'"))
return RuntimeError return RuntimeError
of OpCode.Is: of OpCode.Is:
# Implements object identity (i.e. same pointer) # Implements object identity (i.e. same pointer)
# This is implemented internally for obvious # 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 right = self.pop()
var left = self.pop() var left = self.pop()
self.push(self.getBoolean(left == right)) self.push(self.getBoolean(system.`==`(left, right)))
of OpCode.As: of OpCode.As:
# Implements type casting (TODO: Only allow classes) # Implements type casting (TODO: Only allow classes)
var right = self.pop() var right = self.pop()
@ -629,6 +671,7 @@ proc run(self: VM): InterpretResult =
# Handles returning values from the callee to the caller # Handles returning values from the callee to the caller
# and sets up the stack to proceed with execution # and sets up the stack to proceed with execution
var retResult = self.pop() var retResult = self.pop()
# Pops the function's frame
discard self.frames.pop() discard self.frames.pop()
if self.frames.len() == 0: if self.frames.len() == 0:
discard self.pop() discard self.pop()

View File

@ -9,17 +9,17 @@ print(false and 3 or 4);//stdout:4
print(true and 3 or 4);//stdout:3 print(true and 3 or 4);//stdout:3
print(true and 2);//stdout:2 print(true and 2);//stdout:2
print(false or 5);//stdout:5 print(false or 5);//stdout:5
print(0 or 4);//stdout:4 print(nil or 4);//stdout:4
print(0 and true);//stdout:0 print(0 or true);//stdout:0
print("" and true);//stdout:'' print("" and true);//stdout:''
print("" or true);//stdout:true print("" or true);//stdout:true
print(1 or 2 or 3 or 4);//stdout:1 print(1 or 2 or 3 or 4);//stdout:1
print(1 and 2 and 3 and 4);//stdout:4 print(1 and 2 and 3 and 4);//stdout:4
print(1 and 2 or 3 and 4);//stdout:2 print(1 and 2 or 3 and 4);//stdout:2
print(1 and false or 3 and 4);//stdout:4 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);//stdout:false
print(not 1 and not 2);//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]
[end] [end]

View File

@ -1,5 +1,4 @@
[Test: is] [Test: is]
[skip]
[source:mixed] [source:mixed]
var x = 4; var x = 4;
var y = x; var y = x;