スタックまわりの挙動によって、テストで積まれたスタックの要素が次に残っているようでした。
各ユニットテストの実行前にスタックの初期化(stack.c
のstatic int top
を-1
に設定しなおすこと)をしていないので、前のテスト実行後の状態が残っています。だいたいのテストはスタックが空になって終了するので問題ないですが、一部(発見したのはtest_eval_num_pop()
)のテストではスタックに値が残るので、以降はテスト開始時常にその値が入った状態でテストが始まっているようです。ただ、テストではスタックトップの状態をexpects分だけポップして比較しているので問題は起こっていなさそうでした。
具体的には以下のテストが値3
をスタックに残します。
// https://github.com/maiyama18/c-lesson/blob/b254d29944211a2ff863a3c3e61cd39f86f02b01/sources/forth_modoki/interpreter/eval.c#L671
static void test_eval_num_pop() {
char* input = "3 4 pop";
int expects[1] = { 3 };
cl_getc_set_src(input);
eval();
verify_stack_pop_number_eq(expects, 1);
}
もしテストの前提として「スタックは空の状態」があるのであれば、スタックの長さをテスト時にassertしておくといい気がします。
ネストしたexec_arrayの実行でなぜ落ちそうなコードが動くのかを調査していました。こんなテスト
{ 3 1 { 1 add 100 } { 2 add } ifelse 200 } exec 300
で動作を確認しようとしました。ちなみに上のコード実行後のスタックの状態は4, 100, 200, 300
を想定していました。
eval.cを以下のように変更して
//
diff --git a/sources/forth_modoki/interpreter/eval.c b/sources/forth_modoki/interpreter/eval.c
index f84d4fb..c2e0f81 100644
--- a/sources/forth_modoki/interpreter/eval.c
+++ b/sources/forth_modoki/interpreter/eval.c
@@ -878,14 +878,14 @@ static void test_eval_ifelse_false() {
}
static void test_eval_ifelse_true_in_executable_array() {
- char* input = "{ 3 1 { 1 add } { 2 add } ifelse } exec";
- int expects[1] = { 4 };
+ char* input = "{ 3 1 { 1 add 100 } { 2 add } ifelse 200 } exec 300";
+ int expects[4] = { 4 , 100, 200, 300 };
cl_getc_set_src(input);
eval();
-
- verify_stack_pop_number_eq(expects, 1);
+ stack_print_all();
+ verify_stack_pop_number_eq(expects, 4);
}
static void test_eval_ifelse_false_in_executable_array() {
スタックの中身を出力すると以下のようになりました。
$ gcc *.c && ./a.out
--- stack ---
4: ET_NUMBER 300
3: ET_NUMBER 200
2: ET_NUMBER 100
1: ET_NUMBER 4
0: ET_NUMBER 3
-------------
a.out: eval.c:381: verify_stack_pop_number_eq: Assertion `actual.u.number == expects[i]' failed.
実行順は正しいのですが、なにか余計な3が入っています(余計なものは入っているのですが実行順は正しそうなので本来の目的は達成していません)。add
が複数回実行されたりとかしてるんだろうか、と思ってadd_opのところにprintf仕込んでも1回しか実行されておりませんでした。
そこでこのユニットテスト以外をコメントアウトして実行してみました。以下のような変更です。
diff --git a/sources/forth_modoki/interpreter/eval.c b/sources/forth_modoki/interpreter/eval.c
index f84d4fb..7353eea 100644
--- a/sources/forth_modoki/interpreter/eval.c
+++ b/sources/forth_modoki/interpreter/eval.c
@@ -878,14 +878,14 @@ static void test_eval_ifelse_false() {
}
static void test_eval_ifelse_true_in_executable_array() {
- char* input = "{ 3 1 { 1 add } { 2 add } ifelse } exec";
- int expects[1] = { 4 };
+ char* input = "{ 3 1 { 1 add 100 } { 2 add } ifelse 200 } exec 300";
+ int expects[4] = { 4 , 100, 200, 300 };
cl_getc_set_src(input);
eval();
-
- verify_stack_pop_number_eq(expects, 1);
+ stack_print_all();
+ verify_stack_pop_number_eq(expects, 4);
}
static void test_eval_ifelse_false_in_executable_array() {
@@ -1090,71 +1090,73 @@ static void test_dict_overwritten_not_head_name_found() {
void exec_tests() {
register_primitives();
- test_eval_num_one();
- test_eval_num_two();
- test_eval_num_two_separated_by_newline();
- test_eval_num_add();
- test_eval_num_add_many();
- test_eval_num_sub();
- test_eval_num_mul();
- test_eval_num_div();
- test_eval_num_mod();
- test_eval_num_eq_true();
- test_eval_num_eq_false();
- test_eval_num_neq_true();
- test_eval_num_neq_false();
- test_eval_num_gt_true_when_greater();
- test_eval_num_gt_false_when_equal();
- test_eval_num_gt_false_when_less();
- test_eval_num_ge_true_when_greater();
- test_eval_num_ge_true_when_equal();
- test_eval_num_ge_false_when_less();
- test_eval_num_lt_false_when_greater();
- test_eval_num_lt_false_when_equal();
- test_eval_num_lt_true_when_less();
- test_eval_num_le_false_when_greater();
- test_eval_num_le_true_when_equal();
- test_eval_num_le_true_when_less();
-
- test_eval_num_pop();
- test_eval_num_exch();
- test_eval_num_dup();
- test_eval_num_index();
- test_eval_num_index_top();
- test_eval_num_roll();
-
- test_eval_num_def();
-
- test_eval_exec_array_num();
- test_eval_exec_array_num_many();
- test_eval_exec_array_func();
- test_eval_exec_array_num_nested();
- test_eval_exec_array_func_nested();
- test_eval_exec_array_exec_nested();
-
- test_eval_exec_nums();
- test_eval_exec_func();
- test_eval_exec_func_in_executable_array();
- test_eval_if_true();
- test_eval_if_false();
- test_eval_ifelse_true();
- test_eval_ifelse_false();
+ // test_eval_num_one();
+ // test_eval_num_two();
+ // test_eval_num_two_separated_by_newline();
+ // test_eval_num_add();
+ // test_eval_num_add_many();
+ // test_eval_num_sub();
+ // test_eval_num_mul();
+ // test_eval_num_div();
+ // test_eval_num_mod();
+ // test_eval_num_eq_true();
+
+ // test_eval_num_eq_false();
+ // test_eval_num_neq_true();
+ // test_eval_num_neq_false();
+ // test_eval_num_gt_true_when_greater();
+ // test_eval_num_gt_false_when_equal();
+ // test_eval_num_gt_false_when_less();
+ // test_eval_num_ge_true_when_greater();
+ // test_eval_num_ge_true_when_equal();
+ // test_eval_num_ge_false_when_less();
+ // test_eval_num_lt_false_when_greater();
+
+ // test_eval_num_lt_false_when_equal();
+ // test_eval_num_lt_true_when_less();
+ // test_eval_num_le_false_when_greater();
+ // test_eval_num_le_true_when_equal();
+ // test_eval_num_le_true_when_less();
+
+ // test_eval_num_pop();
+ // test_eval_num_exch();
+ // test_eval_num_dup();
+ // test_eval_num_index();
+ // test_eval_num_index_top();
+ // test_eval_num_roll();
+
+ // test_eval_num_def();
+
+ // test_eval_exec_array_num();
+ // test_eval_exec_array_num_many();
+ // test_eval_exec_array_func();
+ // test_eval_exec_array_num_nested();
+ // test_eval_exec_array_func_nested();
+ // test_eval_exec_array_exec_nested();
+
+ // test_eval_exec_nums();
+ // test_eval_exec_func();
+ // test_eval_exec_func_in_executable_array();
+ // test_eval_if_true();
+ // test_eval_if_false();
+ // test_eval_ifelse_true();
+ // test_eval_ifelse_false();
test_eval_ifelse_true_in_executable_array();
- test_eval_ifelse_false_in_executable_array();
- test_eval_ifelse_then_num();
- test_eval_ifelse_incomplete_executable_array();
- test_eval_while();
- test_eval_while_in_executable_array();
- test_eval_while_then_num();
-
- test_eval_comments();
-
- // test_eval_factorial();
-
- test_dict_no_element_name_not_found();
- test_dict_name_found();
- test_dict_name_found_when_hash_collides();
- test_dict_name_not_found();
- test_dict_overwritten_name_found();
- test_dict_overwritten_not_head_name_found();
+ // test_eval_ifelse_false_in_executable_array();
+ // test_eval_ifelse_then_num();
+ // test_eval_ifelse_incomplete_executable_array();
+ // test_eval_while();
+ // test_eval_while_in_executable_array();
+ // test_eval_while_then_num();
+
+ // test_eval_comments();
+
+ // // test_eval_factorial();
+
+ // test_dict_no_element_name_not_found();
+ // test_dict_name_found();
+ // test_dict_name_found_when_hash_collides();
+ // test_dict_name_not_found();
+ // test_dict_overwritten_name_found();
+ // test_dict_overwritten_not_head_name_found();
}
\ No newline at end of file
するとスタックの内容が以下のようになりました。3
消えました! expectsのスタックの順が逆なのでassertで落ちてますね🦀
$ gcc *.c && ./a.out
--- stack ---
3: ET_NUMBER 300
2: ET_NUMBER 200
1: ET_NUMBER 100
0: ET_NUMBER 4
-------------
a.out: eval.c:381: verify_stack_pop_number_eq: Assertion `actual.u.number == expects[i]' failed.
スタックにどこかでゴミが残ってそうだなーと考えてスタックのコードを見てみたところ、スタックをテスト毎に初期化するコードがなさげだったので、前のテストの状態が残っていそうだな、と気づいたのでした。