mirror of https://github.com/japl-lang/japl.git
Merge pull request #28 from Productive2/master
Testing suite + debugger bugs + some binary operator fixes
This commit is contained in:
commit
aaf2e66528
|
@ -11,6 +11,8 @@ htmldocs/
|
|||
# JAPL
|
||||
|
||||
src/japl
|
||||
src/compiler
|
||||
tests/runtests
|
||||
|
||||
# MacOS
|
||||
|
||||
|
@ -29,3 +31,7 @@ main
|
|||
# JAPL build config file
|
||||
|
||||
config.nim
|
||||
|
||||
# test results
|
||||
|
||||
testresults.txt
|
||||
|
|
2
build.py
2
build.py
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2020 Mattia Giambirtone
|
||||
#
|
||||
|
|
|
@ -72,6 +72,7 @@ type
|
|||
Term,
|
||||
Factor,
|
||||
Unary,
|
||||
Exponentiation,
|
||||
Call,
|
||||
Primary
|
||||
|
||||
|
@ -1095,7 +1096,7 @@ var rules: array[TokenType, ParseRule] = [
|
|||
makeRule(nil, binary, Precedence.Comparison), # GE
|
||||
makeRule(nil, binary, Precedence.Comparison), # LE
|
||||
makeRule(nil, binary, Precedence.Factor), # MOD
|
||||
makeRule(nil, binary, Precedence.Factor), # POW
|
||||
makeRule(nil, binary, Precedence.Exponentiation), # POW
|
||||
makeRule(nil, binary, Precedence.Comparison), # GT
|
||||
makeRule(grouping, call, Precedence.Call), # LP
|
||||
makeRule(nil, nil, Precedence.None), # RP
|
||||
|
|
|
@ -79,10 +79,11 @@ const simpleInstructions* = {OpCode.Return, OpCode.Add, OpCode.Multiply,
|
|||
OpCode.Inf, OpCode.Shl, OpCode.Shr,
|
||||
OpCode.Xor, OpCode.Not, OpCode.Equal,
|
||||
OpCode.Greater, OpCode.Less, OpCode.GetItem,
|
||||
OpCode.Slice, OpCode.Pop, OpCode.DefineGlobal,
|
||||
OpCode.GetGlobal, OpCode.SetGlobal,
|
||||
OpCode.DeleteGlobal}
|
||||
const constantInstructions* = {OpCode.Constant}
|
||||
OpCode.Slice, OpCode.Pop, OpCode.Negate}
|
||||
const constantInstructions* = {OpCode.Constant, OpCode.DefineGlobal,
|
||||
OpCode.GetGlobal, OpCode.SetGlobal,
|
||||
OpCode.DeleteGlobal}
|
||||
|
||||
const constantLongInstructions* = {OpCode.ConstantLong}
|
||||
const byteInstructions* = {OpCode.SetLocal, OpCode.GetLocal, OpCode.DeleteLocal,
|
||||
OpCode.Call}
|
||||
|
|
|
@ -31,8 +31,12 @@ proc natPrint(args: seq[ptr Obj]): tuple[ok: bool, result: ptr Obj] =
|
|||
## is passed, they will be printed separated
|
||||
## by a space
|
||||
var res = ""
|
||||
for arg in args:
|
||||
res = res & arg.stringify() & " "
|
||||
for i in countup(0, args.high()):
|
||||
let arg = args[i]
|
||||
if i < args.high():
|
||||
res = res & arg.stringify() & " "
|
||||
else:
|
||||
res = res & arg.stringify()
|
||||
echo res
|
||||
return (ok: true, result: asNil())
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ proc simpleInstruction(name: string, index: int): int =
|
|||
proc byteInstruction(name: string, chunk: Chunk, offset: int): int =
|
||||
var slot = chunk.code[offset + 1]
|
||||
echo &"\tInstruction at IP: {name}, points to slot {slot}\n"
|
||||
return offset + 1
|
||||
return offset + 2
|
||||
|
||||
|
||||
proc constantLongInstruction(name: string, chunk: Chunk, offset: int): int =
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
//int arithmetic
|
||||
|
||||
print(7+5); //output:12
|
||||
print(-8); //output:-8
|
||||
print(5-8); //output:-3
|
||||
print(1+1+1+1+1); //output:5
|
||||
print(1-1+1-1+1-1); //output:0
|
||||
print(2*3+2);//output:8
|
||||
print(2+3*2);//output:8
|
||||
print(3+2*7);//output:17
|
||||
print(2-9*5);//output:-43
|
||||
print(2*9-5);//output:13
|
||||
print(2**5);//output:32
|
||||
print(3**3);//output:27
|
||||
print(3**3*2);//output:54
|
||||
print(8+2**4);//output:24
|
||||
print(2+7*2+4);//output:20
|
||||
print(1-2**2*5);//output:-19
|
||||
print(7*-2**3+4*7);//output:-28
|
||||
print(-2**2);//output:-4
|
||||
print((-2)**2);//output:4
|
||||
print(-2**3);//output:-8
|
||||
print((-2)**3);//output:-8
|
||||
print((2+3)*4);//output:20
|
||||
print(2*(2+2)*2);//output:16
|
||||
print(2*(2*2)*2);//output:16
|
||||
print(8%5);//output:3
|
||||
print(4%3);//output:1
|
||||
print(8/4);//output:2.0
|
||||
print(28/7/4);//output:1.0
|
||||
print(64/-64);//output:-1.0
|
||||
print(8/0);//output:inf
|
||||
print(8/-0);//output:inf
|
||||
print(-8/0);//output:-inf
|
|
@ -0,0 +1,21 @@
|
|||
print(2 or 3);//output:2
|
||||
print(2 and 3);//output:3
|
||||
print(false or true);//output:true
|
||||
print(true or false);//output:true
|
||||
print(true and false);//output:false
|
||||
print(false and 3 or 4);//output:4
|
||||
print(true and 3 or 4);//output:3
|
||||
print(true and 2);//output:2
|
||||
print(false or 5);//output:5
|
||||
print(0 or 4);//output:4
|
||||
print(0 and true);//output:0
|
||||
print("" and true);//output:
|
||||
print("" or true);//output:true
|
||||
print(1 or 2 or 3 or 4);//output:1
|
||||
print(1 and 2 and 3 and 4);//output:4
|
||||
print(1 and 2 or 3 and 4);//output:2
|
||||
print(1 and false or 3 and 4);//output:4
|
||||
print(!0);//output:true
|
||||
print(!1);//output:false
|
||||
print(!1 and !2);//output:false
|
||||
print(!(1 and 0));//output:true
|
|
@ -0,0 +1,6 @@
|
|||
for (var x = 0; x < 2; x = x + 1)
|
||||
{
|
||||
print(x);
|
||||
//output:0
|
||||
//output:1
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
var x = 5;
|
||||
if (x > 2)
|
||||
{
|
||||
print("large");//output:large
|
||||
}
|
||||
else
|
||||
{
|
||||
print("smol");//won't run
|
||||
}
|
||||
|
||||
if (x < 4)
|
||||
{
|
||||
print("large");//won't run
|
||||
}
|
||||
print("beep");//output:beep
|
||||
|
||||
if (x == 5)
|
||||
print("equal to 5");//output:equal to 5
|
||||
|
||||
if (2 != x)
|
||||
print("not 2");//output:not 2
|
||||
else
|
||||
print("2");
|
||||
|
||||
if (2 == x)
|
||||
print("2");
|
||||
else
|
||||
print("not 2");//output:not 2
|
|
@ -0,0 +1,30 @@
|
|||
var x = 1;
|
||||
var y = 2;
|
||||
print(x);//output:1
|
||||
print(y);//output:2
|
||||
{
|
||||
var x = 4;
|
||||
var y = 5;
|
||||
print(x);//output:4
|
||||
print(y);//output:5
|
||||
{
|
||||
var z = 6;
|
||||
var y = 2;
|
||||
print(x);//output:4
|
||||
print(y);//output:2
|
||||
}
|
||||
print(x);//output:4
|
||||
print(y);//output:5
|
||||
}
|
||||
|
||||
print(x);//output:1
|
||||
print(y);//output:2
|
||||
|
||||
var longName;
|
||||
print(longName); //output:nil
|
||||
longName = 5;
|
||||
print(longName); //output:5
|
||||
longName = "hello";
|
||||
print(longName); //output:hello
|
||||
longName = longName + " world";
|
||||
print(longName); //output:hello world
|
|
@ -0,0 +1,20 @@
|
|||
var x = 5;
|
||||
while (x > 0)
|
||||
{
|
||||
x = x - 1;
|
||||
print(x);
|
||||
//output:4
|
||||
//output:3
|
||||
//output:2
|
||||
//output:1
|
||||
//output:0
|
||||
}
|
||||
|
||||
var string = "h";
|
||||
|
||||
while (x < 10)
|
||||
{
|
||||
x = x + 1;
|
||||
string = string + "A";
|
||||
}
|
||||
print(string);//output:hAAAAAAAAAA
|
|
@ -0,0 +1,7 @@
|
|||
import ../src/multibyte
|
||||
|
||||
|
||||
template testMultibyte* =
|
||||
for i in countup(0, int(uint16.high())):
|
||||
assert fromDouble(toDouble(i)) == uint16(i)
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
import ../../src/multibyte
|
||||
|
||||
|
||||
for i in countup(0, int(uint16.high())):
|
||||
assert fromDouble(toDouble(i)) == uint16(i)
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
# temporary nim file so I can see if the stuff I've touched compiles :'D
|
||||
|
||||
import ..meta/opcode
|
||||
import ..types/jobject
|
||||
import ..util/debug
|
|
@ -0,0 +1,102 @@
|
|||
# Common entry point to run JAPL's tests
|
||||
#
|
||||
# - Assumes "japl" binary in ../src/japl built with all debugging off
|
||||
# - Goes through all tests in (/tests/)
|
||||
# - Runs all tests in (/tests/)japl/ and checks their output (marked by `//output:{output}`)
|
||||
#
|
||||
|
||||
# go through all nim tests
|
||||
import multibyte
|
||||
testMultibyte()
|
||||
|
||||
|
||||
# go through all japl tests
|
||||
import os, strformat, times, re
|
||||
|
||||
proc compileExpectedOutput(path: string): string =
|
||||
for line in path.lines():
|
||||
if line =~ re"^.*//output:(.*)$":
|
||||
result &= matches[0] & "\n"
|
||||
|
||||
proc deepComp(left, right: string): tuple[same: bool, place: int] =
|
||||
result.same = true
|
||||
if left.high() != right.high():
|
||||
result.same = false
|
||||
for i in countup(0, left.high()):
|
||||
result.place = i
|
||||
if i > right.high():
|
||||
# already false bc of the len check at the beginning
|
||||
# already correct place bc it's updated every i
|
||||
return
|
||||
if left[i] != right[i]:
|
||||
result.same = false
|
||||
return
|
||||
|
||||
|
||||
var testsDir = "tests" / "japl"
|
||||
var japlExec = "src" / "japl"
|
||||
|
||||
# support running from both the japl root and the tests dir where it
|
||||
# resides
|
||||
var currentDir = getCurrentDir()
|
||||
if currentDir.lastPathPart() == "tests":
|
||||
testsDir = "japl"
|
||||
japlExec = ".." / japlExec
|
||||
|
||||
|
||||
let testResultsFile = open("testresults.txt", fmAppend)
|
||||
testResultsFile.writeLine(&"Executing tests at {$getTime()}")
|
||||
|
||||
# quick logging levels using procs
|
||||
proc log(msg: string) =
|
||||
testResultsFile.File.writeLine(&"[LOG] {msg}")
|
||||
echo msg
|
||||
|
||||
proc detail(msg: string) =
|
||||
testResultsFile.writeLine(&"[DETAIL] {msg}")
|
||||
|
||||
# Exceptions for tests that represent not-yet implemented behaviour
|
||||
var exceptions = @["all.jpl"]
|
||||
|
||||
log &"Running JAPL tests"
|
||||
log &"Looking for JAPL tests in {testsDir}"
|
||||
log &"Looking for JAPL executable at {japlExec}"
|
||||
if not fileExists(japlExec):
|
||||
log &"JAPL executable not found"
|
||||
quit(1)
|
||||
if not dirExists(testsDir):
|
||||
log &"Tests dir not found"
|
||||
quit(1)
|
||||
|
||||
|
||||
for file in walkDir(testsDir):
|
||||
block singularTest:
|
||||
for exc in exceptions:
|
||||
if exc == file.path.extractFilename:
|
||||
log &"Skipping {file.path} because it's on the exceptions list"
|
||||
break singularTest
|
||||
|
||||
log &"Running test {file.path}"
|
||||
if fileExists("testoutput.txt"):
|
||||
removeFile("testoutput.txt") # in case this crashed
|
||||
discard execShellCmd(&"{japlExec} {file.path} >>testoutput.txt")
|
||||
let expectedOutput = compileExpectedOutput(file.path).replace(re"(\n*)$", "")
|
||||
let realOutputFile = open("testoutput.txt", fmRead)
|
||||
let realOutput = realOutputFile.readAll().replace(re"([\n\r]*)$", "")
|
||||
realOutputFile.close()
|
||||
removeFile("testoutput.txt")
|
||||
let comparison = deepComp(expectedOutput, realOutput)
|
||||
if comparison.same:
|
||||
log &"Successful test {file.path}"
|
||||
else:
|
||||
detail &"Expected output:\n{expectedOutput}\n"
|
||||
detail &"Received output:\n{realOutput}\n"
|
||||
detail &"Mismatch at pos {comparison.place}"
|
||||
if comparison.place > expectedOutput.high() or
|
||||
comparison.place > realOutput.high():
|
||||
detail &"Length mismatch"
|
||||
else:
|
||||
detail &"Expected is '{expectedOutput[comparison.place]}' while received '{realOutput[comparison.place]}'"
|
||||
log &"Test failed {file.path}, check 'testresults.txt' for details"
|
||||
|
||||
testResultsFile.close()
|
Loading…
Reference in New Issue