diff --git a/.gitignore b/.gitignore index 5802a8e..2d0f838 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ compile_commands.json *.dll *.exe *.out +!test/test_case/*.out *.app *.pdb *.ilk diff --git a/test/run_tests.sh b/test/run_tests.sh index dac31d3..377f001 100755 --- a/test/run_tests.sh +++ b/test/run_tests.sh @@ -1,4 +1,206 @@ -# Bash 测试脚本: -# - 批量编译 test/test_case/ 下的 *.sy 用例 -# - 将产物与日志写入 test/test_result/(例如 .ll/.s、运行输出、diff 结果) -# - 汇总通过/失败信息并给出统计 +#!/usr/bin/env bash + +set -uo pipefail + +repo_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +cd "$repo_root" + +cases_dir="test/test_case" +result_root="test/test_result/run_tests" +asm_dir="$result_root/asm" +compiler="./build/bin/compiler" +cross_gcc="aarch64-linux-gnu-gcc" +qemu_bin="qemu-aarch64" +qemu_sysroot="/usr/aarch64-linux-gnu" + +usage() { + cat <<'EOF' +用法: ./test/run_tests.sh [case ...] + +参数: + case 可选。支持以下两种写法: + - test/test_case/foo.sy + - foo (自动解析为 test/test_case/foo.sy) + +行为: + - 批量编译 test/test_case/*.sy 为 AArch64 汇编并链接可执行文件 + - 若存在同名 .in,则将其作为标准输入喂给程序 + - 采集程序标准输出,并将退出码追加为最后一行 + - 与同名 .out 做精确 diff,比对结果写入 test/test_result/run_tests/ +EOF +} + +require_tool() { + local tool=$1 + if ! command -v "$tool" >/dev/null 2>&1; then + echo "未找到依赖工具: $tool" >&2 + exit 1 + fi +} + +resolve_case() { + local arg=$1 + if [[ -f "$arg" ]]; then + printf '%s\n' "$arg" + return 0 + fi + if [[ -f "$cases_dir/$arg.sy" ]]; then + printf '%s\n' "$cases_dir/$arg.sy" + return 0 + fi + return 1 +} + +append_exit_code() { + local stdout_file=$1 + local actual_file=$2 + local status=$3 + + : > "$actual_file" + if [[ -f "$stdout_file" ]]; then + cat "$stdout_file" >> "$actual_file" + if [[ -s "$stdout_file" ]]; then + local last_byte + last_byte=$(tail -c 1 "$stdout_file" | od -An -t x1 | tr -d ' \n') + if [[ "$last_byte" != "0a" ]]; then + printf '\n' >> "$actual_file" + fi + fi + fi + printf '%s\n' "$status" >> "$actual_file" +} + +run_case() { + local input_sy=$1 + local base stem expected input_txt case_dir asm_file exe_file + local compile_log link_log stderr_log stdout_log actual_out diff_log + local status + + base=$(basename "$input_sy") + stem=${base%.sy} + expected="${input_sy%.sy}.out" + input_txt="${input_sy%.sy}.in" + + case_dir="$result_root/$stem" + asm_file="$asm_dir/$stem.s" + exe_file="$asm_dir/$stem" + compile_log="$case_dir/compiler.log" + link_log="$case_dir/link.log" + stderr_log="$case_dir/stderr.log" + stdout_log="$case_dir/stdout.log" + actual_out="$case_dir/actual.out" + diff_log="$case_dir/diff.log" + + rm -rf "$case_dir" + mkdir -p "$case_dir" + rm -f "$asm_file" "$exe_file" + + if [[ ! -f "$expected" ]]; then + echo "[FAIL] $stem: 缺少预期输出文件 $expected" + ((missing_expected_count += 1)) + failed_cases+=("$stem") + return + fi + + if ! "$compiler" --emit-asm "$input_sy" >"$asm_file" 2>"$compile_log"; then + echo "[FAIL] $stem: 编译失败,详见 $compile_log" + ((compile_fail_count += 1)) + failed_cases+=("$stem") + return + fi + + if ! "$cross_gcc" "$asm_file" -o "$exe_file" >"$link_log" 2>&1; then + echo "[FAIL] $stem: 链接失败,详见 $link_log" + ((link_fail_count += 1)) + failed_cases+=("$stem") + return + fi + + : > "$stdout_log" + : > "$stderr_log" + if [[ -f "$input_txt" ]]; then + "$qemu_bin" -L "$qemu_sysroot" "$exe_file" <"$input_txt" >"$stdout_log" 2>"$stderr_log" + status=$? + else + "$qemu_bin" -L "$qemu_sysroot" "$exe_file" >"$stdout_log" 2>"$stderr_log" + status=$? + fi + + append_exit_code "$stdout_log" "$actual_out" "$status" + + if diff -u "$expected" "$actual_out" >"$diff_log"; then + rm -f "$diff_log" + echo "[PASS] $stem" + ((pass_count += 1)) + else + echo "[FAIL] $stem: 输出不匹配,详见 $diff_log" + ((diff_fail_count += 1)) + failed_cases+=("$stem") + fi +} + +if [[ ${1:-} == "-h" || ${1:-} == "--help" ]]; then + usage + exit 0 +fi + +if [[ ! -x "$compiler" ]]; then + echo "未找到编译器: $compiler ,请先构建。" >&2 + exit 1 +fi + +require_tool "$cross_gcc" +require_tool "$qemu_bin" + +mkdir -p "$asm_dir" + +declare -a cases=() +declare -a failed_cases=() + +if [[ $# -gt 0 ]]; then + for arg in "$@"; do + if ! resolved=$(resolve_case "$arg"); then + echo "未找到测试用例: $arg" >&2 + exit 1 + fi + cases+=("$resolved") + done +else + while IFS= read -r -d '' file; do + cases+=("$file") + done < <(find "$cases_dir" -maxdepth 1 -type f -name '*.sy' -print0 | sort -z) +fi + +if [[ ${#cases[@]} -eq 0 ]]; then + echo "未找到任何 .sy 测试用例。" >&2 + exit 1 +fi + +pass_count=0 +compile_fail_count=0 +link_fail_count=0 +diff_fail_count=0 +missing_expected_count=0 + +for case_path in "${cases[@]}"; do + run_case "$case_path" +done + +total_count=${#cases[@]} +fail_count=$((compile_fail_count + link_fail_count + diff_fail_count + missing_expected_count)) + +echo +echo "测试完成: $total_count 个用例" +echo "通过: $pass_count" +echo "失败: $fail_count" +echo " 编译失败: $compile_fail_count" +echo " 链接失败: $link_fail_count" +echo " 输出不匹配: $diff_fail_count" +echo " 缺少预期输出: $missing_expected_count" + +if [[ ${#failed_cases[@]} -gt 0 ]]; then + echo "失败用例: ${failed_cases[*]}" + exit 1 +fi + +exit 0 diff --git a/test/test_case/05_arr_defn4.out b/test/test_case/05_arr_defn4.out new file mode 100644 index 0000000..aabe6ec --- /dev/null +++ b/test/test_case/05_arr_defn4.out @@ -0,0 +1 @@ +21 diff --git a/test/test_case/05_arr_defn4.sy b/test/test_case/05_arr_defn4.sy new file mode 100644 index 0000000..51a0e2c --- /dev/null +++ b/test/test_case/05_arr_defn4.sy @@ -0,0 +1,9 @@ +int main(){ + const int a[4][2] = {{1, 2}, {3, 4}, {}, 7}; + const int N = 3; + int b[4][2] = {}; + int c[4][2] = {1, 2, 3, 4, 5, 6, 7, 8}; + int d[N + 1][2] = {1, 2, {3}, {5}, a[3][0], 8}; + int e[4][2][1] = {{d[2][1], {c[2][1]}}, {3, 4}, {5, 6}, {7, 8}}; + return e[3][1][0] + e[0][0][0] + e[0][1][0] + d[3][0]; +} diff --git a/test/test_case/09_func_defn.out b/test/test_case/09_func_defn.out new file mode 100644 index 0000000..ec63514 --- /dev/null +++ b/test/test_case/09_func_defn.out @@ -0,0 +1 @@ +9 diff --git a/test/test_case/09_func_defn.sy b/test/test_case/09_func_defn.sy new file mode 100644 index 0000000..8b5acb2 --- /dev/null +++ b/test/test_case/09_func_defn.sy @@ -0,0 +1,11 @@ +int a; +int func(int p){ + p = p - 1; + return p; +} +int main(){ + int b; + a = 10; + b = func(a); + return b; +} diff --git a/test/test_case/11_add2.out b/test/test_case/11_add2.out new file mode 100644 index 0000000..ec63514 --- /dev/null +++ b/test/test_case/11_add2.out @@ -0,0 +1 @@ +9 diff --git a/test/test_case/11_add2.sy b/test/test_case/11_add2.sy new file mode 100644 index 0000000..229ae98 --- /dev/null +++ b/test/test_case/11_add2.sy @@ -0,0 +1,7 @@ +//test add +int main(){ + int a, b; + a = 10; + b = -1; + return a + b; +} \ No newline at end of file diff --git a/test/test_case/13_sub2.out b/test/test_case/13_sub2.out new file mode 100644 index 0000000..5d0b6c4 --- /dev/null +++ b/test/test_case/13_sub2.out @@ -0,0 +1 @@ +248 diff --git a/test/test_case/13_sub2.sy b/test/test_case/13_sub2.sy new file mode 100644 index 0000000..54b8d5b --- /dev/null +++ b/test/test_case/13_sub2.sy @@ -0,0 +1,7 @@ +//test sub +const int a = 10; +int main(){ + int b; + b = 2; + return b - a; +} \ No newline at end of file diff --git a/test/test_case/15_graph_coloring.out b/test/test_case/15_graph_coloring.out new file mode 100644 index 0000000..5a2e6d5 --- /dev/null +++ b/test/test_case/15_graph_coloring.out @@ -0,0 +1,2 @@ +1 2 3 2 +0 diff --git a/test/test_case/15_graph_coloring.sy b/test/test_case/15_graph_coloring.sy new file mode 100644 index 0000000..fb7a6e7 --- /dev/null +++ b/test/test_case/15_graph_coloring.sy @@ -0,0 +1,69 @@ +const int V = 4; +const int space = 32; +const int LF = 10; + +void printSolution(int color[]) { + int i = 0; + while (i < V) { + putint(color[i]); + putch(space); + i = i + 1; + } + putch(LF); +} + +void printMessage() { + putch(78);putch(111);putch(116); + putch(space); + putch(101);putch(120);putch(105);putch(115);putch(116); +} + +int isSafe(int graph[][V], int color[]) { + int i = 0; + while (i < V) { + int j = i + 1; + while (j < V) { + if (graph[i][j] && color[j] == color[i]) + return 0; + j = j + 1; + } + i = i + 1; + } + return 1; +} + +int graphColoring(int graph[][V], int m, int i, int color[]) { + if (i == V) { + if (isSafe(graph, color)) { + printSolution(color); + return 1; + } + return 0; + } + int j = 1; + while (j <= m) { + color[i] = j; + if (graphColoring(graph, m, i + 1, color)) + return 1; + color[i] = 0; + j = j + 1; + } + return 0; +} + +int main() { + int graph[V][V] = { + {0, 1, 1, 1}, + {1, 0, 1, 0}, + {1, 1, 0, 1}, + {1, 0, 1, 0} + }, m = 3; + int color[V], i = 0; + while (i < V) { + color[i] = 0; + i = i + 1; + } + if (!graphColoring(graph, m, 0, color)) + printMessage(); + return 0; +} \ No newline at end of file diff --git a/test/test_case/22_matrix_multiply.in b/test/test_case/22_matrix_multiply.in new file mode 100644 index 0000000..b98a477 --- /dev/null +++ b/test/test_case/22_matrix_multiply.in @@ -0,0 +1,10 @@ +4 4 +1 2 3 4 +5 6 7 8 +9 10 11 12 +13 14 15 16 +4 3 +9 5 1 +10 6 2 +11 7 3 +12 8 4 \ No newline at end of file diff --git a/test/test_case/22_matrix_multiply.out b/test/test_case/22_matrix_multiply.out new file mode 100644 index 0000000..de38a0a --- /dev/null +++ b/test/test_case/22_matrix_multiply.out @@ -0,0 +1,5 @@ +110 70 30 +278 174 70 +446 278 110 +614 382 150 +0 diff --git a/test/test_case/22_matrix_multiply.sy b/test/test_case/22_matrix_multiply.sy new file mode 100644 index 0000000..b23a569 --- /dev/null +++ b/test/test_case/22_matrix_multiply.sy @@ -0,0 +1,60 @@ +const int MAX_SIZE = 100; +int a[MAX_SIZE][MAX_SIZE], b[MAX_SIZE][MAX_SIZE]; +int res[MAX_SIZE][MAX_SIZE]; +int n1, m1, n2, m2; +void matrix_multiply() { + int i = 0; + while (i < m1) { + int j = 0; + while (j < n2) { + int k = 0; + while (k < n1) { + res[i][j] = res[i][j] + a[i][k] * b[k][j]; + k = k + 1; + } + j = j + 1; + } + i = i + 1; + } +} +int main() +{ + int i, j; + m1 = getint(); + n1 = getint(); + i = 0; + while (i < m1) { + j = 0; + while (j < n1) { + a[i][j] = getint(); + j = j + 1; + } + i = i + 1; + } + m2 = getint(); + n2 = getint(); + i = 0; + while (i < m2) { + j = 0; + while (j < n2) { + b[i][j] = getint(); + j = j + 1; + } + i = i + 1; + } + matrix_multiply(); + i = 0; + while (i < m1) { + j = 0; + while (j < n2) { + putint(res[i][j]); + putch(32); + j = j + 1; + } + putch(10); + i = i + 1; + } + return 0; +} + + diff --git a/test/test_case/25_scope3.out b/test/test_case/25_scope3.out new file mode 100644 index 0000000..d0428d5 --- /dev/null +++ b/test/test_case/25_scope3.out @@ -0,0 +1,2 @@ +a +46 diff --git a/test/test_case/25_scope3.sy b/test/test_case/25_scope3.sy new file mode 100644 index 0000000..100343c --- /dev/null +++ b/test/test_case/25_scope3.sy @@ -0,0 +1 @@ +int main() { /* scope test */ putch(97); putch(10); int a = 1, putch = 0; { a = a + 2; int b = a + 3; b = b + 4; putch = putch + a + b; { b = b + 5; int main = b + 6; a = a + main; putch = putch + a + b + main; { b = b + a; int a = main + 7; a = a + 8; putch = putch + a + b + main; { b = b + a; int b = main + 9; a = a + 10; const int a = 11; b = b + 12; putch = putch + a + b + main; { main = main + b; int main = b + 13; main = main + a; putch = putch + a + b + main; } putch = putch - main; } putch = putch - b; } putch = putch - a; } } return putch % 77; } \ No newline at end of file diff --git a/test/test_case/29_break.out b/test/test_case/29_break.out new file mode 100644 index 0000000..3bc92d4 --- /dev/null +++ b/test/test_case/29_break.out @@ -0,0 +1 @@ +201 diff --git a/test/test_case/29_break.sy b/test/test_case/29_break.sy new file mode 100644 index 0000000..2ab2a05 --- /dev/null +++ b/test/test_case/29_break.sy @@ -0,0 +1,15 @@ +//test break +int main(){ + int i; + i = 0; + int sum; + sum = 0; + while(i < 100){ + if(i == 50){ + break; + } + sum = sum + i; + i = i + 1; + } + return sum; +} \ No newline at end of file diff --git a/test/test_case/36_op_priority2.out b/test/test_case/36_op_priority2.out new file mode 100644 index 0000000..a45fd52 --- /dev/null +++ b/test/test_case/36_op_priority2.out @@ -0,0 +1 @@ +24 diff --git a/test/test_case/36_op_priority2.sy b/test/test_case/36_op_priority2.sy new file mode 100644 index 0000000..ac9c015 --- /dev/null +++ b/test/test_case/36_op_priority2.sy @@ -0,0 +1,9 @@ +//test the priority of add and mul +int main(){ + int a, b, c, d; + a = 10; + b = 4; + c = 2; + d = 2; + return (c + a) * (b - d); +} \ No newline at end of file diff --git a/test/test_case/95_float.in b/test/test_case/95_float.in new file mode 100644 index 0000000..71d6795 --- /dev/null +++ b/test/test_case/95_float.in @@ -0,0 +1,13 @@ +10 +0x1.999999999999ap-4 0x1.999999999999ap-3 0x1.3333333333333p-2 0x1.999999999999ap-2 0x1.0000000000000p-1 +0x1.3333333333333p-1 0x1.6666666666666p-1 0x1.999999999999ap-1 0x1.ccccccccccccdp-1 0x1.0000000000000p+0 +0x1.199999999999ap+0 +0x1.199999999999ap+1 +0x1.a666666666666p+1 +0x1.199999999999ap+2 +0x1.6000000000000p+2 +0x1.a666666666666p+2 +0x1.ecccccccccccdp+2 +0x1.199999999999ap+3 +0x1.3cccccccccccdp+3 +0x1.4333333333333p+3 diff --git a/test/test_case/95_float.out b/test/test_case/95_float.out new file mode 100644 index 0000000..93869ef --- /dev/null +++ b/test/test_case/95_float.out @@ -0,0 +1,19 @@ +ok +ok +ok +ok +ok +ok +ok +ok +0x1.e691e6p+1 3 +0x1.e691e6p+3 12 +0x1.11b21p+5 28 +0x1.e691e6p+5 50 +0x1.7c21fcp+6 78 +0x1.11b21p+7 113 +0x1.7487b2p+7 153 +0x1.e691e6p+7 201 +0x1.33e85p+8 254 +10: 0x1.333334p+0 0x1.333334p+1 0x1.ccccccp+1 0x1.333334p+2 0x1.8p+2 0x1.ccccccp+2 0x1.0cccccp+3 0x1.333334p+3 0x1.599998p+3 0x1p+0 +0 diff --git a/test/test_case/95_float.sy b/test/test_case/95_float.sy new file mode 100644 index 0000000..3372dde --- /dev/null +++ b/test/test_case/95_float.sy @@ -0,0 +1,98 @@ +// float global constants +const float RADIUS = 5.5, PI = 03.141592653589793, EPS = 1e-6; + +// hexadecimal float constant +const float PI_HEX = 0x1.921fb6p+1, HEX2 = 0x.AP-3; + +// float constant evaluation +const float FACT = -.33E+5, EVAL1 = PI * RADIUS * RADIUS, EVAL2 = 2 * PI_HEX * RADIUS, EVAL3 = PI * 2 * RADIUS; + +// float constant implicit conversion +const float CONV1 = 233, CONV2 = 0xfff; +const int MAX = 1e9, TWO = 2.9, THREE = 3.2, FIVE = TWO + THREE; + +// float -> float function +float float_abs(float x) { + if (x < 0) return -x; + return x; +} + +// int -> float function & float/int expression +float circle_area(int radius) { + return (PI * radius * radius + (radius * radius) * PI) / 2; +} + +// float -> float -> int function & float/int expression +int float_eq(float a, float b) { + if (float_abs(a - b) < EPS) { + return 1 * 2. / 2; + } else { + return 0; + } +} + +void error() { + putch(101); + putch(114); + putch(114); + putch(111); + putch(114); + putch(10); +} + +void ok() { + putch(111); + putch(107); + putch(10); +} + +void assert(int cond) { + if (!cond) { + error(); + } else { + ok(); + } +} + +void assert_not(int cond) { + if (cond) { + error(); + } else { + ok(); + } +} + +int main() { + assert_not(float_eq(HEX2, FACT)); + assert_not(float_eq(EVAL1, EVAL2)); + assert(float_eq(EVAL2, EVAL3)); + assert(float_eq(circle_area(RADIUS) /* f->i implicit conversion */, + circle_area(FIVE))); + assert_not(float_eq(CONV1, CONV2) /* i->f implicit conversion */); + + // float conditional expressions + if (1.5) ok(); + if (!!3.3) ok(); + if (.0 && 3) error(); + if (0 || 0.3) ok(); + + // float array & I/O functions + int i = 1, p = 0; + float arr[10] = {1., 2}; + int len = getfarray(arr); + while (i < MAX) { + float input = getfloat(); + float area = PI * input * input, area_trunc = circle_area(input); + arr[p] = arr[p] + input; + + putfloat(area); + putch(32); + putint(area_trunc); // f->i implicit conversion + putch(10); + + i = i * - -1e1; + p = p + 1; + } + putfarray(len, arr); + return 0; +} diff --git a/test/test_case/simple_add.out b/test/test_case/simple_add.out new file mode 100644 index 0000000..00750ed --- /dev/null +++ b/test/test_case/simple_add.out @@ -0,0 +1 @@ +3