mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Fix, separate out the function pass from the decl pass. Diagnose non-visible symbols as not visible rather than missing. Fix crash when module identifier is incorrect. !! operator added. New character literal parsing. Added simple test framework.
This commit is contained in:
committed by
Christoffer Lerno
parent
b4c661eaad
commit
f45d6ef84b
205
test/src/tester.py
Normal file
205
test/src/tester.py
Normal file
@@ -0,0 +1,205 @@
|
||||
#!/usr/bin/python
|
||||
import os, sys, shutil, subprocess
|
||||
|
||||
TEST_DIR = '/tmp/c3test/'
|
||||
class Config:
|
||||
run_skipped = False
|
||||
cwd = "."
|
||||
numtests = 0
|
||||
|
||||
class File:
|
||||
def __init__(self, filepath):
|
||||
with open(filepath) as reader:
|
||||
self.content = reader.read().splitlines()
|
||||
self.filename = filepath
|
||||
|
||||
|
||||
|
||||
class Issues:
|
||||
def __init__(self, conf, file, single):
|
||||
self.file = file
|
||||
self.single = single
|
||||
self.line = 0
|
||||
self.file_start = 0
|
||||
self.line_offset = 0
|
||||
self.has_errors = False
|
||||
self.error_message = "unknown"
|
||||
self.skip = False
|
||||
self.cur = 0
|
||||
self.current_file = None
|
||||
self.errors = {}
|
||||
self.warnings = {}
|
||||
if single:
|
||||
self.current_file = conf.cwd + "/" + file.filename
|
||||
|
||||
def exit_error(self, message):
|
||||
print('Error in file ' + self.file.filename + ': ' + message)
|
||||
exit(-1)
|
||||
|
||||
def set_failed(self):
|
||||
if not self.has_errors: print(" Failed.")
|
||||
self.has_errors = True
|
||||
|
||||
def check_line(self, type, file, line, message):
|
||||
if file == 'test.c3': file = self.file.filename
|
||||
map = {}
|
||||
if type == 'Error':
|
||||
map = self.errors
|
||||
elif type == 'Warning':
|
||||
map = self.warnings
|
||||
else:
|
||||
self.exit_error("Unknown type: " + type)
|
||||
key = file + ":" + line
|
||||
value = map.get(key)
|
||||
if value == None: return False
|
||||
if value in message:
|
||||
del map[key]
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def parse_result(self, lines):
|
||||
for line in lines:
|
||||
parts = line.split('|', maxsplit=4)
|
||||
if len(parts) != 4: self.exit_error("Illegal error result: " + line);
|
||||
if not self.check_line(parts[0], parts[1], parts[2], parts[3]):
|
||||
self.set_failed()
|
||||
print("Unexpected " + parts[0].lower() + " in " + parts[1] + " line " + parts[2] + ":", end="")
|
||||
print('"' + parts[3] + '"')
|
||||
if len(self.errors) > 0:
|
||||
self.set_failed()
|
||||
print("Expected errors that never occured:")
|
||||
num = 1
|
||||
for key, value in self.errors.items():
|
||||
pos = key.split(":", 2)
|
||||
print(str(num) + ". " + pos[0] + " line: " + pos[1] + " expected: " + value)
|
||||
num += 1
|
||||
|
||||
def compile(self, args):
|
||||
path = os.path.dirname(sys.argv[0]) + "/../../cmake-build-debug/"
|
||||
code = subprocess.run(path + 'c3c ' + args, universal_newlines=True, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
if code.returncode != 0 and code.returncode != 1:
|
||||
self.set_failed()
|
||||
print("Error: " + code.stderr)
|
||||
self.has_errors = True
|
||||
return
|
||||
self.parse_result(code.stderr.splitlines(keepends=False))
|
||||
|
||||
def parse_single(self):
|
||||
lines = len(self.file.content)
|
||||
while self.line < lines:
|
||||
line = self.file.content[self.line].strip()
|
||||
if "// #" in line:
|
||||
self.parse_trailing_directive(line)
|
||||
self.line += 1
|
||||
with open(TEST_DIR + 'test.c3', mode='w') as f:
|
||||
f.write("\n".join(self.file.content))
|
||||
f.write("\n")
|
||||
print("- " + self.file.filename + ":", end="")
|
||||
self.compile("--test compile " + TEST_DIR + 'test.c3')
|
||||
|
||||
if not self.has_errors:
|
||||
print(" Passed.")
|
||||
|
||||
def parse_header_directive(self, line):
|
||||
line = line[4:].strip()
|
||||
if (line.startswith("warnings:")):
|
||||
print("TODO" + line)
|
||||
exit(-1)
|
||||
elif (line.startswith("file:")):
|
||||
line = line[5:].strip()
|
||||
print("NEW FILE" + line)
|
||||
exit(-1)
|
||||
elif (line.startswith("expect:")):
|
||||
line = line[7:].strip()
|
||||
print("Expect " + line)
|
||||
exit(-1)
|
||||
else:
|
||||
self.exit_error("unknown header directive " + line)
|
||||
|
||||
def parse_trailing_directive(self, line):
|
||||
line = line.split('// #', 2)[1].strip()
|
||||
if (line.startswith("warning:")):
|
||||
print("TODO" + line)
|
||||
exit(-1)
|
||||
elif (line.startswith("error:")):
|
||||
line = line[6:].strip()
|
||||
self.errors[self.file.filename + ":%d" % (self.line + 1)] = line
|
||||
else:
|
||||
self.exit_error("unknown trailing directive " + line)
|
||||
|
||||
def parse_template(self):
|
||||
lines = len(self.file.content)
|
||||
while self.line < lines:
|
||||
line = self.file.content[self.line].strip()
|
||||
if line.startswith("// #"):
|
||||
self.parse_header_directive(line)
|
||||
elif "// #" in line:
|
||||
self.parse_trailing_directive(line)
|
||||
self.line += 1
|
||||
|
||||
print("parse mult")
|
||||
|
||||
def parse(self):
|
||||
if len(self.file.content) == 0: self.exit_error("File was empty")
|
||||
is_skip = self.file.content[0].startswith("// #skip")
|
||||
if is_skip != self.skip: return
|
||||
|
||||
if is_skip: self.line += 1
|
||||
if self.single:
|
||||
self.parse_single()
|
||||
else:
|
||||
self.parse_template()
|
||||
|
||||
|
||||
def usage():
|
||||
print("Usage: " + sys.argv[0] + " <file/dir> [-s]")
|
||||
print('')
|
||||
print('Options:')
|
||||
print(" -s, --skipped only run skipped tests")
|
||||
exit(-1)
|
||||
|
||||
def handle_file(filepath, conf):
|
||||
if filepath.endswith('.c3'):
|
||||
single = True
|
||||
elif filepath.endswith('.c3t'):
|
||||
single = False
|
||||
else:
|
||||
return
|
||||
|
||||
shutil.rmtree(TEST_DIR, ignore_errors=True)
|
||||
os.mkdir(TEST_DIR, mode = 0o777)
|
||||
|
||||
conf.numtests += 1
|
||||
|
||||
issues = Issues(conf, File(filepath), single)
|
||||
issues.parse()
|
||||
|
||||
|
||||
|
||||
def handle_dir(filepath, conf):
|
||||
for file in os.listdir(filepath):
|
||||
file = filepath + "/" + file
|
||||
if os.path.isdir(file):
|
||||
handle_dir(file, conf)
|
||||
elif os.path.isfile(file):
|
||||
handle_file(file, conf)
|
||||
|
||||
def main():
|
||||
args = len(sys.argv)
|
||||
conf = Config()
|
||||
if args != 1 and args > 3: usage()
|
||||
if args == 3:
|
||||
if (sys.argv[2] != '-s' and sys.argv[2] != '--skipped'): usage()
|
||||
conf.run_skipped = True
|
||||
filepath = sys.argv[1]
|
||||
if filepath.endswith('/'): filepath = filepath[:-1]
|
||||
conf.cwd = os.getcwd()
|
||||
if os.path.isfile(filepath):
|
||||
handle_file(filepath, conf)
|
||||
elif os.path.isdir(filepath):
|
||||
handle_dir(filepath, conf)
|
||||
else:
|
||||
usage()
|
||||
|
||||
main()
|
||||
13
test/test_suite/comments/simple_comments.c3
Normal file
13
test/test_suite/comments/simple_comments.c3
Normal file
@@ -0,0 +1,13 @@
|
||||
module comments;
|
||||
/* Span *//* style */
|
||||
|
||||
/+ Nested /+ Errors /* Inside +/ +/
|
||||
// Single line
|
||||
/*
|
||||
Multiline span style
|
||||
*/
|
||||
|
||||
func void test()
|
||||
{
|
||||
return;
|
||||
}
|
||||
11
test/test_suite/enums/enum_errors.c3
Normal file
11
test/test_suite/enums/enum_errors.c3
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
|
||||
enum EnumTestOverflow
|
||||
{
|
||||
VALUE = 0x80000000, // #error: does not fit into 'int'
|
||||
}
|
||||
|
||||
enum EnumTestErrorType : float // #error: must be an integer type not 'float'
|
||||
{
|
||||
VALUE_BOOM
|
||||
}
|
||||
31
test/test_suite/enums/enum_ok.c3
Normal file
31
test/test_suite/enums/enum_ok.c3
Normal file
@@ -0,0 +1,31 @@
|
||||
enum EnumTest : long
|
||||
{
|
||||
VALUE1 = 4,
|
||||
VALUE2
|
||||
}
|
||||
|
||||
typedef long as Frob;
|
||||
|
||||
enum EnumTestAlias : Frob
|
||||
{
|
||||
VALUE1 = 4,
|
||||
VALUE2
|
||||
}
|
||||
|
||||
enum EnumTestDefault
|
||||
{
|
||||
VALUE,
|
||||
VALUE2
|
||||
}
|
||||
|
||||
enum EnumTestNoOverflowAfterLong : long
|
||||
{
|
||||
VALUE = 0x7FFF_FFFF_FFFF_FFFE,
|
||||
VALUE_NO_EXCEED
|
||||
}
|
||||
|
||||
enum EnumTestSmall : ushort
|
||||
{
|
||||
VALUE = 0xFF,
|
||||
VALUE2 = 0xFFFF
|
||||
}
|
||||
19
test/test_suite/enums/enum_parse_errors.c3
Normal file
19
test/test_suite/enums/enum_parse_errors.c3
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
enum EnumWithErrorWithMissingName : int (int) // #error: function parameter must be named
|
||||
{
|
||||
TEST
|
||||
}
|
||||
|
||||
enum EnumWithErrorData : int (int // #error: end of the parameter list
|
||||
{
|
||||
TEST
|
||||
}
|
||||
|
||||
error TheError
|
||||
{
|
||||
union // #error: A type name was expected here
|
||||
{
|
||||
int a;
|
||||
int b;
|
||||
}
|
||||
}
|
||||
9
test/test_suite/errors/error_decl_fails.c3
Normal file
9
test/test_suite/errors/error_decl_fails.c3
Normal file
@@ -0,0 +1,9 @@
|
||||
module foo;
|
||||
|
||||
error TooBig // #error: Error type may not exceed pointer
|
||||
{
|
||||
usize a;
|
||||
char b;
|
||||
}
|
||||
|
||||
|
||||
9
test/test_suite/errors/error_decl_ok.c3
Normal file
9
test/test_suite/errors/error_decl_ok.c3
Normal file
@@ -0,0 +1,9 @@
|
||||
module errors;
|
||||
|
||||
error TheError
|
||||
{
|
||||
int a;
|
||||
}
|
||||
|
||||
error OtherError;
|
||||
|
||||
38
test/test_suite/expressions/arithmetics.c3
Normal file
38
test/test_suite/expressions/arithmetics.c3
Normal file
@@ -0,0 +1,38 @@
|
||||
module arithmetics;
|
||||
|
||||
func void testAdd(int a, int b)
|
||||
{
|
||||
a = a + b;
|
||||
a = a +% b;
|
||||
a +%= b;
|
||||
a += b;
|
||||
a += 1;
|
||||
a +%= 1;
|
||||
}
|
||||
|
||||
func void testSub(int a, int b)
|
||||
{
|
||||
a = a - b;
|
||||
a = a -% b;
|
||||
a -%= b;
|
||||
a -= b;
|
||||
a -%= 1;
|
||||
a -= 1;
|
||||
}
|
||||
|
||||
func void testMult(int a, int b)
|
||||
{
|
||||
a = a * b;
|
||||
a = a *% b;
|
||||
a *%= b;
|
||||
a *= b;
|
||||
a *%= 1;
|
||||
a *= 1;
|
||||
}
|
||||
|
||||
func void testDiv(int a, int b)
|
||||
{
|
||||
a = a / b;
|
||||
a /= b;
|
||||
a /= 1;
|
||||
}
|
||||
6
test/test_suite/functions/bar.c3
Normal file
6
test/test_suite/functions/bar.c3
Normal file
@@ -0,0 +1,6 @@
|
||||
module bob;
|
||||
|
||||
func void hello()
|
||||
{
|
||||
int i = 0;
|
||||
}
|
||||
3
test/test_suite/functions/foo.c3
Normal file
3
test/test_suite/functions/foo.c3
Normal file
@@ -0,0 +1,3 @@
|
||||
// Hello
|
||||
|
||||
func int foo() {} // #error: Missing return statement
|
||||
58
test/test_suite/functions/multi_module.c3t2
Normal file
58
test/test_suite/functions/multi_module.c3t2
Normal file
@@ -0,0 +1,58 @@
|
||||
// @recipe bin
|
||||
$warnings no-unused
|
||||
$generate-c
|
||||
|
||||
// #file: file1
|
||||
module test1;
|
||||
|
||||
import test2;
|
||||
|
||||
public func void pub1() {}
|
||||
|
||||
func void nonpub1() {}
|
||||
|
||||
public func i32 main(i32 argc, const i8*[] argv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// #file: file2
|
||||
module test2;
|
||||
|
||||
public func void pub2() {}
|
||||
|
||||
func void nonpub2() {}
|
||||
|
||||
// #expect: test1.h
|
||||
|
||||
void test1_pub1();
|
||||
|
||||
// #expect: test1.c
|
||||
#include "test1.h"
|
||||
|
||||
static void test1_nonpub1();
|
||||
|
||||
void test1_pub1() {
|
||||
}
|
||||
|
||||
static void test1_nonpub1() {
|
||||
}
|
||||
|
||||
int32_t main(int32_t argc, const char* argv[]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// @expect{atleast, build/test2.h}
|
||||
|
||||
void test2_pub2();
|
||||
|
||||
// @expect{atleast, build/test2.c}
|
||||
#include "test2.h"
|
||||
|
||||
static void test2_nonpub2();
|
||||
|
||||
void test2_pub2() {
|
||||
}
|
||||
|
||||
static void test2_nonpub2() {
|
||||
}
|
||||
|
||||
13
test/test_suite/strings/literal_errors.c3
Normal file
13
test/test_suite/strings/literal_errors.c3
Normal file
@@ -0,0 +1,13 @@
|
||||
char bar = '\xaf';
|
||||
char bar = '\x0F';
|
||||
|
||||
char bar = '\xgh'; // #error: Expected a two character
|
||||
|
||||
char baz = '\ueeof'; // #error: Expected a four char
|
||||
char eofk = '\u233'; // #error: Expected a four char
|
||||
|
||||
char zab = '\Uaokdokok'; // #error: Expected an eight
|
||||
char zab = '\Uaokdooekfoekfekfkeofkekok'; // #error: Expected an eight
|
||||
char eofk = '\UaUfko'; // #error: Expected an eight
|
||||
|
||||
char foo = ' // #error: Character literal did not terminate
|
||||
3
test/test_suite/strings/literal_hex_ok.c3
Normal file
3
test/test_suite/strings/literal_hex_ok.c3
Normal file
@@ -0,0 +1,3 @@
|
||||
byte bar1 = '\xaf';
|
||||
char bar2 = '\x0F';
|
||||
ushort bar4 = '\u0FaF';
|
||||
Reference in New Issue
Block a user