cleanups and more test cases
This commit is contained in:
@@ -1,8 +0,0 @@
|
||||
|
||||
all: tester
|
||||
|
||||
libnoja.a:
|
||||
make -B -C .. tests/libnoja.a BUILD_MODE=RELEASE OUTDIR=tests
|
||||
|
||||
tester: tester.c libnoja.a
|
||||
gcc $^ -o $@ -Wall -Wextra -lm -g
|
||||
@@ -1,201 +0,0 @@
|
||||
|
||||
from random import randint
|
||||
|
||||
NODE_KINDS = ['expr', 'if-else', 'while', 'do-while', 'return', 'comp', 'func']
|
||||
EXPR_KINDS = ['int', 'float', 'string', 'array', 'object']
|
||||
|
||||
def generate_ident():
|
||||
return 'Apple'
|
||||
|
||||
def generate_string_expr_node():
|
||||
return 'Hello, world!'
|
||||
|
||||
def generate_node(kind=None, depth=0):
|
||||
|
||||
if kind == None and depth > 3:
|
||||
return {
|
||||
'kind': 'comp',
|
||||
'body': []
|
||||
}
|
||||
|
||||
if kind == None:
|
||||
kind = NODE_KINDS[randint(0, len(NODE_KINDS)-1)]
|
||||
|
||||
assert kind in NODE_KINDS
|
||||
|
||||
if kind == 'expr':
|
||||
|
||||
return generate_expr_node(None, depth+1)
|
||||
|
||||
elif kind == 'if-else':
|
||||
|
||||
branches = randint(1, 2)
|
||||
assert branches in [1, 2]
|
||||
|
||||
if branches == 1:
|
||||
return {
|
||||
'kind': 'if-else',
|
||||
'cond': generate_expr_node(None, depth+1),
|
||||
'if-case': generate_node(None, depth+1)
|
||||
}
|
||||
else:
|
||||
return {
|
||||
'kind': 'if-else',
|
||||
'cond': generate_expr_node(None, depth+1),
|
||||
'if-case': generate_node(None, depth+1),
|
||||
'else-case': generate_node(None, depth+1)
|
||||
}
|
||||
|
||||
elif kind == 'while':
|
||||
|
||||
return {
|
||||
'kind': 'while',
|
||||
'cond': generate_expr_node(None, depth+1),
|
||||
'body': generate_node(None, depth+1)
|
||||
}
|
||||
|
||||
elif kind == 'do-while':
|
||||
|
||||
return {
|
||||
'kind': 'do-while',
|
||||
'cond': generate_expr_node(None, depth+1),
|
||||
'body': generate_node(None, depth+1)
|
||||
}
|
||||
|
||||
elif kind == 'return':
|
||||
|
||||
return {
|
||||
'kind': 'return',
|
||||
'value': generate_expr_node(None, depth+1)
|
||||
}
|
||||
|
||||
elif kind == 'comp':
|
||||
|
||||
n = randint(0, 3)
|
||||
|
||||
return {
|
||||
'kind': 'comp',
|
||||
'body': [generate_node(None, depth+1) for i in range(n)]
|
||||
}
|
||||
|
||||
elif kind == 'func':
|
||||
|
||||
argc = randint(0, 3)
|
||||
|
||||
return {
|
||||
'kind': 'func',
|
||||
'name': generate_ident(),
|
||||
'args': [generate_ident() for i in range(argc)],
|
||||
'body': generate_node(None, depth+1)
|
||||
}
|
||||
|
||||
def generate_expr_node(expr_kind=None, depth=0):
|
||||
|
||||
if expr_kind == None:
|
||||
expr_kind = EXPR_KINDS[randint(0, len(EXPR_KINDS)-1)]
|
||||
|
||||
assert expr_kind in EXPR_KINDS
|
||||
|
||||
if depth > 3:
|
||||
expr_kind = ['int', 'float', 'string'][randint(0, 2)]
|
||||
|
||||
if expr_kind == 'int':
|
||||
|
||||
return {
|
||||
'kind': 'expr',
|
||||
'expr-kind': 'int',
|
||||
'value': randint(0, 100)
|
||||
}
|
||||
|
||||
elif expr_kind == 'float':
|
||||
|
||||
return {
|
||||
'kind': 'expr',
|
||||
'expr-kind': 'float',
|
||||
'value': float(str(randint(0, 100)) + '.' + str(randint(0, 100)))
|
||||
}
|
||||
|
||||
elif expr_kind == 'string':
|
||||
|
||||
return {
|
||||
'kind': 'expr',
|
||||
'expr-kind': 'string',
|
||||
'value': generate_string_expr_node()
|
||||
}
|
||||
|
||||
elif expr_kind == 'array':
|
||||
|
||||
n = randint(0, 3)
|
||||
|
||||
return {
|
||||
'kind': 'expr',
|
||||
'expr-kind': 'array',
|
||||
'items': [generate_expr_node(None, depth+1) for i in range(n)]
|
||||
}
|
||||
|
||||
elif expr_kind == 'object':
|
||||
|
||||
n = randint(0, 3)
|
||||
|
||||
return {
|
||||
'kind': 'expr',
|
||||
'expr-kind': 'object',
|
||||
'items': [(generate_expr_node(None, depth+1), generate_expr_node(None, depth+1)) for i in range(n)]
|
||||
}
|
||||
|
||||
def serialize_expr(node):
|
||||
|
||||
if node['expr-kind'] == 'int':
|
||||
|
||||
return str(node['value'])
|
||||
|
||||
elif node['expr-kind'] == 'float':
|
||||
|
||||
return str(node['value'])
|
||||
|
||||
elif node['expr-kind'] == 'string':
|
||||
|
||||
return '"' + node['value'] + '"'
|
||||
|
||||
elif node['expr-kind'] == 'array':
|
||||
|
||||
return '[' + ', '.join([serialize_expr(item) for item in node['items']]) + ']'
|
||||
|
||||
elif node['expr-kind'] == 'object':
|
||||
|
||||
return '{' + ', '.join([serialize_expr(item[0]) + ': ' + serialize_expr(item[1]) for item in node['items']]) + '}'
|
||||
|
||||
def serialize(node):
|
||||
|
||||
if node['kind'] == 'expr':
|
||||
|
||||
return serialize_expr(node) + ';'
|
||||
|
||||
elif node['kind'] == 'if-else':
|
||||
|
||||
if 'else-case' in node:
|
||||
return 'if ' + serialize_expr(node['cond']) + ': ' + serialize(node['if-case']) + ' else ' + serialize(node['else-case'])
|
||||
else:
|
||||
return 'if ' + serialize_expr(node['cond']) + ': ' + serialize(node['if-case'])
|
||||
|
||||
elif node['kind'] == 'while':
|
||||
|
||||
return 'while ' + serialize_expr(node['cond']) + ': ' + serialize(node['body'])
|
||||
|
||||
elif node['kind'] == 'do-while':
|
||||
|
||||
return 'do ' + serialize(node['body']) + ' while ' + serialize_expr(node['cond']) + ';'
|
||||
|
||||
elif node['kind'] == 'return':
|
||||
|
||||
return 'return ' + serialize_expr(node['value']) + ';'
|
||||
|
||||
elif node['kind'] == 'comp':
|
||||
|
||||
return '{' + ''.join([serialize(stmt) for stmt in node['body']]) + '}'
|
||||
|
||||
elif node['kind'] == 'func':
|
||||
|
||||
return 'fun ' + node['name'] + '(' + ', '.join(node['args']) + ') ' + serialize(node['body'])
|
||||
|
||||
print(serialize(generate_node()))
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
#source
|
||||
|
||||
return none;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHNNE;
|
||||
RETURN 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
#source
|
||||
|
||||
{}
|
||||
|
||||
#bytecode
|
||||
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
#source
|
||||
|
||||
-1;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHINT 1;
|
||||
NEG;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
#source
|
||||
|
||||
+1;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHINT 1;
|
||||
POS;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
#source
|
||||
|
||||
not 1;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHINT 1;
|
||||
NOT;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
#source
|
||||
|
||||
1 == 2;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHINT 1;
|
||||
PUSHINT 2;
|
||||
EQL;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
#source
|
||||
|
||||
1 != 2;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHINT 1;
|
||||
PUSHINT 2;
|
||||
NQL;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
#source
|
||||
|
||||
1 < 2;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHINT 1;
|
||||
PUSHINT 2;
|
||||
LSS;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
#source
|
||||
|
||||
1 > 2;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHINT 1;
|
||||
PUSHINT 2;
|
||||
GRT;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
#source
|
||||
|
||||
1 <= 2;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHINT 1;
|
||||
PUSHINT 2;
|
||||
LEQ;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
#source
|
||||
|
||||
1 >= 2;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHINT 1;
|
||||
PUSHINT 2;
|
||||
GEQ;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
#source
|
||||
|
||||
1 and 2;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHINT 1;
|
||||
PUSHINT 2;
|
||||
AND;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
#source
|
||||
|
||||
1 or 2;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHINT 1;
|
||||
PUSHINT 2;
|
||||
OR;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
#source
|
||||
|
||||
1 + 2 * 3;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHINT 1;
|
||||
PUSHINT 2;
|
||||
PUSHINT 3;
|
||||
MUL;
|
||||
ADD;
|
||||
|
||||
POP 1;
|
||||
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
#source
|
||||
|
||||
1 * 2 + 3;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHINT 1;
|
||||
PUSHINT 2;
|
||||
MUL;
|
||||
PUSHINT 3;
|
||||
ADD;
|
||||
|
||||
POP 1;
|
||||
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
#source
|
||||
|
||||
(1 + 2) * 3;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHINT 1;
|
||||
PUSHINT 2;
|
||||
ADD;
|
||||
PUSHINT 3;
|
||||
MUL;
|
||||
|
||||
POP 1;
|
||||
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
#source
|
||||
|
||||
1 + (2 * 3);
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHINT 1;
|
||||
PUSHINT 2;
|
||||
PUSHINT 3;
|
||||
MUL;
|
||||
ADD;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
#source
|
||||
|
||||
1 * 2 * 3 * 4;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHINT 1;
|
||||
PUSHINT 2;
|
||||
MUL;
|
||||
|
||||
PUSHINT 3;
|
||||
MUL;
|
||||
|
||||
PUSHINT 4;
|
||||
MUL;
|
||||
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
#source
|
||||
|
||||
1 * (2 * (3 * 4));
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHINT 1;
|
||||
PUSHINT 2;
|
||||
PUSHINT 3;
|
||||
PUSHINT 4;
|
||||
MUL;
|
||||
MUL;
|
||||
MUL;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
#source
|
||||
|
||||
1 + 2 * 3 + 4;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHINT 1;
|
||||
PUSHINT 2;
|
||||
PUSHINT 3;
|
||||
MUL;
|
||||
ADD;
|
||||
PUSHINT 4;
|
||||
ADD;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
#source
|
||||
|
||||
1 * 2 + 3 * 4;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHINT 1;
|
||||
PUSHINT 2;
|
||||
MUL;
|
||||
|
||||
PUSHINT 3;
|
||||
PUSHINT 4;
|
||||
MUL;
|
||||
|
||||
ADD;
|
||||
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
#source
|
||||
|
||||
A - B - C;
|
||||
A - B * C;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHVAR "A";
|
||||
PUSHVAR "B";
|
||||
SUB;
|
||||
PUSHVAR "C";
|
||||
SUB;
|
||||
POP 1;
|
||||
|
||||
PUSHVAR "A";
|
||||
PUSHVAR "B";
|
||||
PUSHVAR "C";
|
||||
MUL;
|
||||
SUB;
|
||||
POP 1;
|
||||
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
#source
|
||||
|
||||
A and B or C and D;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHVAR "A";
|
||||
PUSHVAR "B";
|
||||
AND;
|
||||
PUSHVAR "C";
|
||||
PUSHVAR "D";
|
||||
AND;
|
||||
OR;
|
||||
POP 1;
|
||||
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
#source
|
||||
|
||||
X or Y and Z or W;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHVAR "X";
|
||||
PUSHVAR "Y";
|
||||
PUSHVAR "Z";
|
||||
AND;
|
||||
OR;
|
||||
PUSHVAR "W";
|
||||
OR;
|
||||
POP 1;
|
||||
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,46 @@
|
||||
|
||||
#source
|
||||
|
||||
1 + 2 > 3 + 4;
|
||||
1 + 2 < 3 + 4;
|
||||
1 + 2 >= 3 + 4;
|
||||
1 + 2 <= 3 + 4;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHINT 1;
|
||||
PUSHINT 2;
|
||||
ADD;
|
||||
PUSHINT 3;
|
||||
PUSHINT 4;
|
||||
ADD;
|
||||
GRT;
|
||||
POP 1;
|
||||
|
||||
PUSHINT 1;
|
||||
PUSHINT 2;
|
||||
ADD;
|
||||
PUSHINT 3;
|
||||
PUSHINT 4;
|
||||
ADD;
|
||||
LSS;
|
||||
POP 1;
|
||||
|
||||
PUSHINT 1;
|
||||
PUSHINT 2;
|
||||
ADD;
|
||||
PUSHINT 3;
|
||||
PUSHINT 4;
|
||||
ADD;
|
||||
GEQ;
|
||||
POP 1;
|
||||
|
||||
PUSHINT 1;
|
||||
PUSHINT 2;
|
||||
ADD;
|
||||
PUSHINT 3;
|
||||
PUSHINT 4;
|
||||
ADD;
|
||||
LEQ;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
#source
|
||||
|
||||
[1];
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHLST 1;
|
||||
PUSHINT 0;
|
||||
PUSHINT 1;
|
||||
INSERT;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
#source
|
||||
|
||||
[true, false];
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHLST 2;
|
||||
|
||||
PUSHINT 0;
|
||||
PUSHTRU;
|
||||
INSERT;
|
||||
|
||||
PUSHINT 1;
|
||||
PUSHFLS;
|
||||
INSERT;
|
||||
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
#source
|
||||
|
||||
({'name': 'Francesco'});
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHMAP 1;
|
||||
PUSHSTR "name";
|
||||
PUSHSTR "Francesco";
|
||||
INSERT;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
#source
|
||||
|
||||
({'name': 'Francesco', 'age': 25});
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHMAP 2;
|
||||
|
||||
PUSHSTR "name";
|
||||
PUSHSTR "Francesco";
|
||||
INSERT;
|
||||
|
||||
PUSHSTR "age";
|
||||
PUSHINT 25;
|
||||
INSERT;
|
||||
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
#source
|
||||
|
||||
({name: 'Francesco'});
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHMAP 1;
|
||||
PUSHSTR "name";
|
||||
PUSHSTR "Francesco";
|
||||
INSERT;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
#source
|
||||
|
||||
({name: 'Francesco', age: 25});
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHMAP 2;
|
||||
|
||||
PUSHSTR "name";
|
||||
PUSHSTR "Francesco";
|
||||
INSERT;
|
||||
|
||||
PUSHSTR "age";
|
||||
PUSHINT 25;
|
||||
INSERT;
|
||||
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
#source
|
||||
|
||||
({+name: 'Francesco'});
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHMAP 1;
|
||||
PUSHVAR "name";
|
||||
POS;
|
||||
PUSHSTR "Francesco";
|
||||
INSERT;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
#source
|
||||
|
||||
({+name: 'Francesco'});
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHMAP 1;
|
||||
PUSHVAR "name";
|
||||
POS;
|
||||
PUSHSTR "Francesco";
|
||||
INSERT;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
#source
|
||||
|
||||
var = none;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHNNE;
|
||||
ASS "var";
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
#source
|
||||
|
||||
var[key] = none;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHNNE;
|
||||
PUSHVAR "var";
|
||||
PUSHVAR "key";
|
||||
INSERT2;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
#source
|
||||
|
||||
A, B = func();
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHVAR "func";
|
||||
CALL 0, 2;
|
||||
|
||||
ASS "B";
|
||||
POP 1;
|
||||
|
||||
ASS "A";
|
||||
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
#source
|
||||
|
||||
var.key = none;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHNNE;
|
||||
PUSHVAR "var";
|
||||
PUSHSTR "key";
|
||||
INSERT2;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
#source
|
||||
|
||||
func();
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHVAR "func";
|
||||
CALL 0, 1;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
#source
|
||||
|
||||
func(none);
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHNNE;
|
||||
PUSHVAR "func";
|
||||
CALL 1, 1;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
#source
|
||||
|
||||
func(true, false);
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHFLS;
|
||||
PUSHTRU;
|
||||
PUSHVAR "func";
|
||||
CALL 2, 1;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
#source
|
||||
|
||||
set[key];
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHVAR "set";
|
||||
PUSHVAR "key";
|
||||
SELECT;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,25 @@
|
||||
#source
|
||||
|
||||
set["dog", "cow", "cat"];
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHVAR "set";
|
||||
|
||||
PUSHLST 3;
|
||||
|
||||
PUSHINT 0;
|
||||
PUSHSTR "dog";
|
||||
INSERT;
|
||||
|
||||
PUSHINT 1;
|
||||
PUSHSTR "cow";
|
||||
INSERT;
|
||||
|
||||
PUSHINT 2;
|
||||
PUSHSTR "cat";
|
||||
INSERT;
|
||||
|
||||
SELECT;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
#source
|
||||
|
||||
set.key;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHVAR "set";
|
||||
PUSHSTR "key";
|
||||
SELECT;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
#source
|
||||
|
||||
if true:
|
||||
{}
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHTRU;
|
||||
JUMPIFNOTANDPOP end;
|
||||
end:
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
#source
|
||||
|
||||
if true:
|
||||
none;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHTRU;
|
||||
JUMPIFNOTANDPOP end;
|
||||
PUSHNNE;
|
||||
POP 1;
|
||||
end:
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
#source
|
||||
|
||||
if true:
|
||||
{}
|
||||
else
|
||||
{}
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHTRU;
|
||||
JUMPIFNOTANDPOP end;
|
||||
|
||||
JUMP end;
|
||||
|
||||
end:
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
#source
|
||||
|
||||
if none:
|
||||
true;
|
||||
else
|
||||
false;
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHNNE;
|
||||
JUMPIFNOTANDPOP else;
|
||||
PUSHTRU;
|
||||
POP 1;
|
||||
JUMP end;
|
||||
else:
|
||||
PUSHFLS;
|
||||
POP 1;
|
||||
end:
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
#source
|
||||
|
||||
while none:
|
||||
{}
|
||||
|
||||
#bytecode
|
||||
|
||||
begin:
|
||||
PUSHNNE;
|
||||
JUMPIFNOTANDPOP end;
|
||||
JUMP begin;
|
||||
end:
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
#source
|
||||
|
||||
while none:
|
||||
true;
|
||||
false;
|
||||
|
||||
#bytecode
|
||||
|
||||
begin:
|
||||
PUSHNNE;
|
||||
JUMPIFNOTANDPOP end;
|
||||
PUSHTRU;
|
||||
POP 1;
|
||||
JUMP begin;
|
||||
end:
|
||||
PUSHFLS;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
#source
|
||||
|
||||
while none: {
|
||||
true;
|
||||
"Hello, world!";
|
||||
}
|
||||
false;
|
||||
|
||||
#bytecode
|
||||
|
||||
begin:
|
||||
PUSHNNE;
|
||||
JUMPIFNOTANDPOP end;
|
||||
PUSHTRU;
|
||||
POP 1;
|
||||
PUSHSTR "Hello, world!";
|
||||
POP 1;
|
||||
JUMP begin;
|
||||
end:
|
||||
PUSHFLS;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
#source
|
||||
|
||||
while none: {
|
||||
true;
|
||||
"Hello, world!";
|
||||
break;
|
||||
}
|
||||
false;
|
||||
|
||||
#bytecode
|
||||
|
||||
begin:
|
||||
PUSHNNE;
|
||||
JUMPIFNOTANDPOP end;
|
||||
PUSHTRU;
|
||||
POP 1;
|
||||
PUSHSTR "Hello, world!";
|
||||
POP 1;
|
||||
JUMP end;
|
||||
JUMP begin;
|
||||
end:
|
||||
PUSHFLS;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
#source
|
||||
|
||||
while none:
|
||||
break;
|
||||
false;
|
||||
|
||||
#bytecode
|
||||
|
||||
begin:
|
||||
PUSHNNE;
|
||||
JUMPIFNOTANDPOP end;
|
||||
JUMP end;
|
||||
JUMP begin;
|
||||
end:
|
||||
PUSHFLS;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
#source
|
||||
|
||||
do
|
||||
{}
|
||||
while none;
|
||||
|
||||
#bytecode
|
||||
|
||||
begin:
|
||||
PUSHNNE;
|
||||
JUMPIFANDPOP begin;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
#source
|
||||
|
||||
do
|
||||
true;
|
||||
while none;
|
||||
false;
|
||||
|
||||
#bytecode
|
||||
|
||||
begin:
|
||||
PUSHTRU;
|
||||
POP 1;
|
||||
PUSHNNE;
|
||||
JUMPIFANDPOP begin;
|
||||
PUSHFLS;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
#source
|
||||
|
||||
do {
|
||||
true;
|
||||
"Hello, world!";
|
||||
} while none;
|
||||
false;
|
||||
|
||||
#bytecode
|
||||
|
||||
begin:
|
||||
PUSHTRU;
|
||||
POP 1;
|
||||
PUSHSTR "Hello, world!";
|
||||
POP 1;
|
||||
PUSHNNE;
|
||||
JUMPIFANDPOP begin;
|
||||
PUSHFLS;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
#source
|
||||
|
||||
do {
|
||||
true;
|
||||
"Hello, world!";
|
||||
break;
|
||||
} while none;
|
||||
false;
|
||||
|
||||
#bytecode
|
||||
|
||||
begin:
|
||||
PUSHTRU;
|
||||
POP 1;
|
||||
PUSHSTR "Hello, world!";
|
||||
POP 1;
|
||||
JUMP end;
|
||||
PUSHNNE;
|
||||
JUMPIFANDPOP begin;
|
||||
end:
|
||||
PUSHFLS;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
#source
|
||||
|
||||
do
|
||||
break;
|
||||
while none;
|
||||
false;
|
||||
|
||||
#bytecode
|
||||
|
||||
begin:
|
||||
JUMP end;
|
||||
PUSHNNE;
|
||||
JUMPIFANDPOP begin;
|
||||
end:
|
||||
PUSHFLS;
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
#source
|
||||
|
||||
fun X() {}
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHFUN fun, 0;
|
||||
ASS "X";
|
||||
POP 1;
|
||||
JUMP end;
|
||||
fun:
|
||||
RETURN 0;
|
||||
end:
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
#source
|
||||
|
||||
fun X() "Hello, world!";
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHFUN fun, 0;
|
||||
ASS "X";
|
||||
POP 1;
|
||||
JUMP end;
|
||||
fun:
|
||||
PUSHSTR "Hello, world!";
|
||||
POP 1;
|
||||
RETURN 0;
|
||||
end:
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
#source
|
||||
|
||||
fun X() {
|
||||
"Hello, world!";
|
||||
return false;
|
||||
}
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHFUN fun, 0;
|
||||
ASS "X";
|
||||
POP 1;
|
||||
JUMP end;
|
||||
fun:
|
||||
PUSHSTR "Hello, world!";
|
||||
POP 1;
|
||||
PUSHFLS;
|
||||
RETURN 1;
|
||||
RETURN 0;
|
||||
end:
|
||||
RETURN 0;
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
#source
|
||||
|
||||
fun add(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHFUN fun, 2;
|
||||
ASS "add";
|
||||
POP 1;
|
||||
JUMP end;
|
||||
fun:
|
||||
ASS "b";
|
||||
POP 1;
|
||||
ASS "a";
|
||||
POP 1;
|
||||
PUSHVAR "a";
|
||||
PUSHVAR "b";
|
||||
ADD;
|
||||
RETURN 1;
|
||||
RETURN 0;
|
||||
end:
|
||||
RETURN 0;
|
||||
Binary file not shown.
-555
@@ -1,555 +0,0 @@
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include "../src/noja/utils/source.h"
|
||||
#include "../src/noja/compiler/parse.h"
|
||||
#include "../src/noja/compiler/compile.h"
|
||||
#include "../src/noja/common/executable.h"
|
||||
#include "../src/noja/assembler/assemble.h"
|
||||
|
||||
//Regular text
|
||||
#define BLK "\e[0;30m"
|
||||
#define RED "\e[0;31m"
|
||||
#define GRN "\e[0;32m"
|
||||
#define YEL "\e[0;33m"
|
||||
#define BLU "\e[0;34m"
|
||||
#define MAG "\e[0;35m"
|
||||
#define CYN "\e[0;36m"
|
||||
#define WHT "\e[0;37m"
|
||||
|
||||
//Regular bold text
|
||||
#define BBLK "\e[1;30m"
|
||||
#define BRED "\e[1;31m"
|
||||
#define BGRN "\e[1;32m"
|
||||
#define BYEL "\e[1;33m"
|
||||
#define BBLU "\e[1;34m"
|
||||
#define BMAG "\e[1;35m"
|
||||
#define BCYN "\e[1;36m"
|
||||
#define BWHT "\e[1;37m"
|
||||
|
||||
//Regular underline text
|
||||
#define UBLK "\e[4;30m"
|
||||
#define URED "\e[4;31m"
|
||||
#define UGRN "\e[4;32m"
|
||||
#define UYEL "\e[4;33m"
|
||||
#define UBLU "\e[4;34m"
|
||||
#define UMAG "\e[4;35m"
|
||||
#define UCYN "\e[4;36m"
|
||||
#define UWHT "\e[4;37m"
|
||||
|
||||
//Regular background
|
||||
#define BLKB "\e[40m"
|
||||
#define REDB "\e[41m"
|
||||
#define GRNB "\e[42m"
|
||||
#define YELB "\e[43m"
|
||||
#define BLUB "\e[44m"
|
||||
#define MAGB "\e[45m"
|
||||
#define CYNB "\e[46m"
|
||||
#define WHTB "\e[47m"
|
||||
|
||||
//High intensty background
|
||||
#define BLKHB "\e[0;100m"
|
||||
#define REDHB "\e[0;101m"
|
||||
#define GRNHB "\e[0;102m"
|
||||
#define YELHB "\e[0;103m"
|
||||
#define BLUHB "\e[0;104m"
|
||||
#define MAGHB "\e[0;105m"
|
||||
#define CYNHB "\e[0;106m"
|
||||
#define WHTHB "\e[0;107m"
|
||||
|
||||
//High intensty text
|
||||
#define HBLK "\e[0;90m"
|
||||
#define HRED "\e[0;91m"
|
||||
#define HGRN "\e[0;92m"
|
||||
#define HYEL "\e[0;93m"
|
||||
#define HBLU "\e[0;94m"
|
||||
#define HMAG "\e[0;95m"
|
||||
#define HCYN "\e[0;96m"
|
||||
#define HWHT "\e[0;97m"
|
||||
|
||||
//Bold high intensity text
|
||||
#define BHBLK "\e[1;90m"
|
||||
#define BHRED "\e[1;91m"
|
||||
#define BHGRN "\e[1;92m"
|
||||
#define BHYEL "\e[1;93m"
|
||||
#define BHBLU "\e[1;94m"
|
||||
#define BHMAG "\e[1;95m"
|
||||
#define BHCYN "\e[1;96m"
|
||||
#define BHWHT "\e[1;97m"
|
||||
|
||||
//Reset
|
||||
#define reset "\e[0m"
|
||||
#define CRESET "\e[0m"
|
||||
#define COLOR_RESET "\e[0m"
|
||||
|
||||
// NOTE: From https://gist.github.com/RabaDabaDoba/145049536f815903c79944599c6f952a
|
||||
|
||||
static void print_error(const char *type, Error *error)
|
||||
{
|
||||
if(type == NULL)
|
||||
fprintf(stderr, RED "Error" CRESET);
|
||||
else if(error->internal)
|
||||
fprintf(stderr, RED "Internal Error" CRESET);
|
||||
else
|
||||
fprintf(stderr, RED "%s Error" CRESET, type);
|
||||
|
||||
fprintf(stderr, " :: %s.", error->message);
|
||||
|
||||
#ifdef DEBUG
|
||||
if(error->file != NULL)
|
||||
{
|
||||
if(error->line > 0 && error->func != NULL)
|
||||
fprintf(stderr, " (Reported in %s:%d in %s)", error->file, error->line, error->func);
|
||||
else if(error->line > 0 && error->func == NULL)
|
||||
fprintf(stderr, " (Reported in %s:%d)", error->file, error->line);
|
||||
else if(error->line < 1 && error->func != NULL)
|
||||
fprintf(stderr, " (Reported in %s in %s)", error->file, error->func);
|
||||
}
|
||||
#endif
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
TokenType_END,
|
||||
TokenType_TEXT,
|
||||
TokenType_DIRECTIVE_SOURCE,
|
||||
TokenType_DIRECTIVE_BYTECODE,
|
||||
} TokenType;
|
||||
|
||||
typedef struct {
|
||||
TokenType type;
|
||||
size_t offset;
|
||||
size_t length;
|
||||
} Token;
|
||||
|
||||
static bool startsWithSourceDirective(const char *str, size_t len)
|
||||
{
|
||||
return (((7 < len && !isalpha(str[7])) || 7 == len)
|
||||
&& str[0] == '#' && str[1] == 's'
|
||||
&& str[2] == 'o' && str[3] == 'u'
|
||||
&& str[4] == 'r' && str[5] == 'c'
|
||||
&& str[6] == 'e');
|
||||
}
|
||||
|
||||
static bool startsWithBytecodeDirective(const char *str, size_t len)
|
||||
{
|
||||
return (((9 < len && !isalpha(str[9])) || 9 == len)
|
||||
&& str[0] == '#' && str[1] == 'b'
|
||||
&& str[2] == 'y' && str[3] == 't'
|
||||
&& str[4] == 'e' && str[5] == 'c'
|
||||
&& str[6] == 'o' && str[7] == 'd'
|
||||
&& str[8] == 'e');
|
||||
}
|
||||
|
||||
static bool startsWithDirective(const char *str, size_t len)
|
||||
{
|
||||
return startsWithSourceDirective(str, len)
|
||||
|| startsWithBytecodeDirective(str, len);
|
||||
}
|
||||
|
||||
static Token nextToken(const char *str, size_t len, size_t *cur_)
|
||||
{
|
||||
size_t cur = *cur_;
|
||||
|
||||
while(cur < len && isspace(str[cur]))
|
||||
cur += 1;
|
||||
|
||||
Token token;
|
||||
if(cur == len) {
|
||||
token.type = TokenType_END;
|
||||
token.offset = cur;
|
||||
token.length = 0;
|
||||
} else if(startsWithSourceDirective(str + cur, len - cur)) {
|
||||
token.type = TokenType_DIRECTIVE_SOURCE;
|
||||
token.offset = cur;
|
||||
cur += strlen("#source");
|
||||
token.length = cur - token.offset;
|
||||
} else if(startsWithBytecodeDirective(str + cur, len - cur)) {
|
||||
token.type = TokenType_DIRECTIVE_BYTECODE;
|
||||
token.offset = cur;
|
||||
cur += strlen("#bytecode");
|
||||
token.length = cur - token.offset;
|
||||
} else {
|
||||
token.type = TokenType_TEXT;
|
||||
token.offset = cur;
|
||||
|
||||
while(1) {
|
||||
// Consume all characters until
|
||||
// the next '#'.
|
||||
while(cur < len && str[cur] != '#')
|
||||
cur += 1;
|
||||
|
||||
if(cur == len)
|
||||
break;
|
||||
|
||||
// If it's the start of a directive,
|
||||
// stop the loop. If it was not, skip
|
||||
// the '#'.
|
||||
|
||||
if(startsWithDirective(str + cur, len - cur))
|
||||
break;
|
||||
|
||||
cur += 1; // Skip the '#'
|
||||
}
|
||||
|
||||
token.length = cur - token.offset;
|
||||
}
|
||||
*cur_ = cur;
|
||||
return token;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
Source *source;
|
||||
Source *bytecode;
|
||||
} TestCase;
|
||||
|
||||
static bool parseTestCaseSource(Source *source, TestCase *testcase)
|
||||
{
|
||||
const char *str = Source_GetBody(source);
|
||||
size_t len = Source_GetSize(source);
|
||||
assert(str != NULL);
|
||||
|
||||
bool no_source = true;
|
||||
size_t source_offset;
|
||||
size_t source_length;
|
||||
|
||||
bool no_bytecode = true;
|
||||
size_t bytecode_offset;
|
||||
size_t bytecode_length;
|
||||
|
||||
size_t cur = 0;
|
||||
while(1) {
|
||||
Token token = nextToken(str, len, &cur);
|
||||
|
||||
if(token.type == TokenType_END)
|
||||
break;
|
||||
|
||||
if(token.type == TokenType_TEXT) {
|
||||
fprintf(stderr, RED "Error" CRESET " :: Expected a directive at offset %ld\n", token.offset);
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(token.type == TokenType_DIRECTIVE_SOURCE
|
||||
|| token.type == TokenType_DIRECTIVE_BYTECODE);
|
||||
|
||||
Token directive = token;
|
||||
|
||||
size_t directive_body_offset;
|
||||
size_t directive_body_length;
|
||||
|
||||
size_t backup = cur;
|
||||
token = nextToken(str, len, &cur);
|
||||
switch(token.type) {
|
||||
case TokenType_END:
|
||||
case TokenType_DIRECTIVE_SOURCE:
|
||||
case TokenType_DIRECTIVE_BYTECODE:
|
||||
// Right after the previous directive
|
||||
// there's either the end of the file
|
||||
// or another directive. We assume
|
||||
// that the directive is followed by
|
||||
// and empty string.
|
||||
directive_body_offset = token.offset;
|
||||
directive_body_length = 0;
|
||||
|
||||
// Put the cursor back so that this
|
||||
// token is tokenized again at the
|
||||
// start of the next iteration.
|
||||
cur = backup;
|
||||
break;
|
||||
|
||||
case TokenType_TEXT:
|
||||
directive_body_offset = token.offset;
|
||||
directive_body_length = token.length;
|
||||
break;
|
||||
}
|
||||
|
||||
if(directive.type == TokenType_DIRECTIVE_SOURCE) {
|
||||
|
||||
if(no_source == false) {
|
||||
fprintf(stderr, RED "Error" CRESET " :: #source directive specified twice.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
no_source = false;
|
||||
source_offset = directive_body_offset;
|
||||
source_length = directive_body_length;
|
||||
|
||||
} else {
|
||||
assert(directive.type == TokenType_DIRECTIVE_BYTECODE);
|
||||
|
||||
if(no_bytecode == false) {
|
||||
fprintf(stderr, RED "Error" CRESET " :: #bytecode directive specified twice.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
no_bytecode = false;
|
||||
bytecode_offset = directive_body_offset;
|
||||
bytecode_length = directive_body_length;
|
||||
}
|
||||
}
|
||||
|
||||
if(no_source == true && no_bytecode == true) {
|
||||
fprintf(stderr, RED "Error" CRESET " :: Missing both #source and #bytecode directives.\n");
|
||||
return false;
|
||||
}
|
||||
if(no_source == true) {
|
||||
fprintf(stderr, RED "Error" CRESET " :: Missing #source directive.\n");
|
||||
return false;
|
||||
}
|
||||
if(no_bytecode == true) {
|
||||
fprintf(stderr, RED "Error" CRESET " :: Missing #bytecode directive.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
Error error;
|
||||
Error_Init(&error);
|
||||
|
||||
testcase->source = Source_FromString("<input>", str + source_offset, source_length, &error);
|
||||
if(testcase->source == NULL) {
|
||||
print_error(NULL, &error);
|
||||
Error_Free(&error);
|
||||
return false;
|
||||
}
|
||||
|
||||
testcase->bytecode = Source_FromString("<output>", str + bytecode_offset, bytecode_length, &error);
|
||||
if(testcase->bytecode == NULL) {
|
||||
print_error(NULL, &error);
|
||||
Source_Free(testcase->source);
|
||||
Error_Free(&error);
|
||||
return false;
|
||||
}
|
||||
Error_Free(&error);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static Executable *build(Source *src)
|
||||
{
|
||||
Executable *exe;
|
||||
|
||||
// Create a bump-pointer allocator to hold the AST.
|
||||
BPAlloc *alloc = BPAlloc_Init(-1);
|
||||
|
||||
if(alloc == NULL)
|
||||
{
|
||||
fprintf(stderr, RED "Internal Error" CRESET " :: Couldn't allocate bump-pointer allocator to hold the AST.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Error error;
|
||||
Error_Init(&error);
|
||||
|
||||
// NOTE: The AST is stored in the BPAlloc. It's
|
||||
// lifetime is the same as the pool.
|
||||
AST *ast = parse(src, alloc, &error);
|
||||
|
||||
if(ast == NULL)
|
||||
{
|
||||
assert(error.occurred);
|
||||
print_error("Parsing", &error);
|
||||
Error_Free(&error);
|
||||
BPAlloc_Free(alloc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
exe = compile(ast, alloc, &error);
|
||||
|
||||
// We're done with the AST, independently from
|
||||
// the compilation result.
|
||||
BPAlloc_Free(alloc);
|
||||
|
||||
if(exe == NULL)
|
||||
{
|
||||
assert(error.occurred);
|
||||
print_error("Compilation", &error);
|
||||
Error_Free(&error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Error_Free(&error);
|
||||
return exe;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
TestResult_PASSED,
|
||||
TestResult_FAILED,
|
||||
TestResult_ABORT,
|
||||
} TestResult;
|
||||
|
||||
static TestResult runTest(const char *file)
|
||||
{
|
||||
Source *source;
|
||||
{
|
||||
Error error;
|
||||
Error_Init(&error);
|
||||
|
||||
source = Source_FromFile(file, &error);
|
||||
if(source == NULL) {
|
||||
print_error(NULL, &error);
|
||||
Error_Free(&error);
|
||||
return TestResult_ABORT;
|
||||
}
|
||||
Error_Free(&error);
|
||||
}
|
||||
|
||||
TestCase testcase;
|
||||
{
|
||||
if(!parseTestCaseSource(source, &testcase)) {
|
||||
Source_Free(source);
|
||||
return TestResult_ABORT;
|
||||
}
|
||||
}
|
||||
|
||||
Source_Free(source);
|
||||
|
||||
Executable *exe1 = build(testcase.source);
|
||||
if(exe1 == NULL) {
|
||||
Source_Free(testcase.source);
|
||||
Source_Free(testcase.bytecode);
|
||||
return TestResult_ABORT;
|
||||
}
|
||||
|
||||
Executable *exe2;
|
||||
{
|
||||
Error error;
|
||||
Error_Init(&error);
|
||||
|
||||
exe2 = assemble(testcase.bytecode, &error);
|
||||
if(exe2 == NULL) {
|
||||
print_error("Assemblation", &error);
|
||||
Error_Free(&error);
|
||||
Executable_Free(exe1);
|
||||
Source_Free(testcase.source);
|
||||
Source_Free(testcase.bytecode);
|
||||
return TestResult_ABORT;
|
||||
}
|
||||
|
||||
Error_Free(&error);
|
||||
}
|
||||
|
||||
bool passed = Executable_Equiv(exe1, exe2, stderr, " " WHT "Log" CRESET " :: ");
|
||||
|
||||
Executable_Free(exe1);
|
||||
Executable_Free(exe2);
|
||||
Source_Free(testcase.source);
|
||||
Source_Free(testcase.bytecode);
|
||||
|
||||
return passed
|
||||
? TestResult_PASSED
|
||||
: TestResult_FAILED;
|
||||
}
|
||||
|
||||
static void runTestSuite(const char *folder)
|
||||
{
|
||||
DIR *handle = opendir(folder);
|
||||
if(handle == NULL) {
|
||||
fprintf(stderr, RED "Error" CRESET " :: Failed to open folder '%s'.\n", folder);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t total_tests = 0;
|
||||
size_t passed_tests = 0;
|
||||
size_t failed_tests = 0;
|
||||
size_t aborted_tests = 0;
|
||||
size_t skipped_files = 0;
|
||||
|
||||
size_t folder_len = strlen(folder);
|
||||
|
||||
// If the folder path ends with a "/",
|
||||
// but it's not the first character,
|
||||
// then pop it.
|
||||
if(folder_len > 0 && folder[folder_len-1] == '/')
|
||||
folder_len -= 1;
|
||||
|
||||
// Now we know for sure that the provided
|
||||
// folder needs a trailing "/".
|
||||
|
||||
char test_path[1024];
|
||||
|
||||
// Make sure the buffer can contain the
|
||||
// folder's name (plus the trailing "/"
|
||||
// and the null byte).
|
||||
if(folder_len+2 > sizeof(test_path)) {
|
||||
fprintf(stderr, RED "Error" CRESET " :: Suite folder name is too long\n");
|
||||
return;
|
||||
}
|
||||
memcpy(test_path, folder, folder_len);
|
||||
test_path[folder_len] = '/';
|
||||
test_path[folder_len+1] = '\0';
|
||||
|
||||
// Now count the "/".
|
||||
folder_len += 1;
|
||||
|
||||
struct dirent *dir;
|
||||
while((dir = readdir(handle)) != NULL) {
|
||||
|
||||
const char *name = dir->d_name;
|
||||
size_t name_len = strlen(name);
|
||||
|
||||
if(dir->d_type != DT_REG) {
|
||||
fprintf(stderr, " " WHT "Log" CRESET " :: Skipping '%s' (not a regular file).\n", name);
|
||||
skipped_files += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(folder_len + name_len >= sizeof(test_path)) {
|
||||
fprintf(stderr, " " WHT "Log" CRESET " :: Skipping '%s' (path is too long).\n", name);
|
||||
skipped_files += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove the previous file's name
|
||||
// by truncating the parent folder
|
||||
// name with a null byte.
|
||||
test_path[folder_len] = '\0';
|
||||
strcat(test_path, name);
|
||||
|
||||
TestResult res = runTest(test_path);
|
||||
switch(res) {
|
||||
|
||||
case TestResult_PASSED:
|
||||
fprintf(stdout, " " BLU "Test" CRESET " :: Passed (%s)\n", name);
|
||||
passed_tests += 1;
|
||||
break;
|
||||
|
||||
case TestResult_FAILED:
|
||||
fprintf(stdout, " " YEL "Test" CRESET " :: Failed (%s)\n", name);
|
||||
failed_tests += 1;
|
||||
break;
|
||||
|
||||
case TestResult_ABORT:
|
||||
fprintf(stdout, " " RED "Test" CRESET " :: Aborted (%s)\n", name);
|
||||
aborted_tests += 1;
|
||||
break;
|
||||
}
|
||||
total_tests += 1;
|
||||
}
|
||||
|
||||
fprintf(stdout, "\n");
|
||||
fprintf(stdout, "@-----SUMMARY-----@\n");
|
||||
fprintf(stdout, "| Total : %-3ld |\n", total_tests);
|
||||
fprintf(stdout, "| Passed : %-3ld |\n", passed_tests);
|
||||
fprintf(stdout, "| Failed : %-3ld |\n", failed_tests);
|
||||
fprintf(stdout, "| Aborted : %-3ld |\n", aborted_tests);
|
||||
fprintf(stdout, "@-----------------@\n");
|
||||
|
||||
closedir(handle);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if(argc < 2) {
|
||||
fprintf(stderr, RED "Error" CRESET " :: No test suite folder name was provided.\n");
|
||||
fprintf(stderr, WHT "Usage" CRESET " :: %s <path>\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *folder = argv[1];
|
||||
runTestSuite(folder);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user