Merge pull request #43 from Productive2/master

Fix for.jpl, testing improvements
This commit is contained in:
Mattia 2021-03-01 18:58:26 +01:00 committed by GitHub
commit 4a9f210e28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
60 changed files with 483 additions and 253 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} {'--stdout' if verbose else ''}", mode="run", shell=True, stderr=PIPE) process = run_command(f"{tests_path} {'-e' 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()}")
return False return False

View File

@ -2,8 +2,7 @@
// from nim itself (the nim compiler options are identical to those of // from nim itself (the nim compiler options are identical to those of
// production.json) // production.json)
{"flags": { {"flags": {
"gc": "none", "gc": "none"
"d": "danger"
}, },
"verbose": true, "verbose": true,
"override_config": true, "override_config": true,

View File

@ -2,8 +2,7 @@
// from nim itself (the nim compiler options are identical to those of // from nim itself (the nim compiler options are identical to those of
// production.json) // production.json)
{"flags": { {"flags": {
"gc": "none", "gc": "none"
"d": "danger"
}, },
"verbose": true, "verbose": true,
"override_config": true, "override_config": true,

View File

@ -36,6 +36,8 @@ import config
when isMainModule: when isMainModule:
import util/debug import util/debug
import types/methods import types/methods
when DEBUG_TRACE_COMPILER:
import terminal
type type
@ -212,7 +214,11 @@ proc emitByte(self: Compiler, byt: OpCode|uint8) =
## Emits a single bytecode instruction and writes it ## Emits a single bytecode instruction and writes it
## to the current chunk being compiled ## to the current chunk being compiled
when DEBUG_TRACE_COMPILER: when DEBUG_TRACE_COMPILER:
echo "DEBUG - Compiler: Emitting " & $byt & " (uint8 value of " & $(uint8 byt) & ")" write stdout, &"DEBUG - Compiler: Emitting {$byt} (uint8 value of {$(uint8 byt)}"
if byt.int() <= OpCode.high().int():
write stdout, &"; opcode value of {$byt.OpCode}"
write stdout, ")\n"
self.currentChunk.writeChunk(uint8 byt, self.parser.previous.line) self.currentChunk.writeChunk(uint8 byt, self.parser.previous.line)
@ -652,6 +658,10 @@ proc emitJump(self: Compiler, opcode: OpCode): int =
self.emitByte(opcode) self.emitByte(opcode)
self.emitByte(0xff) self.emitByte(0xff)
self.emitByte(0xff) self.emitByte(0xff)
when DEBUG_TRACE_COMPILER:
setForegroundColor(fgYellow)
write stdout, &"DEBUG - Compiler: emit jump @ {self.currentChunk.code.len-2}\n"
setForegroundColor(fgDefault)
return self.currentChunk.code.len - 2 return self.currentChunk.code.len - 2
@ -669,14 +679,23 @@ proc patchJump(self: Compiler, offset: int) =
## be jumped over, so the size of the if/else conditions ## be jumped over, so the size of the if/else conditions
## or loops is limited (hopefully 65 thousands and change ## or loops is limited (hopefully 65 thousands and change
## instructions are enough for everyone) ## instructions are enough for everyone)
when DEBUG_TRACE_COMPILER:
setForegroundColor(fgYellow)
write stdout, &"DEBUG - Compiler: patching jump @ {offset}"
let jump = self.currentChunk.code.len - offset - 2 let jump = self.currentChunk.code.len - offset - 2
if jump > (int uint16.high): if jump > (int uint16.high):
when DEBUG_TRACE_COMPILER:
setForegroundColor(fgDefault)
write stdout, "\n"
self.compileError("too much code to jump over") self.compileError("too much code to jump over")
else: else:
let casted = toDouble(jump) let casted = toDouble(jump)
self.currentChunk.code[offset] = casted[0] self.currentChunk.code[offset] = casted[0]
self.currentChunk.code[offset + 1] = casted[1] self.currentChunk.code[offset + 1] = casted[1]
when DEBUG_TRACE_COMPILER:
write stdout, &" points to {casted[0]}, {casted[1]} = {jump}\n"
setForegroundColor(fgDefault)
proc ifStatement(self: Compiler) = proc ifStatement(self: Compiler) =
## Parses if statements in a C-style fashion ## Parses if statements in a C-style fashion
@ -703,14 +722,22 @@ proc ifStatement(self: Compiler) =
proc emitLoop(self: Compiler, start: int) = proc emitLoop(self: Compiler, start: int) =
## Creates a loop and emits related instructions. ## Creates a loop and emits related instructions.
when DEBUG_TRACE_COMPILER:
setForegroundColor(fgYellow)
write stdout, &"DEBUG - Compiler: emitting loop at start {start} "
self.emitByte(OpCode.Loop) self.emitByte(OpCode.Loop)
var offset = self.currentChunk.code.len - start + 2 var offset = self.currentChunk.code.len - start + 2
if offset > (int uint16.high): if offset > (int uint16.high):
when DEBUG_TRACE_COMPILER:
setForegroundColor(fgDefault)
write stdout, "\n"
self.compileError("loop body is too large") self.compileError("loop body is too large")
else: else:
let offsetBytes = toDouble(offset) let offsetBytes = toDouble(offset)
self.emitByte(offsetBytes[0]) self.emitByte(offsetBytes[0])
self.emitByte(offsetBytes[1]) self.emitByte(offsetBytes[1])
when DEBUG_TRACE_COMPILER:
write stdout, &"pointing to {offsetBytes[0]}, {offsetBytes[1]} = {offset}\n"
proc endLooping(self: Compiler) = proc endLooping(self: Compiler) =
@ -720,14 +747,15 @@ proc endLooping(self: Compiler) =
if self.loop.loopEnd != -1: if self.loop.loopEnd != -1:
self.patchJump(self.loop.loopEnd) self.patchJump(self.loop.loopEnd)
self.emitByte(OpCode.Pop) self.emitByte(OpCode.Pop)
var i = self.loop.body
while i < self.currentChunk.code.len: for brk in self.loop.breaks:
if self.currentChunk.code[i] == uint OpCode.Break: when DEBUG_TRACE_COMPILER:
self.currentChunk.code[i] = uint8 OpCode.Jump setForegroundColor(fgYellow)
self.patchJump(i + 1) write stdout, &"DEBUG - Compiler: patching break at {brk}\n"
i += 3 setForegroundColor(fgDefault)
else: self.currentChunk.code[brk] = OpCode.Jump.uint8
i += 1 self.patchJump(brk + 1)
self.loop = self.loop.outer self.loop = self.loop.outer
@ -816,6 +844,7 @@ proc parseBreak(self: Compiler) =
self.emitByte(OpCode.Pop) self.emitByte(OpCode.Pop)
i -= 1 i -= 1
discard self.emitJump(OpCode.Break) discard self.emitJump(OpCode.Break)
self.loop.breaks.add(self.currentChunk.code.len() - 3)
proc parseAnd(self: Compiler, canAssign: bool) = proc parseAnd(self: Compiler, canAssign: bool) =

View File

@ -37,7 +37,7 @@ proc clear*(self: CallFrame): int =
inc result inc result
proc getView*(self: CallFrame): ptr ArrayList[ptr Obj] = proc getView*(self: CallFrame): ptr ArrayList[ptr Obj] =
result = self.stack[self.slot..self.stack.high()] result = self.stack[self.slot..self.stack.len()]
proc len*(self: CallFrame): int = proc len*(self: CallFrame): int =

View File

@ -21,3 +21,4 @@ type Loop* = ref object
alive*: bool alive*: bool
body*: int body*: int
loopEnd*: int loopEnd*: int
breaks*: seq[int]

View File

@ -83,7 +83,9 @@ 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, GreaterOrEqual, LessOrEqual} OpCode.Is, OpCode.As, OpCode.GreaterOrEqual,
OpCode.LessOrEqual, OpCode.Bor, OpCode.Band,
OpCode.Bnot}
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

@ -84,7 +84,7 @@ proc `[]`*[T](self: ptr ArrayList[T], slice: Hslice[int, int]): ptr ArrayList[T]
## of the slice ## of the slice
if self.length == 0: if self.length == 0:
raise newException(IndexDefect, "ArrayList index out of bounds") raise newException(IndexDefect, "ArrayList index out of bounds")
if slice.a notin 0..self.length - 1 or slice.b notin 0..self.length - 1: if slice.a notin 0..self.length - 1 or slice.b notin 0..self.length:
raise newException(IndexDefect, "ArrayList index out of bounds") raise newException(IndexDefect, "ArrayList index out of bounds")
result = newArrayList[T]() result = newArrayList[T]()
for i in countup(slice.a, slice.b - 1): for i in countup(slice.a, slice.b - 1):
@ -189,4 +189,4 @@ proc `$`*[T](self: ptr ArrayList[T]): string =
proc getIter*[T](self: ptr ArrayList[T]): Iterator = proc getIter*[T](self: ptr ArrayList[T]): Iterator =
## Returns the iterator object of the ## Returns the iterator object of the
## arraylist ## arraylist
result = allocate(Iterator, ) result = allocate(Iterator, )

View File

@ -20,17 +20,30 @@ import ../types/baseObject
import ../types/methods import ../types/methods
import ../types/arraylist import ../types/arraylist
import strformat import strformat
import terminal
import ../multibyte
proc printName(name: string) =
setForegroundColor(fgGreen)
write stdout, name
setForegroundColor(fgDefault)
proc nl =
write stdout, "\n"
proc simpleInstruction(name: string, index: int): int = proc simpleInstruction(name: string, index: int): int =
echo &"DEBUG - VM:\tInstruction -> {name}" write stdout, &"DEBUG - VM:\tInstruction -> "
printName(name)
nl()
return index + 1 return index + 1
proc byteInstruction(name: string, chunk: Chunk, offset: int): int = proc byteInstruction(name: string, chunk: Chunk, offset: int): int =
var slot = chunk.code[offset + 1] var slot = chunk.code[offset + 1]
echo &"DEBUG - VM:\tInstruction -> {name}, points to slot {slot}" write stdout, &"DEBUG - VM:\tInstruction -> "
printName(name)
write stdout, &", points to slot {slot}"
nl()
return offset + 2 return offset + 2
@ -39,7 +52,10 @@ proc constantInstruction(name: string, chunk: Chunk, offset: int): int =
var constantArray: array[3, uint8] = [chunk.code[offset + 1], chunk.code[offset + 2], chunk.code[offset + 3]] var constantArray: array[3, uint8] = [chunk.code[offset + 1], chunk.code[offset + 2], chunk.code[offset + 3]]
var constant: int var constant: int
copyMem(constant.addr, constantArray.addr, sizeof(constantArray)) copyMem(constant.addr, constantArray.addr, sizeof(constantArray))
echo &"DEBUG - VM:\tInstruction -> {name}, points to slot {constant}" write stdout, &"DEBUG - VM:\tInstruction -> "
printName(name)
write stdout, &", points to slot {constant}"
nl()
let obj = chunk.consts[constant] let obj = chunk.consts[constant]
echo &"DEBUG - VM:\tOperand -> {stringify(obj)}\nDEBUG - VM:\tValue kind -> {obj.kind}" echo &"DEBUG - VM:\tOperand -> {stringify(obj)}\nDEBUG - VM:\tValue kind -> {obj.kind}"
return offset + 4 return offset + 4
@ -47,9 +63,10 @@ proc constantInstruction(name: string, chunk: Chunk, offset: int): int =
proc jumpInstruction(name: string, chunk: Chunk, offset: int): int = proc jumpInstruction(name: string, chunk: Chunk, offset: int): int =
var jumpArray: array[2, uint8] = [chunk.code[offset + 1], chunk.code[offset + 2]] var jumpArray: array[2, uint8] = [chunk.code[offset + 1], chunk.code[offset + 2]]
var jump: int write stdout, &"DEBUG - VM:\tInstruction -> "
copyMem(jump.addr, jumpArray.addr, sizeof(uint16)) printName(name)
echo &"DEBUG - VM:\tInstruction -> {name}\nDEBUG - VM:\tJump size -> {jump}" write stdout, &"\nDEBUG - VM:\tJump size -> {jumpArray.fromDouble()} ( = {$jumpArray[0]}, {$jumpArray[1]})"
nl()
return offset + 3 return offset + 3

View File

@ -38,12 +38,16 @@ import types/typeutils
import types/function import types/function
import types/native import types/native
import types/arraylist import types/arraylist
import multibyte
# We always import it to # We always import it to
# avoid the compiler complaining # avoid the compiler complaining
# about functions not existing # about functions not existing
# in production builds # in production builds
import util/debug import util/debug
when DEBUG_TRACE_VM:
import terminal
type type
KeyboardInterrupt* = object of CatchableError KeyboardInterrupt* = object of CatchableError
@ -278,9 +282,7 @@ proc readBytes(self: CallFrame): int =
proc readShort(self: CallFrame): uint16 = proc readShort(self: CallFrame): uint16 =
## Reads a 16 bit number from the ## Reads a 16 bit number from the
## given frame's chunk ## given frame's chunk
let arr = [self.readByte(), self.readByte()] fromDouble([self.readByte(), self.readByte()])
copyMem(result.addr, unsafeAddr(arr), sizeof(uint16))
proc readConstant(self: CallFrame): ptr Obj = proc readConstant(self: CallFrame): ptr Obj =
## Reads a constant from the given ## Reads a constant from the given
@ -292,44 +294,49 @@ proc readConstant(self: CallFrame): ptr Obj =
proc showRuntime*(self: VM, frame: CallFrame, iteration: uint64) = when DEBUG_TRACE_VM:
## Shows debug information about the current proc showRuntime*(self: VM, frame: CallFrame, iteration: uint64) =
## state of the virtual machine ## Shows debug information about the current
let view = frame.getView() ## state of the virtual machine
stdout.write("DEBUG - VM: General information\n")
stdout.write(&"DEBUG - VM:\tIteration -> {iteration}\nDEBUG - VM:\tStack -> [") let view = frame.getView()
for i, v in self.stack: setForegroundColor(fgYellow)
stdout.write(stringify(v)) stdout.write("DEBUG - VM: General information\n")
if i < self.stack.high(): stdout.write(&"DEBUG - VM:\tIteration -> {iteration}\n")
stdout.write(", ") setForegroundColor(fgDefault)
stdout.write("]\nDEBUG - VM: \tGlobals -> {") stdout.write("DEBUG - VM:\tStack -> [")
for i, (k, v) in enumerate(self.globals.pairs()): for i, v in self.stack:
stdout.write(&"'{k}': {stringify(v)}") stdout.write(stringify(v))
if i < self.globals.len() - 1: if i < self.stack.high():
stdout.write(", ") stdout.write(", ")
stdout.write("}\nDEBUG - VM: Frame information\n") stdout.write("]\nDEBUG - VM: \tGlobals -> {")
stdout.write("DEBUG - VM:\tType -> ") for i, (k, v) in enumerate(self.globals.pairs()):
if frame.function.name == nil: stdout.write(&"'{k}': {stringify(v)}")
stdout.write("main\n") if i < self.globals.len() - 1:
else: stdout.write(", ")
stdout.write(&"function, '{frame.function.name.stringify()}'\n") stdout.write("}\nDEBUG - VM: Frame information\n")
echo &"DEBUG - VM:\tCount -> {self.frames.len()}" stdout.write("DEBUG - VM:\tType -> ")
echo &"DEBUG - VM:\tLength -> {view.len}" if frame.function.name == nil:
stdout.write("DEBUG - VM:\tTable -> ") stdout.write("main\n")
stdout.write("[") else:
for i, e in frame.function.chunk.consts: stdout.write(&"function, '{frame.function.name.stringify()}'\n")
stdout.write(stringify(e)) echo &"DEBUG - VM:\tCount -> {self.frames.len()}"
if i < len(frame.function.chunk.consts) - 1: echo &"DEBUG - VM:\tLength -> {view.len}"
stdout.write(", ") stdout.write("DEBUG - VM:\tTable -> ")
stdout.write("]\nDEBUG - VM:\tStack view -> ") stdout.write("[")
stdout.write("[") for i, e in frame.function.chunk.consts:
for i, e in view: stdout.write(stringify(e))
stdout.write(stringify(e)) if i < len(frame.function.chunk.consts) - 1:
if i < len(view) - 1: stdout.write(", ")
stdout.write(", ") stdout.write("]\nDEBUG - VM:\tStack view -> ")
stdout.write("]\n") stdout.write("[")
echo "DEBUG - VM: Current instruction" for i, e in view:
discard disassembleInstruction(frame.function.chunk, frame.ip - 1) stdout.write(stringify(e))
if i < len(view) - 1:
stdout.write(", ")
stdout.write("]\n")
echo "DEBUG - VM: Current instruction"
discard disassembleInstruction(frame.function.chunk, frame.ip - 1)
proc run(self: VM): InterpretResult = proc run(self: VM): InterpretResult =

View File

@ -1,6 +1,8 @@
/*
[Test: all] [Test: all]
[skip] [skip]
[source: mixed] */
//[source: mixed]
// Example file to test JAPL's syntax // Example file to test JAPL's syntax
// Mathematical expressions // Mathematical expressions
@ -140,5 +142,5 @@ mark.greet();
"implicit start"[:5]; // From 0 to 5 "implicit start"[:5]; // From 0 to 5
"hello" + " world"; // Strings are immutable! "hello" + " world"; // Strings are immutable!
"hello" * 3; //hellohellohello "hello" * 3; //hellohellohello
[end] //[end]
[end] //[end]

View File

@ -1,5 +1,5 @@
[Test: arithmetic] //[Test: arithmetic]
[source: mixed] //[source: mixed]
//int arithmetic //int arithmetic
print(7+5); //stdout:12 print(7+5); //stdout:12
@ -34,5 +34,5 @@ print(64/-64);//stdout:-1.0
print(8/0);//stdout:inf print(8/0);//stdout:inf
print(8/-0);//stdout:inf print(8/-0);//stdout:inf
print(-8/0);//stdout:-inf print(-8/0);//stdout:-inf
[end] //[end]
[end] //[end]

View File

@ -0,0 +1,8 @@
//[Test: assignment expressions]
//[source: mixed]
var x;
var a = x = 5;
print(x);//stdout:5
print(a);//stdout:5
//[end]
//[end]

View File

@ -1,5 +1,5 @@
[Test: bitwise] //[Test: bitwise]
[source: mixed] //[source: mixed]
print(~5 | 5);//stdout:-1 print(~5 | 5);//stdout:-1
print(1 | 2);//stdout:3 print(1 | 2);//stdout:3
print(1 & 2);//stdout:0 print(1 & 2);//stdout:0
@ -9,5 +9,5 @@ print(32 | 64);//stdout:96
print(96 & 32);//stdout:32 print(96 & 32);//stdout:32
print(~0);//stdout:-1 print(~0);//stdout:-1
print(~356);//stdout:-357 print(~356);//stdout:-357
[end] //[end]
[end] //[end]

View File

@ -1,5 +1,5 @@
[Test: booleans] //[Test: booleans]
[source: mixed] //[source: mixed]
print(2 or 3);//stdout:2 print(2 or 3);//stdout:2
print(2 and 3);//stdout:3 print(2 and 3);//stdout:3
print(false or true);//stdout:true print(false or true);//stdout:true
@ -21,5 +21,5 @@ 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 false));//stdout:true print(not (1 and false));//stdout:true
[end] //[end]
[end] //[end]

23
tests/japl/break.jpl Normal file
View File

@ -0,0 +1,23 @@
//[Test: breaks]
//[source:mixed]
var x = 5;
while (true) {
var a = 1;
x = x - a;
if (x < 0) {
break;
}
print(x);
}
//[end]
/*
[stdout]
4
3
2
1
0
[end]
[end]
*/

View File

@ -1,5 +1,7 @@
/*
[Test: callchain] [Test: callchain]
[source: mixed] */
//[source: mixed]
fun add2(x) fun add2(x)
{ {
return x + 2; return x + 2;
@ -14,8 +16,10 @@ fun mul2(x)
} }
print(add2(sub2(mul2(sub2(5))))); print(add2(sub2(mul2(sub2(5)))));
//5-2=3 //[end]
//3*2=6 /*
//stdout:6 [stdout]
6
[end] [end]
[end] [end]
*/

View File

@ -1,5 +1,5 @@
[Test: comparisons] //[Test: comparisons]
[source: mixed] //[source: mixed]
var x = 4; var x = 4;
var y = 5; var y = 5;
var z = 6; var z = 6;
@ -42,5 +42,25 @@ if (8 <= z)
print("15"); print("15");
else else
print("16");//stdout:16 print("16");//stdout:16
[end] //[end]
[end] //[end]
//[Test: multicomparisons]
//[skip]
//[source: mixed]
var x = 2 < 3 < 4;
print(x);//stdout:4
x = 1 > 3 < 5;
print(x);//stdout:false
x = 1 >= 0 >= 0;
print(x);//stdout:0
if ( 4 < x < 6 ) {
print("inside");
} else {
print("outside");//stdout:outside
}
//[end]
//[end]

View File

@ -1,5 +1,5 @@
[Test: compile_time_intern] //[Test: compile_time_intern]
[source: mixed] //[source: mixed]
//compile time interning //compile time interning
var a = "hello"; var a = "hello";
@ -11,6 +11,6 @@ print(a is b);//stdout:true
var x = "ex"; var x = "ex";
var y = "ey"; var y = "ey";
print(x is y);//stdout:false print(x is y);//stdout:false
[end] //[end]
[end] //[end]

11
tests/japl/defaults.jpl Normal file
View File

@ -0,0 +1,11 @@
//[Test: defaults]
//[skip]
//[source:mixed]
var a = 3;
fun test(b = a) {
print (b);
}
a = 9;
test();//stdout:3
//[end]
//[end]

View File

@ -1,8 +1,8 @@
[Test: read_in_own_init_regex] //[Test: read_in_own_init_regex]
[source: raw] //[source: raw]
var a = 1; { var a = a; } var a = 1; { var a = a; }
[end] [end]
[stderr: re] [stderr: re]
[[^\-]*-> cannot read local variable in its own initializer [[^\-]*-> cannot read local variable in its own initializer
[end] //[end]
[end] //[end]

View File

@ -1,10 +1,12 @@
[Test: undefname_raw] //[Test: undefname_raw]
[source: raw] //[source: raw]
var a = b; var a = b;
[end] //[end]
/*
[stderr] [stderr]
An unhandled exception occurred, traceback below: An unhandled exception occurred, traceback below:
File '''', line 1, in <module>: File '''', line 1, in <module>:
ReferenceError: undefined name 'b' ReferenceError: undefined name 'b'
[end] [end]
[end] */
//[end]

View File

@ -1,10 +1,12 @@
[Test: unsup_binary_instr] //[Test: unsup_binary_instr]
[source: raw] //[source: raw]
var a = 2 + "hey"; var a = 2 + "hey";
[end] //[end]
/*
[stderr] [stderr]
An unhandled exception occurred, traceback below: An unhandled exception occurred, traceback below:
File '''', line 1, in <module>: File '''', line 1, in <module>:
TypeError: unsupported binary operator '+' for objects of type 'integer' and 'string' TypeError: unsupported binary operator '+' for objects of type 'integer' and 'string'
[end] [end]
[end] */
//[end]

View File

@ -1,5 +1,5 @@
[Test: problem1] //[Test: problem1]
[source: mixed] //[source: mixed]
// Task: find the multiples of 3 and 5 below 1000, find their sum // Task: find the multiples of 3 and 5 below 1000, find their sum
var sum = 0; var sum = 0;
@ -11,5 +11,5 @@ for (var x = 3; x < 1001; x = x + 1)
} }
} }
print(sum);//stdout:234168 print(sum);//stdout:234168
[end] //[end]
[end] //[end]

View File

@ -1,5 +1,5 @@
[Test: problem2] //[Test: problem2]
[source: mixed] //[source: mixed]
// Sum of even valued fibonacci numbers that don't exceed 4M // Sum of even valued fibonacci numbers that don't exceed 4M
var a = 1; var a = 1;
@ -16,5 +16,5 @@ while (a < 4000000)
b = c; b = c;
} }
print(sum);//stdout:4613732 print(sum);//stdout:4613732
[end] //[end]
[end] //[end]

View File

@ -1,6 +1,6 @@
[Test: problem4] //[Test: problem4]
[skip] //[skip]
[source: mixed] //[source: mixed]
// Find the largest palindrome that is a product of two 3 digit numbers // Find the largest palindrome that is a product of two 3 digit numbers
fun isPalindrome(n) fun isPalindrome(n)
@ -66,5 +66,5 @@ for (var i = 100; i < 1000; i = i + 1)
} }
} }
print(largest); print(largest);
[end] //[end]
[end] //[end]

View File

@ -1,5 +1,7 @@
[Test: fibonacci]
[source: raw] //[Test: fibonacci]
//[source: raw]
fun fib(n) { fun fib(n) {
if (n < 2) if (n < 2)
return n; return n;
@ -14,7 +16,8 @@ print(fib(6));
print(fib(7)); print(fib(7));
print(fib(8)); print(fib(8));
print(fib(9)); print(fib(9));
[end] //[end]
/*
[stdout] [stdout]
1 1
1 1
@ -26,4 +29,5 @@ print(fib(9));
21 21
34 34
[end] [end]
[end] */
//[end]

View File

@ -1,10 +1,10 @@
[Test: for] //[Test: for]
[source: mixed] //[source: mixed]
for (var x = 0; x < 2; x = x + 1) for (var x = 0; x < 2; x = x + 1)
{ {
print(x); print(x);
//stdout:0 //stdout:0
//stdout:1 //stdout:1
} }
[end] //[end]
[end] //[end]

View File

@ -1,6 +1,6 @@
[Test: forwithfunction] //[Test: forwithfunction]
[skip] //[skip]
[source: mixed] //[source: mixed]
var y = 0; //a global to keep track of state var y = 0; //a global to keep track of state
//does not need closures for this to work yet //does not need closures for this to work yet
@ -84,5 +84,5 @@ for (var i = 0; i != -1; i = next(i))
// y = 9 // y = 9
//stdout:10 //stdout:10
// y = 10 // y = 10
[end] //[end]
[end] //[end]

View File

@ -1,15 +1,17 @@
[Test: hellojapl] //[Test: hellojapl]
[source: mixed] //[source: mixed]
print("Hello, JAPL."); print("Hello, JAPL.");
//stdout:Hello, JAPL. //stdout:Hello, JAPL.
[end] //[end]
[end] //[end]
[Test: hello_second_way] //[Test: hello_second_way]
[source: raw] //[source: raw]
print("Hello, JAPL."); print("Hello, JAPL.");
[end] //[end]
/*
[stdout] [stdout]
Hello, JAPL. Hello, JAPL.
[end] [end]
[end] */
//[end]

View File

@ -1,5 +1,5 @@
[Test: if] //[Test: if]
[source: mixed] //[source: mixed]
var x = 5; var x = 5;
if (x > 2) if (x > 2)
{ {
@ -28,5 +28,5 @@ if (2 == x)
print("2"); print("2");
else else
print("not 2");//stdout:not 2 print("not 2");//stdout:not 2
[end] //[end]
[end] //[end]

View File

@ -1,5 +1,5 @@
[Test: ifchain] //[Test: ifchain]
[source: mixed] //[source: mixed]
fun printInt(x) { fun printInt(x) {
if (x == 1) if (x == 1)
print("one"); print("one");
@ -22,5 +22,5 @@ x = 7;
printInt(x); printInt(x);
x = 1; x = 1;
printInt(x);//stdout:one printInt(x);//stdout:one
[end] //[end]
[end] //[end]

View File

@ -1,8 +1,10 @@
/*
[Test: inputtesttwo] [Test: inputtesttwo]
[source: raw] */
//[source: raw]
print(readLine()); print(readLine());
[end] //[end]
/*
[stdin] [stdin]
Hello world! Hello world!
[end] [end]
@ -11,3 +13,4 @@ Hello world!
Hello world! Hello world!
[end] [end]
[end] [end]
*/

View File

@ -1,5 +1,5 @@
[Test: is] //[Test: is]
[source:mixed] //[source:mixed]
var x = 4; var x = 4;
var y = x; var y = x;
@ -21,5 +21,5 @@ print((l is z) is l);//stdout:true
var k; var k;
print(k is nil);//stdout:true print(k is nil);//stdout:true
[end] //[end]
[end] //[end]

View File

@ -1,21 +0,0 @@
[Test: lambdachain]
[source: mixed]
var add2 = lambda(x)
{
return x + 2;
};
var sub2 = lambda(x)
{
return x - 2;
};
var mul2 = lambda(x)
{
return x * 2;
};
print(add2(sub2(mul2(sub2(5)))));
//5-2=3
//3*2=6
//stdout:6
[end]
[end]

50
tests/japl/lambdas.jpl Normal file
View File

@ -0,0 +1,50 @@
/*
[Test: lambdachain]
*/
//[source: raw]
var add2 = lambda(x)
{
return x + 2;
};
var sub2 = lambda(x)
{
return x - 2;
};
var mul2 = lambda(x)
{
return x * 2;
};
print(add2(sub2(mul2(sub2(5)))));
//[end]
/*
[stdout]
6
[end]
[end]
[Test: simple lambdas]
*/
//[source: raw]
var identity = lambda(x) { return x; };
var comparison = lambda(x, y) {
if (x > y) {
return x;
} else {
return y;
}
};
var max = lambda(x, y, z) { return identity(comparison(comparison(x, y), z)); };
print(max(1, 5, 6));
print(max(6, 2, 9));
print(max(1.3, 7, 9.0));
print(max(-4, 3, 2));
//[end]
/*
[stdout]
6
9
9.0
3
[end]
[end]
*/

View File

@ -1,5 +1,5 @@
[Test: constant_long] //[Test: constant_long]
[source: mixed] //[source: mixed]
// Test for constants // Test for constants
var v_1 = 1; var v_1 = 1;
@ -130,5 +130,5 @@ var v_125 = 1;
var v_126 = 1; var v_126 = 1;
var v_127 = 1; var v_127 = 1;
var v_128 = 1; var v_128 = 1;
[end] //[end]
[end] //[end]

View File

@ -1,5 +1,5 @@
[Test: glob_assgn_read] //[Test: glob_assgn_read]
[source: mixed] //[source: mixed]
var a0 = 451; var a0 = 451;
var a1 = 5098; var a1 = 5098;
var a2 = 469; var a2 = 469;
@ -1501,5 +1501,5 @@ print(a151);//stdout:4839
print(a975);//stdout:7651 print(a975);//stdout:7651
print(a7);//stdout:2979 print(a7);//stdout:2979
print(a661);//stdout:8235 print(a661);//stdout:8235
[end] //[end]
[end] //[end]

View File

@ -1,5 +1,5 @@
[Test: glob_with_sets] //[Test: glob_with_sets]
[source: mixed] //[source: mixed]
var a0 = 829; var a0 = 829;
var a1 = 6820; var a1 = 6820;
var a2 = 114; var a2 = 114;
@ -5238,5 +5238,5 @@ print(a87);//stdout:1282
print(a445);//stdout:1726 print(a445);//stdout:1726
print(a790);//stdout:1140 print(a790);//stdout:1140
print(a961);//stdout:1708 print(a961);//stdout:1708
[end] //[end]
[end] //[end]

View File

@ -1,5 +1,5 @@
[Test: loc_assgn_read] //[Test: loc_assgn_read]
[source: mixed] //[source: mixed]
{ {
var a0 = 9103; var a0 = 9103;
var a1 = 4565; var a1 = 4565;
@ -1503,5 +1503,5 @@ print(a722);//stdout:5380
print(a538);//stdout:8625 print(a538);//stdout:8625
print(a809);//stdout:4506 print(a809);//stdout:4506
} }
[end] //[end]
[end] //[end]

View File

@ -1,5 +1,5 @@
[Test: loc_with_sets] //[Test: loc_with_sets]
[source: mixed] //[source: mixed]
{ {
var a0 = 8313; var a0 = 8313;
var a1 = 3509; var a1 = 3509;
@ -5186,5 +5186,5 @@ print(a380);//stdout:2145
print(a125);//stdout:4280 print(a125);//stdout:4280
print(a55);//stdout:6992 print(a55);//stdout:6992
} }
[end] //[end]
[end] //[end]

View File

@ -1,4 +1,4 @@
/*
[; This is a comment [; This is a comment
This is a comment as well. This is a comment as well.
@ -17,3 +17,4 @@ the test builder, but it should be a test with
the name "". the name "".
[end] [end]
*/

View File

@ -1,19 +1,21 @@
/*
[test: mixed] [test: mixed]
[source: mixed] */
//[source: mixed]
print("Hello", readLine()); print("Hello", readLine());
//stdout:Hello world //stdout:Hello world
//stdin:world //stdin:world
print("aaaaaa"); print("aaaaaa");
//stdoutre:a* //matchout:a*
printErr("Hello", readLine()); printErr("Hello", readLine());
//stderr:Hello error //stderr:Hello error
//stdin:error //stdin:error
printErr("bbbbbb"); printErr("bbbbbb");
//stderrre:b* //matcherr:b*
[end] //[end]
[end] //[end]

View File

@ -1,12 +1,13 @@
[Test: nw] //[Test: nw]
[source] //[source]
print("hey"); print("hey");
print("second line"); print("second line");
printErr("hey there"); printErr("hey there");
print("abcde"); print("abcde");
printErr("12345"); printErr("12345");
printErr("0123456789."); printErr("0123456789.");
[end] //[end]
/*
[stdout: nw] [stdout: nw]
hey hey
second line second line
@ -21,4 +22,5 @@ printErr("0123456789.");
[[0-9]* [[0-9]*
[0-9]*. [0-9]*.
[end] [end]
[end] */
//[end]

View File

@ -1,11 +1,12 @@
[Test: raw] //[Test: raw]
[source: raw] //[source: raw]
print("Hi", readLine()); print("Hi", readLine());
print("aaaaaaa"); print("aaaaaaa");
printErr("Bye", readLine()); printErr("Bye", readLine());
printErr("bbbbbbb"); printErr("bbbbbbb");
//stdout:This is not a part of the expected output //stdout:This is not a part of the expected output
[end] //[end]
/*
[stdin] [stdin]
person person
very important person very important person
@ -23,3 +24,4 @@ Bye very important person
b* b*
[end] [end]
[end] [end]
*/

View File

@ -1,7 +1,8 @@
[test: skipped] //[test: skipped]
[skip] //[skip]
/*
[stdout] [stdout]
Hello this text won't be matched. Hello this text won't be matched.
[end] [end]
[end] */
//[end]

View File

@ -1,9 +1,11 @@
[Test: nan] //[Test: nan]
[source: raw] //[source: raw]
print((5/0)*0); print((5/0)*0);
[end] //[end]
/*
[stdout] [stdout]
nan nan
[end] [end]
[end] */
//[end]

13
tests/japl/nested.jpl Normal file
View File

@ -0,0 +1,13 @@
//[Test: nested multiline comments]
//[source: raw]
/* first
/* second
/* third
/* fourth
*/
*/
*/
*/
//[end]
//[end]

13
tests/japl/procedures.jpl Normal file
View File

@ -0,0 +1,13 @@
//[Test: functions without parentheses]
//[source: mixed]
fun sayhi {
print("hi");
}
fun saybye {
print("bye");
}
sayhi();//stdout:hi
saybye();//stdout:bye
//[end]
//[end]

View File

@ -1,5 +1,5 @@
[Test: reassignment] //[Test: reassignment]
[source: mixed] //[source: mixed]
{ {
var x = 5; var x = 5;
var y = x; var y = x;
@ -25,5 +25,5 @@ fun resetter(x) {
var q = 5; var q = 5;
resetter(q);//stdout:7 resetter(q);//stdout:7
print(q);//stdout:5 print(q);//stdout:5
[end] //[end]
[end] //[end]

View File

@ -1,6 +1,6 @@
[Test: runtimeinterning] //[Test: runtimeinterning]
[skip] //[skip]
[source: mixed] //[source: mixed]
//runtime interning //runtime interning
var f = "leafy"; var f = "leafy";
@ -14,5 +14,5 @@ print(h is j);//stdout:true
var x = "ex"; var x = "ex";
var y = "ey"; var y = "ey";
print(x is y);//stdout:false print(x is y);//stdout:false
[end] //[end]
[end] //[end]

View File

@ -1,5 +1,5 @@
[Test: shadowing] //[Test: shadowing]
[source: mixed] //[source: mixed]
//similar to vars.jpl, but more focused on shadowing //similar to vars.jpl, but more focused on shadowing
// simple shadowing // simple shadowing
@ -76,5 +76,5 @@ eat();//stdout:nom nom nom
print(eat);//stdout:5 print(eat);//stdout:5
} }
eat();//stdout:nom nom nom eat();//stdout:nom nom nom
[end] //[end]
[end] //[end]

View File

@ -1,5 +1,5 @@
[Test: strings] //[Test: strings]
[source: mixed] //[source: mixed]
var left = "left"; var left = "left";
var right = "right"; var right = "right";
var directions = left + " " + right; var directions = left + " " + right;
@ -12,5 +12,15 @@ left = left + " side";
print(left);//stdout:left side print(left);//stdout:left side
right = "side: " + right; right = "side: " + right;
print(right);//stdout:side: right print(right);//stdout:side: right
[end] //[end]
[end] //[end]
//[Test: string slicing]
//[skip]
//[source: mixed]
var longstring = "a very long string that will be sliced";
var part = longstring[0:5];
print(part);//stdout:a ver
var part2 = longstring[0..4];
print(part2);//stdout:a ver
//[end]
//[end]

View File

@ -1,5 +1,5 @@
[Test: vars] //[Test: vars]
[source: mixed] //[source: mixed]
var x = 1; var x = 1;
var y = 2; var y = 2;
print(x);//stdout:1 print(x);//stdout:1
@ -30,5 +30,5 @@ longName = "hello";
print(longName); //stdout:hello print(longName); //stdout:hello
longName = longName + " world"; longName = longName + " world";
print(longName); //stdout:hello world print(longName); //stdout:hello world
[end] //[end]
[end] //[end]

View File

@ -1,5 +1,5 @@
[Test: while] //[Test: while]
[source: mixed] //[source: mixed]
var x = 5; var x = 5;
while (x > 0) while (x > 0)
{ {
@ -20,5 +20,5 @@ while (x < 10)
string = string + "A"; string = string + "A";
} }
print(string);//stdout:hAAAAAAAAAA print(string);//stdout:hAAAAAAAAAA
[end] //[end]
[end] //[end]

View File

@ -152,6 +152,7 @@ Debug output flags:
Test behavior flags: Test behavior flags:
-j:<parallel test count> (or --jobs:<parallel test count>) to specify number of tests to run parallel -j:<parallel test count> (or --jobs:<parallel test count>) to specify number of tests to run parallel
-t:<test file or dir> (or --test:<path> or --tests:<path>) to specify where tests are -t:<test file or dir> (or --test:<path> or --tests:<path>) to specify where tests are
--timeout <timeout in seconds> to specify when to kill tests
-f (or --force) will run skipped tests -f (or --force) will run skipped tests
Miscellaneous flags: Miscellaneous flags:
-h (or --help) displays this help message -h (or --help) displays this help message

View File

@ -107,6 +107,7 @@ type Buffer* = ref object
## Represents an updateable line on the terminal ## Represents an updateable line on the terminal
contents: string contents: string
previous: string previous: string
termwidth: int
proc newBuffer*: Buffer = proc newBuffer*: Buffer =
## Creates a Buffer, hides the cursor ## Creates a Buffer, hides the cursor
@ -131,6 +132,8 @@ proc updateProgressBar*(buf: Buffer, text: string, total: int, current: int) =
let w = terminalWidth() let w = terminalWidth()
if w > newline.len(): if w > newline.len():
newline &= " ".repeat(w - newline.len() - 1) newline &= " ".repeat(w - newline.len() - 1)
else:
newline = newline[0..w-2]
buf.contents = newline buf.contents = newline
proc clearLineAndWrite(text: string, oldsize: int) = proc clearLineAndWrite(text: string, oldsize: int) =

View File

@ -30,8 +30,8 @@ proc parseModalLine(line: string): tuple[modal: bool, mode: string, detail: stri
## if comment is true, the returned value has to be ignored ## if comment is true, the returned value has to be ignored
# when non modal, mode becomes the line # when non modal, mode becomes the line
# when comment is true, it must not do anything to whenever it is exported # when comment is true, it must not do anything to whenever it is exported
let line = line
# initialize result # initialize result
var start = 0
result.modal = false result.modal = false
result.mode = "" result.mode = ""
result.detail = "" result.detail = ""
@ -48,14 +48,21 @@ proc parseModalLine(line: string): tuple[modal: bool, mode: string, detail: stri
result.modal = true result.modal = true
return result return result
result.modal = true result.modal = true
start = 1
# not modal line early return # not modal line early return
elif line.len() >= 3 and line[0..2] == "//[":
if line.len() > 3:
result.modal = true
start = 3
else:
fatal "Invalid line //[, no mode defined."
else: else:
result.mode = line result.mode = line
return result return result
# normal modal line: # normal modal line:
var colon = false # if there has been a colon already var colon = false # if there has been a colon already
for i in countup(0, line.high()): for i in countup(start, line.high()):
let ch = line[i] let ch = line[i]
if ch in Letters or ch in Digits or ch in {'_', '-'}: if ch in Letters or ch in Digits or ch in {'_', '-'}:
# legal characters # legal characters
@ -187,8 +194,7 @@ proc buildTests*(testDir: string): seq[Test] =
else: else:
fatal "test dir/file doesn't exist" fatal "test dir/file doesn't exist"
for candidateObj in walkDir(testDir): for kind, candidate in walkDir(testDir):
let candidate = candidateObj.path
if dirExists(candidate): if dirExists(candidate):
log(LogLevel.Debug, &"Descending into dir {candidate}") log(LogLevel.Debug, &"Descending into dir {candidate}")
result &= buildTests(candidate) result &= buildTests(candidate)

View File

@ -36,16 +36,18 @@ Must not contain a BOM. Line endings must be a single
### Mode syntax ### Mode syntax
The modes are constructed from modelines, The modes are constructed from modelines,
which are lines starting with the character '['. which are lines starting with the character '[', or alternatively
they can also start with the sequence "//[".
Modelines also have to be closed by a ']' character Modelines also have to be closed by a ']' character
on the end of this line. These lines may not contain on the end of this line. These lines may not contain
whitespace before the opening '[' nor after then ending whitespace before the opening '[' or "//[" nor after then ending
']' characters. Inside the brackets, letters (case ']' characters. Inside the brackets, letters (case
insensitive), numbers, underscores and dashes form\ insensitive), numbers, underscores and dashes form\
a name describing what the modeline does. a name describing what the modeline does.
``` ```
[ name ] [ name ]
//[ name ]
``` ```
Optionally, an argument may be passed, which is Optionally, an argument may be passed, which is
@ -53,6 +55,7 @@ separated by a colon.
``` ```
[ name : detail ] [ name : detail ]
//[name: detail]
``` ```
Whitespace inside the brackets is ignored (even inside Whitespace inside the brackets is ignored (even inside
@ -144,13 +147,13 @@ added to add lines to the expected stdout/stderr or
the stdin of the test using the legacy test format. the stdin of the test using the legacy test format.
They are defined by the sequences `//stdout:`, They are defined by the sequences `//stdout:`,
`//stderr:`, `//stdin:`, `//stdoutre:` and `//stderr:`, `//stdin:`, `//matchout:` and
`//stderrre:`. Every character after the colon and `//matcherr:`. Every character after the colon and
before the end of the line is appended to the respective before the end of the line is appended to the respective
field of the test. `stdout` adds a raw line to be field of the test. `stdout` adds a raw line to be
matched to the expected stdout of the test. `stdoutre` matched to the expected stdout of the test. `matchout`
adds a regex to match a line of the stdout of the test. adds a regex to match a line of the stdout of the test.
`stderr` and `stderrre` are the stderr equivalents. `stderr` and `matcherr` are the stderr equivalents.
`stdin` adds a line to the stdin that the JAPL source `stdin` adds a line to the stdin that the JAPL source
can read from. can read from.
@ -209,3 +212,10 @@ Hello there
Coming soon. Coming soon.
# Best practices
Tests should be written so that they are valid jpl code. The test title
and modes surrounding source code should be prefixed with `//`. Stdin/stdout
and other raw non-jpl sources should be inside `/* */` blocks. Single line
commands such as skips should be either prefixed with `//` or inside a `/* */`
block.

View File

@ -71,10 +71,10 @@ proc compileExpectedOutput(source: string, rawkw: string, rekw: string): seq[Exp
result &= genEL(matches[0], ExpectedLineKind.Regex) result &= genEL(matches[0], ExpectedLineKind.Regex)
proc compileExpectedOutput(source: string): seq[ExpectedLine] = proc compileExpectedOutput(source: string): seq[ExpectedLine] =
compileExpectedOutput(source, "stdout", "stdoutre") compileExpectedOutput(source, "stdout", "matchout")
proc compileExpectedError(source: string): seq[ExpectedLine] = proc compileExpectedError(source: string): seq[ExpectedLine] =
compileExpectedOutput(source, "stderr", "stderrre") compileExpectedOutput(source, "stderr", "matcherr")
proc compileInput(source: string): string = proc compileInput(source: string): string =
for line in source.split('\n'): for line in source.split('\n'):