diff --git a/NEWS.md b/NEWS.md index 98b52024b2e8..5251096d9f2a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,13 @@ # News +## 5.1.1 + +This is a production release that completes a bug fix from `5.1.0`. The bug +exists in all versions of `bc`. + +The bug was that `if` statements without `else` statements would not be handled +correctly at the end of files or right before a function definition. + ## 5.1.0 This is a production release with some fixes and new features. diff --git a/include/bc.h b/include/bc.h index 3d4a11592875..a4198b91ebc6 100644 --- a/include/bc.h +++ b/include/bc.h @@ -396,6 +396,15 @@ void bc_parse_expr(BcParse *p, uint8_t flags); */ void bc_parse_parse(BcParse *p); +/** + * Ends a series of if statements. This is to ensure that full parses happen + * when a file finishes or before defining a function. Without this, bc thinks + * that it cannot parse any further. But if we reach the end of a file or a + * function definition, we know we can add an empty else clause. + * @param p The parser. + */ +void bc_parse_endif(BcParse *p); + /// References to the signal message and its length. extern const char bc_sig_msg[]; extern const uchar bc_sig_msg_len; diff --git a/include/version.h b/include/version.h index 3be823189b8f..72500c8e3f28 100644 --- a/include/version.h +++ b/include/version.h @@ -37,6 +37,6 @@ #define BC_VERSION_H /// The current version. -#define VERSION 5.1.0 +#define VERSION 5.1.1 #endif // BC_VERSION_H diff --git a/manuals/build.md b/manuals/build.md index 13e969e8e673..1ed2b269f13c 100644 --- a/manuals/build.md +++ b/manuals/build.md @@ -65,7 +65,7 @@ with Visual Studio*. To build `bc`, run the following from the root directory: ``` -msbuild -property:Configuration= bc.sln +msbuild -property:Configuration= vs/bc.sln ``` where `` is either one of `Debug` or `Release`. @@ -73,10 +73,10 @@ where `` is either one of `Debug` or `Release`. To build the library, run the following from the root directory: ``` -msbuild -property:Configuration= bcl.sln +msbuild -property:Configuration= vs/bcl.sln ``` -where `` is either one of `Debug` or `Release`. +where `` is either one of `Debug`, `ReleaseMD`, or `ReleaseMT`. ## POSIX-Compatible Systems diff --git a/src/bc_parse.c b/src/bc_parse.c index c64121ec5da8..c2fc2186a065 100644 --- a/src/bc_parse.c +++ b/src/bc_parse.c @@ -999,6 +999,44 @@ static void bc_parse_startBody(BcParse *p, uint16_t flags) { bc_vec_push(&p->flags, &flags); } +void bc_parse_endif(BcParse *p) { + + size_t i; + bool good; + + // Not a problem if this is true. + if (BC_NO_ERR(!BC_PARSE_NO_EXEC(p))) return; + + good = true; + + // Find an instance of a body that needs closing, i.e., a statement that did + // not have a right brace when it should have. + for (i = 0; good && i < p->flags.len; ++i) { + uint16_t flag = *((uint16_t*) bc_vec_item(&p->flags, i)); + good = ((flag & BC_PARSE_FLAG_BRACE) != BC_PARSE_FLAG_BRACE); + } + + // If we did not find such an instance... + if (good) { + + // We set this to restore it later. We don't want the parser thinking + // that we are on stdin for this one because it will want more. + bool is_stdin = vm.is_stdin; + + vm.is_stdin = false; + + // End all of the if statements and loops. + while (p->flags.len > 1 || BC_PARSE_IF_END(p)) { + if (BC_PARSE_IF_END(p)) bc_parse_noElse(p); + if (p->flags.len > 1) bc_parse_endBody(p, false); + } + + vm.is_stdin = is_stdin; + } + // If we reach here, a block was not properly closed, and we should error. + else bc_parse_err(&vm.prs, BC_ERR_PARSE_BLOCK); +} + /** * Parses an if statement. * @param p The parser. @@ -1730,12 +1768,9 @@ void bc_parse_parse(BcParse *p) { // Functions need special parsing. else if (p->l.t == BC_LEX_KW_DEFINE) { if (BC_ERR(BC_PARSE_NO_EXEC(p))) { - if (p->flags.len == 1 && - BC_PARSE_TOP_FLAG(p) == BC_PARSE_FLAG_IF_END) - { - bc_parse_noElse(p); - } - else bc_parse_err(p, BC_ERR_PARSE_TOKEN); + bc_parse_endif(p); + if (BC_ERR(BC_PARSE_NO_EXEC(p))) + bc_parse_err(p, BC_ERR_PARSE_TOKEN); } bc_parse_func(p); } diff --git a/src/vm.c b/src/vm.c index 8f222f8ccf69..853dff0820dd 100644 --- a/src/vm.c +++ b/src/vm.c @@ -839,45 +839,14 @@ static void bc_vm_process(const char *text, bool is_stdin) { #if BC_ENABLED /** - * Ends an if statement that ends a file. This is to ensure that full parses - * happen when a file finishes. Without this, bc thinks that it cannot parse - * any further. But if we reach the end of a file, we know we can add an empty - * else clause. + * Ends a series of if statements. This is to ensure that full parses happen + * when a file finishes or stdin has no more data. Without this, bc thinks that + * it cannot parse any further. But if we reach the end of a file or stdin has + * no more data, we know we can add an empty else clause. */ static void bc_vm_endif(void) { - - size_t i; - bool good; - - // Not a problem if this is true. - if (BC_NO_ERR(!BC_PARSE_NO_EXEC(&vm.prs))) return; - - good = true; - - // Find an instance of a body that needs closing, i.e., a statement that did - // not have a right brace when it should have. - for (i = 0; good && i < vm.prs.flags.len; ++i) { - uint16_t flag = *((uint16_t*) bc_vec_item(&vm.prs.flags, i)); - good = ((flag & BC_PARSE_FLAG_BRACE) != BC_PARSE_FLAG_BRACE); - } - - // If we did not find such an instance... - if (good) { - - // We set this to restore it later. We don't want the parser thinking - // that we are on stdin for this one because it will want more. - bool is_stdin = vm.is_stdin; - - vm.is_stdin = false; - - // Cheat and keep parsing empty else clauses until all of them are - // satisfied. - while (BC_PARSE_IF_END(&vm.prs)) bc_vm_process("else {}", false); - - vm.is_stdin = is_stdin; - } - // If we reach here, a block was not properly closed, and we should error. - else bc_parse_err(&vm.prs, BC_ERR_PARSE_BLOCK); + bc_parse_endif(&vm.prs); + bc_program_exec(&vm.prog); } #endif // BC_ENABLED diff --git a/tests/bc/scripts/all.txt b/tests/bc/scripts/all.txt index 4ebfe5643c3d..0a8d2fe17c6c 100644 --- a/tests/bc/scripts/all.txt +++ b/tests/bc/scripts/all.txt @@ -14,3 +14,5 @@ rand.bc references.bc screen.bc strings2.bc +ifs.bc +ifs2.bc diff --git a/tests/bc/scripts/ifs.bc b/tests/bc/scripts/ifs.bc new file mode 100644 index 000000000000..9d6ab2dbb0ef --- /dev/null +++ b/tests/bc/scripts/ifs.bc @@ -0,0 +1,49 @@ +#! /usr/bin/bc -q + +a = 1 +b = 2 +c = 3 + +if (a == 1) if (b == 2) if (c == 3) print "Yay!\n" + +define void g(x) { + print "g: x: ", x, "\n" +} + +if (a == 1) { + if (b == 2) { + if (c == 3) { + g(5) + } + } +} + +define void h(x) { + print "h: x: ", x, "\n" +} + +if (z == 0) + for (i = 0; i < 2; ++i) + if (a == 1) + for (j = 0; j < 2; ++j) + if (b == 2) + for (k = 0; k < 2; ++k) + if (c == 3) h(k) + +define void i(x) { + print "i: x: ", x, "\n" +} + +if (z == 0) { + for (i = 0; i < 2; ++i) { + if (a == 1) { + for (j = 0; j < 2; ++j) { + if (b == 2) { + for (k = 0; k < 2; ++k) { + if (c == 3) i(k) + } + } + } + } + } +} diff --git a/tests/bc/scripts/ifs.txt b/tests/bc/scripts/ifs.txt new file mode 100644 index 000000000000..56e07330f1f2 --- /dev/null +++ b/tests/bc/scripts/ifs.txt @@ -0,0 +1,18 @@ +Yay! +g: x: 5 +h: x: 0 +h: x: 1 +h: x: 0 +h: x: 1 +h: x: 0 +h: x: 1 +h: x: 0 +h: x: 1 +i: x: 0 +i: x: 1 +i: x: 0 +i: x: 1 +i: x: 0 +i: x: 1 +i: x: 0 +i: x: 1 diff --git a/tests/bc/scripts/ifs2.bc b/tests/bc/scripts/ifs2.bc new file mode 100644 index 000000000000..052ef06ee4e3 --- /dev/null +++ b/tests/bc/scripts/ifs2.bc @@ -0,0 +1,33 @@ +#! /usr/bin/bc -q + +a = 1 +b = 2 +c = 3 + +if (a == 1) if (b == 2) if (c == 3) print "Yay!\n" + +define void g(x) { + print "g: x: ", x, "\n" +} + +if (a == 1) { + if (b == 2) { + if (c == 3) { + g(5) + } + } +} + +define void h(x) { + print "h: x: ", x, "\n" +} + +if (z == 0) + for (i = 0; i < 2; ++i) + for (l = 0; l < 2; ++l) + if (a == 1) + for (j = 0; j < 2; ++j) + for (m = 0; m < 2; ++m) + if (b == 2) + for (k = 0; k < 2; ++k) + if (c == 3) h(k) diff --git a/tests/bc/scripts/ifs2.txt b/tests/bc/scripts/ifs2.txt new file mode 100644 index 000000000000..b226e98ad44b --- /dev/null +++ b/tests/bc/scripts/ifs2.txt @@ -0,0 +1,34 @@ +Yay! +g: x: 5 +h: x: 0 +h: x: 1 +h: x: 0 +h: x: 1 +h: x: 0 +h: x: 1 +h: x: 0 +h: x: 1 +h: x: 0 +h: x: 1 +h: x: 0 +h: x: 1 +h: x: 0 +h: x: 1 +h: x: 0 +h: x: 1 +h: x: 0 +h: x: 1 +h: x: 0 +h: x: 1 +h: x: 0 +h: x: 1 +h: x: 0 +h: x: 1 +h: x: 0 +h: x: 1 +h: x: 0 +h: x: 1 +h: x: 0 +h: x: 1 +h: x: 0 +h: x: 1 diff --git a/tests/bc/stdin2.txt b/tests/bc/stdin2.txt index f260cfa7dbcf..a7f1981c6658 100644 --- a/tests/bc/stdin2.txt +++ b/tests/bc/stdin2.txt @@ -1 +1,4 @@ for (i = 0; i < 3; ++i) if (2 < 3) 1 +if (3 < 4) for (i = 0; i < 3; ++i) if (4 < 5) 2 +for (j = 0; j < 3; ++j) if (5 < 6) for (i = 0; i < 3; ++i) if (4 < 5) 3 +if (6 < 7) for (j = 0; j < 3; ++j) if (5 < 6) for (i = 0; i < 3; ++i) if (4 < 5) 4 diff --git a/tests/bc/stdin2_results.txt b/tests/bc/stdin2_results.txt index e8183f05f5db..43e2b02f53f2 100644 --- a/tests/bc/stdin2_results.txt +++ b/tests/bc/stdin2_results.txt @@ -1,3 +1,24 @@ 1 1 1 +2 +2 +2 +3 +3 +3 +3 +3 +3 +3 +3 +3 +4 +4 +4 +4 +4 +4 +4 +4 +4 diff --git a/tests/stdin.sh b/tests/stdin.sh index c9e02253c30a..69e6f2cabf34 100755 --- a/tests/stdin.sh +++ b/tests/stdin.sh @@ -92,10 +92,10 @@ checktest "$d" "$?" "stdin" "$testdir/$d/stdin_results.txt" "$out" if [ "$d" = "bc" ]; then cat "$testdir/$d/stdin1.txt" | "$exe" "$@" "$options" > "$out" 2> /dev/null - checktest "$d" "$?" "stdin" "$testdir/$d/stdin1_results.txt" "$out" + checktest "$d" "$?" "stdin1" "$testdir/$d/stdin1_results.txt" "$out" cat "$testdir/$d/stdin2.txt" | "$exe" "$@" "$options" > "$out" 2> /dev/null - checktest "$d" "$?" "stdin" "$testdir/$d/stdin2_results.txt" "$out" + checktest "$d" "$?" "stdin2" "$testdir/$d/stdin2_results.txt" "$out" fi rm -f "$out" diff --git a/vs/bin/some.txt b/vs/bin/some.txt deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/vs/tests/some.txt b/vs/tests/some.txt deleted file mode 100644 index e69de29bb2d1..000000000000