mirror of https://github.com/japl-lang/japl.git
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:
parent
607d88f0c0
commit
5d8c31fb07
2
build.py
2
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:
|
||||
|
|
|
@ -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
|
||||
|
||||
]
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, "")
|
||||
|
||||
|
|
51
src/vm.nim
51
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()
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
[Test: is]
|
||||
[skip]
|
||||
[source:mixed]
|
||||
var x = 4;
|
||||
var y = x;
|
||||
|
|
Loading…
Reference in New Issue