Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Productive2 2021-02-26 15:13:20 +01:00
commit 960ba879cb
12 changed files with 191 additions and 89 deletions

26
.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,26 @@
# Automatically runs tests
name: Run tests
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
workflow_dispatch:
jobs:
tests:
runs-on: ubuntu-latest
steps:
- name: Setup Python
uses: actions/setup-python@v2.2.1
with:
# We test using a reasonably modern Python version
python-version: 3.8.0
architecture: x64
- uses: actions/checkout@v2
- name: Run production-mode tests
run: ./build.py --profile resources/profiles/production.json
- name: Run developmet tests
run: ./build.py --profile resources/profiles/production.json

View File

@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# Copyright 2020 Mattia Giambirtone # Copyright 2020 Mattia Giambirtone
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
@ -148,7 +148,7 @@ def build(path: str, flags: Optional[Dict[str, str]] = {}, options: Optional[Dic
listing = "\n- {} = {}" listing = "\n- {} = {}"
if not os.path.exists(path): if not os.path.exists(path):
logging.error(f"Input path '{path}' does not exist") logging.error(f"Input path '{path}' does not exist")
return return False
if os.path.isfile(config_path) and not override: if os.path.isfile(config_path) and not override:
logging.warning(f"A config file exists at '{config_path}', keeping it") logging.warning(f"A config file exists at '{config_path}', keeping it")
else: else:
@ -159,7 +159,7 @@ def build(path: str, flags: Optional[Dict[str, str]] = {}, options: Optional[Dic
build_config.write(CONFIG_TEMPLATE.format(**options)) build_config.write(CONFIG_TEMPLATE.format(**options))
except Exception as fatal: except Exception as fatal:
logging.error(f"A fatal unhandled exception occurred -> {type(fatal).__name__}: {fatal}") logging.error(f"A fatal unhandled exception occurred -> {type(fatal).__name__}: {fatal}")
return return False
else: else:
logging.debug(f"Config file has been generated, compiling with options as follows: {''.join(listing.format(k, v) for k, v in options.items())}") logging.debug(f"Config file has been generated, compiling with options as follows: {''.join(listing.format(k, v) for k, v in options.items())}")
logging.debug(f"Nim compiler options: {''.join(listing.format(k, v) for k, v in flags.items())}") logging.debug(f"Nim compiler options: {''.join(listing.format(k, v) for k, v in flags.items())}")
@ -186,22 +186,22 @@ def build(path: str, flags: Optional[Dict[str, str]] = {}, options: Optional[Dic
_, stderr, status = run_command(command, stdout=DEVNULL, stderr=PIPE) _, stderr, status = run_command(command, stdout=DEVNULL, 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: return False
command = f"nim compile --opt:speed {tests_path}" command = f"nim compile --opt:speed {tests_path}"
_, stderr, status = run_command(command, stdout=DEVNULL, stderr=PIPE) _, stderr, status = run_command(command, stdout=DEVNULL, 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: return False
logging.debug(f"Test suite compilation completed in {time() - start:.2f} seconds") logging.debug(f"Test suite compilation completed in {time() - start:.2f} seconds")
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: return False
logging.debug(f"Test suite ran in {time() - start:.2f} seconds") logging.debug(f"Test suite ran in {time() - start:.2f} seconds")
logging.info("Test suite completed!") logging.info("Test suite completed!")
if args.install: if args.install:
if os.name == "nt": if os.name == "nt":
logging.warning("Sorry, but automatically installing JAPL is not yet supported on windows") logging.warning("Sorry, but automatically installing JAPL is not yet supported on windows")
@ -220,14 +220,15 @@ def build(path: str, flags: Optional[Dict[str, str]] = {}, options: Optional[Dic
logging.debug(f"Path '{path}' is not writable, attempting next entry in PATH") logging.debug(f"Path '{path}' is not writable, attempting next entry in PATH")
except Exception as fatal: except Exception as fatal:
logging.error(f"A fatal unhandled exception occurred -> {type(fatal).__name__}: {fatal}") logging.error(f"A fatal unhandled exception occurred -> {type(fatal).__name__}: {fatal}")
else: return False
logging.debug(f"JAPL installed at '{path}', setting executable permissions") logging.debug(f"JAPL installed at '{path}', setting executable permissions")
# TODO: Use external oschmod library once we support windows! # TODO: Use external oschmod library once we support windows!
try: try:
os.chmod(install_path, os.stat(install_path).st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) os.chmod(install_path, os.stat(install_path).st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
except Exception as fatal: except Exception as fatal:
logging.error(f"A fatal unhandled exception occurred -> {type(fatal).__name__}: {fatal}") logging.error(f"A fatal unhandled exception occurred -> {type(fatal).__name__}: {fatal}")
break break
return True
if __name__ == "__main__": if __name__ == "__main__":
@ -310,14 +311,17 @@ if __name__ == "__main__":
exit() exit()
else: else:
logging.info(f"Using profile '{args.profile}'") logging.info(f"Using profile '{args.profile}'")
build(args.path, if build(args.path,
flags, flags,
options, options,
args.override_config, args.override_config,
args.skip_tests, args.skip_tests,
args.install, args.install,
args.ignore_binary, args.ignore_binary,
args.verbose) args.verbose):
logging.debug("Build tool exited") logging.debug("Build tool exited successfully")
else:
logging.debug("Build tool exited with error")
exit(1)
except KeyboardInterrupt: except KeyboardInterrupt:
logging.info("Interrupted by the user") logging.info("Interrupted by the user")

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;