1327 - better error handling in chessboard

Also a bugfix in break to label, because I noticed the screen wasn't
being cleaned up on quit.
This commit is contained in:
Kartik K. Agaram 2015-05-10 11:38:18 -07:00
parent 6b16a2ef6b
commit 4071055aee
9 changed files with 187 additions and 57 deletions

View File

@ -130,7 +130,8 @@ if (argc > 1) {
:(before "End Main")
if (!Run_tests) {
setup();
Trace_stream = new trace_stream;
//? Trace_file = "interactive"; //? 1
START_TRACING_UNTIL_END_OF_SCOPE;
//? Trace_stream->dump_layer = "all"; //? 2
transform_all();
recipe_number r = Recipe_number[string("main")];

View File

@ -42,6 +42,7 @@ Recipe_number["jump-if"] = JUMP_IF;
:(before "End Primitive Recipe Implementations")
case JUMP_IF: {
assert(current_instruction().ingredients.at(1).initialized);
trace("AAA") << current_instruction().to_string() << '\n';
assert(ingredients.size() == 2);
assert(ingredients.at(0).size() == 1); // scalar
if (!ingredients.at(0).at(0)) {

View File

@ -15,9 +15,8 @@ case ASSERT: {
assert(ingredients.at(0).size() == 1); // scalar
if (!ingredients.at(0).at(0)) {
assert(isa_literal(current_instruction().ingredients.at(1)));
//? tb_shutdown(); //? 1
tb_shutdown();
raise << current_instruction().ingredients.at(1).name << '\n' << die();
//? exit(0); //? 1
}
break;
}

View File

@ -62,6 +62,7 @@ recipe f [
//: ingredients unless they're also products. The /same-as-ingredient inside
//: the recipe's 'reply' will help catch accidental misuse of such
//: 'ingredient-results' (sometimes called in-out parameters in other languages).
:(scenario reply_same_as_ingredient)
% Hide_warnings = true;
recipe main [
@ -90,3 +91,44 @@ string to_string(const vector<long long int>& in) {
out << "]";
return out.str();
}
//: Conditional reply.
:(scenario reply_if)
recipe main [
1:integer <- test1
]
recipe test1 [
reply-if 0:literal, 34:literal
reply 35:literal
]
+mem: storing 35 in location 1
:(scenario reply_if2)
recipe main [
1:integer <- test1
]
recipe test1 [
reply-if 1:literal, 34:literal
reply 35:literal
]
+mem: storing 34 in location 1
:(before "End Rewrite Instruction(curr)")
// rewrite `reply-if a, b, c, ...` to
// ```
// jump-unless a, 1:offset
// reply b, c, ...
// ```
if (curr.name == "reply-if") {
assert(curr.products.empty());
curr.operation = Recipe_number["jump-unless"];
vector<reagent> results;
copy(++curr.ingredients.begin(), curr.ingredients.end(), inserter(results, results.end()));
curr.ingredients.resize(1);
curr.ingredients.push_back(reagent("1:offset"));
result.steps.push_back(curr);
curr.clear();
curr.operation = Recipe_number["reply"];
curr.ingredients.swap(results);
}

View File

@ -24,25 +24,25 @@ void transform_labels(const recipe_number r) {
instruction& inst = Recipe[r].steps.at(i);
if (inst.operation == Recipe_number["jump"]) {
//? cerr << inst.to_string() << '\n'; //? 1
replace_offset(inst.ingredients.at(0), offset, r);
replace_offset(inst.ingredients.at(0), offset, i, r);
}
if (inst.operation == Recipe_number["jump-if"] || inst.operation == Recipe_number["jump-unless"]) {
replace_offset(inst.ingredients.at(1), offset, r);
replace_offset(inst.ingredients.at(1), offset, i, r);
}
if ((inst.operation == Recipe_number["loop"] || inst.operation == Recipe_number["break"])
&& inst.ingredients.size() == 1) {
replace_offset(inst.ingredients.at(0), offset, r);
replace_offset(inst.ingredients.at(0), offset, i, r);
}
if ((inst.operation == Recipe_number["loop-if"] || inst.operation == Recipe_number["loop-unless"]
|| inst.operation == Recipe_number["break-if"] || inst.operation == Recipe_number["break-unless"])
&& inst.ingredients.size() == 2) {
replace_offset(inst.ingredients.at(1), offset, r);
replace_offset(inst.ingredients.at(1), offset, i, r);
}
}
}
:(code)
void replace_offset(reagent& x, /*const*/ map<string, index_t>& offset, const recipe_number r) {
void replace_offset(reagent& x, /*const*/ map<string, index_t>& offset, const index_t current_offset, const recipe_number r) {
//? cerr << "AAA " << x.to_string() << '\n'; //? 1
assert(isa_literal(x));
//? cerr << "BBB " << x.to_string() << '\n'; //? 1
@ -52,7 +52,7 @@ void replace_offset(reagent& x, /*const*/ map<string, index_t>& offset, const re
//? cerr << "DDD " << x.to_string() << '\n'; //? 1
if (offset.find(x.name) == offset.end())
raise << "can't find label " << x.name << " in routine " << Recipe[r].name << '\n';
x.set_value(offset[x.name]);
x.set_value(offset[x.name]-current_offset);
}
:(scenario break_to_label)
@ -91,3 +91,17 @@ recipe main [
+target
]
-mem: storing 0 in location 1
:(scenario jump_runs_code_after_label)
recipe main [
# first a few lines of padding to exercise the offset computation
1:integer <- copy 0:literal
2:integer <- copy 0:literal
3:integer <- copy 0:literal
jump +target:offset
4:integer <- copy 0:literal
+target
5:integer <- copy 0:literal
]
+mem: storing 0 in location 5
-mem: storing 0 in location 4

View File

@ -88,7 +88,7 @@ time_t mu_time; time(&mu_time);
cerr << "\nMu tests: " << ctime(&mu_time);
for (index_t i = 0; i < Scenarios.size(); ++i) {
//? cerr << Passed << '\n'; //? 1
//? cerr << i << ": " << Scenarios.at(i).name << '\n'; //? 1
//? cerr << i << ": " << Scenarios.at(i).name << '\n'; //? 2
run_mu_scenario(Scenarios.at(i));
if (Passed) cerr << ".";
}
@ -294,6 +294,10 @@ recipe main [
// Like runs of contiguous '+' lines, order is important. The trace checks
// that the lines are present *and* in the specified sequence. (There can be
// other lines in between.)
//
// Be careful not to mix setting Hide_warnings and checking the trace in .mu
// files. It'll work in C++ scenarios, but the test failure gets silently
// hidden in mu scenarios.
:(scenario trace_check_warns_on_failure)
% Hide_warnings = true;
@ -319,8 +323,10 @@ case TRACE_SHOULD_CONTAIN: {
// simplified version of check_trace_contents() that emits warnings rather
// than just printing to stderr
bool check_trace(const string& expected) {
//? cerr << "AAA " << expected << '\n'; //? 1
Trace_stream->newline();
vector<pair<string, string> > expected_lines = parse_trace(expected);
//? cerr << "BBB " << expected_lines.size() << '\n'; //? 1
if (expected_lines.empty()) return true;
index_t curr_expected_line = 0;
for (vector<pair<string, pair<int, string> > >::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) {
@ -328,7 +334,10 @@ bool check_trace(const string& expected) {
if (expected_lines.at(curr_expected_line).second != p->second.second) continue;
// match
++curr_expected_line;
if (curr_expected_line == expected_lines.size()) return true;
if (curr_expected_line == expected_lines.size()) {
//? cerr << "ZZZ\n"; //? 1
return true;
}
}
raise << "missing [" << expected_lines.at(curr_expected_line).second << "] "

View File

@ -152,100 +152,148 @@ container move [
to-rank:integer
]
# result:address:move <- read-move stdin:address:channel
# result:address:move, quit?:boolean, error?:boolean <- read-move stdin:address:channel, screen:address
# prints only error messages to screen
recipe read-move [
default-space:address:array:location <- new location:type, 30:literal
stdin:address:channel <- next-ingredient
from-file:integer <- read-file stdin:address:channel
screen:address <- next-ingredient
#? $print screen:address #? 1
from-file:integer, quit?:boolean, error?:boolean <- read-file stdin:address:channel, screen:address
reply-if quit?:boolean, 0:literal/dummy, quit?:boolean, error?:boolean
reply-if error?:boolean, 0:literal/dummy, quit?:boolean, error?:boolean
#? return-to-console #? 1
{
q-pressed?:boolean <- lesser-than from-file:integer, 0:literal
break-unless q-pressed?:boolean
reply 0:literal
}
# construct the move object
result:address:move <- new move:literal
x:address:integer <- get-address result:address:move/deref, from-file:offset
x:address:integer/deref <- copy from-file:integer
x:address:integer <- get-address result:address:move/deref, from-rank:offset
x:address:integer/deref <- read-rank stdin:address:channel
expect-from-channel stdin:address:channel, 45:literal # '-'
x:address:integer/deref, quit?:boolean, error?:boolean <- read-rank stdin:address:channel, screen:address
reply-if quit?:boolean, 0:literal/dummy, quit?:boolean, error?:boolean
reply-if error?:boolean, 0:literal/dummy, quit?:boolean, error?:boolean
error?:boolean <- expect-from-channel stdin:address:channel, 45:literal/dash, screen:address
reply-if error?:boolean, 0:literal/dummy, 0:literal/quit, error?:boolean
x:address:integer <- get-address result:address:move/deref, to-file:offset
x:address:integer/deref <- read-file stdin:address:channel
x:address:integer/deref, quit?:boolean, error?:boolean <- read-file stdin:address:channel, screen:address
reply-if quit?:boolean, 0:literal/dummy, quit?:boolean, error?:boolean
reply-if error?:boolean, 0:literal/dummy, quit?:boolean, error?:boolean
x:address:integer <- get-address result:address:move/deref, to-rank:offset
x:address:integer/deref <- read-rank stdin:address:channel
x:address:integer/deref, quit?:boolean, error?:boolean <- read-rank stdin:address:channel, screen:address
reply-if quit?:boolean, 0:literal/dummy, quit?:boolean, error?:boolean
reply-if error?:boolean, 0:literal/dummy, quit?:boolean, error?:boolean
#? $exit #? 1
expect-from-channel stdin:address:channel, 13:literal # newline
reply result:address:move
error?:boolean <- expect-from-channel stdin:address:channel, 13:literal/newline, screen:address
reply-if error?:boolean, 0:literal/dummy, 0:literal/quit, error?:boolean
reply result:address:move, quit?:boolean, error?:boolean
]
# file:integer, quit:boolean, error:boolean <- read-file stdin:address:channel, screen:address
# valid values for file: 0-7
recipe read-file [
default-space:address:array:location <- new location:type, 30:literal
stdin:address:channel <- next-ingredient
screen:address <- next-ingredient
c:character, stdin:address:channel <- read stdin:address:channel
{
q-pressed?:boolean <- equal c:character, 81:literal # 'Q'
break-unless q-pressed?:boolean
reply -1:literal
reply 0:literal/dummy, 1:literal/quit, 0:literal/error
}
{
q-pressed?:boolean <- equal c:character, 113:literal # 'q'
break-unless q-pressed?:boolean
reply -1:literal
reply 0:literal/dummy, 1:literal/quit, 0:literal/error
}
file:integer <- subtract c:character, 97:literal # 'a'
#? $print file:integer, [ #? 1
#? ] #? 1
# 'a' <= file <= 'h'
above-min:boolean <- greater-or-equal file:integer, 0:literal
assert above-min:boolean [file too low]
below-max:boolean <- lesser-than file:integer, 8:literal
assert below-max:boolean [file too high]
reply file:integer
{
above-min:boolean <- greater-or-equal file:integer, 0:literal
break-if above-min:boolean
error-message:address:array:character <- new [file too low: ]
print-string screen:address, error-message:address:array:character
print-character screen:address, c:character
cursor-to-next-line screen:address
reply 0:literal/dummy, 0:literal/quit, 1:literal/error
}
{
below-max:boolean <- lesser-than file:integer, 8:literal
break-if below-max:boolean
error-message:address:array:character <- new [file too high: ]
print-string screen:address, error-message:address:array:character
print-character screen:address, c:character
reply 0:literal/dummy, 0:literal/quit, 1:literal/error
}
reply file:integer, 0:literal/quit, 0:literal/error
]
# rank:integer <- read-rank stdin:address:channel, screen:address
# valid values: 0-7, -1 (quit), -2 (error)
recipe read-rank [
default-space:address:array:location <- new location:type, 30:literal
stdin:address:channel <- next-ingredient
screen:address <- next-ingredient
c:character, stdin:address:channel <- read stdin:address:channel
{
q-pressed?:boolean <- equal c:character, 81:literal # 'Q'
break-unless q-pressed?:boolean
reply -1:literal
reply 0:literal/dummy, 1:literal/quit, 0:literal/error
}
{
q-pressed?:boolean <- equal c:character, 113:literal # 'q'
break-unless q-pressed?:boolean
reply -1:literal
reply 0:literal/dummy, 1:literal/quit, 0:literal/error
}
rank:integer <- subtract c:character, 49:literal # '1'
#? $print rank:integer, [ #? 1
#? ] #? 1
# assert'1' <= rank <= '8'
above-min:boolean <- greater-or-equal rank:integer 0:literal
assert above-min:boolean [rank too low]
below-max:boolean <- lesser-or-equal rank:integer 7:literal
assert below-max:boolean [rank too high]
reply rank:integer
{
above-min:boolean <- greater-or-equal rank:integer 0:literal
break-if above-min:boolean
error-message:address:array:character <- new [rank too low: ]
print-string screen:address, error-message:address:array:character
print-character screen:address, c:character
reply 0:literal/dummy, 0:literal/quit, 1:literal/error
}
{
below-max:boolean <- lesser-or-equal rank:integer 7:literal
break-if below-max:boolean
error-message:address:array:character <- new [rank too high: ]
print-string screen:address, error-message:address:array:character
print-character screen:address, c:character
reply 0:literal/dummy, 0:literal/quit, 1:literal/error
}
reply rank:integer, 0:literal/quit, 0:literal/error
]
# read a character from the given channel and check that it's what we expect
# return true on error
recipe expect-from-channel [
default-space:address:array:location <- new location:type, 30:literal
stdin:address:channel <- next-ingredient
expected:character <- next-ingredient
screen:address <- next-ingredient
c:character, stdin:address:channel <- read stdin:address:channel
match?:boolean <- equal c:character, expected:character
assert match?:boolean [expected character not found]
{
break-if match?:boolean
s:address:array:character <- new [expected character not found]
print-string screen:address, s:address:array:character
}
result:boolean <- not match?:boolean
reply result:boolean
]
scenario read-move-blocking [
assume-screen 20:literal/width, 2:literal/height
run [
#? $start-tracing #? 1
1:address:channel <- init-channel 2:literal
#? $print [aaa channel address: ], 1:address:channel, [ #? 1
#? ] #? 1
2:integer/routine <- start-running read-move:recipe, 1:address:channel
2:integer/routine <- start-running read-move:recipe, 1:address:channel, screen:address
# 'read-move' is waiting for input
wait-for-routine 2:integer
#? $print [bbb channel address: ], 1:address:channel, [ #? 1
@ -333,9 +381,10 @@ F read-move-blocking: routine failed to terminate on newline]
]
scenario read-move-quit [
assume-screen 20:literal/width, 2:literal/height
run [
1:address:channel <- init-channel 2:literal
2:integer/routine <- start-running read-move:recipe, 1:address:channel
2:integer/routine <- start-running read-move:recipe, 1:address:channel, screen:address
# 'read-move' is waiting for input
wait-for-routine 2:integer
3:integer <- routine-state 2:integer/id
@ -359,10 +408,10 @@ F read-move-quit: routine failed to terminate on 'q']
]
scenario read-move-illegal-file [
assume-screen 20:literal/width, 2:literal/height
run [
hide-warnings
1:address:channel <- init-channel 2:literal
2:integer/routine <- start-running read-move:recipe, 1:address:channel
2:integer/routine <- start-running read-move:recipe, 1:address:channel, screen:address
# 'read-move' is waiting for input
wait-for-routine 2:integer
3:integer <- routine-state 2:integer/id
@ -373,16 +422,17 @@ F read-move-file: routine failed to pause after coming up (before any keys were
restart 2:integer/routine
wait-for-routine 2:integer
]
trace-should-contain [
warn: file too low
screen-should-contain [
.file too low: 2 .
. .
]
]
scenario read-move-illegal-rank [
assume-screen 20:literal/width, 2:literal/height
run [
hide-warnings
1:address:channel <- init-channel 2:literal
2:integer/routine <- start-running read-move:recipe, 1:address:channel
2:integer/routine <- start-running read-move:recipe, 1:address:channel, screen:address
# 'read-move' is waiting for input
wait-for-routine 2:integer
3:integer <- routine-state 2:integer/id
@ -394,8 +444,9 @@ F read-move-file: routine failed to pause after coming up (before any keys were
restart 2:integer/routine
wait-for-routine 2:integer
]
trace-should-contain [
warn: rank too high
screen-should-contain [
.rank too high: a .
. .
]
]
@ -496,15 +547,20 @@ recipe chessboard [
msg:address:array:character <- new [Hit 'q' to exit.
]
print-string 0:literal/screen, msg:address:array:character
cursor-to-next-line 0:literal/screen
msg:address:array:character <- new [move: ]
print-string 0:literal/screen, msg:address:array:character
m:address:move <- read-move buffered-stdin:address:channel
break-unless m:address:move
{
cursor-to-next-line 0:literal/screen
msg:address:array:character <- new [move: ]
print-string 0:literal/screen, msg:address:array:character
m:address:move, quit:boolean, error:boolean <- read-move buffered-stdin:address:channel, 0:literal/screen
break-if quit:boolean, +quit:offset
loop-if error:boolean
}
board:address:array:address:array:character <- make-move board:address:array:address:array:character, m:address:move
clear-screen 0:literal/screen
loop
}
+quit
#? $print [aaa] #? 1
return-to-console
]

2
mu.vim
View File

@ -35,7 +35,7 @@ syntax match muDelimiter "[{}\[\]]" | highlight link muDelimiter Delimiter
syntax match muLabel " [^a-zA-Z0-9 \[][a-zA-Z0-9-]\+" | highlight link muLabel Function
syntax match muAssign " <- " | highlight link muAssign SpecialChar
syntax match muAssign "\<raw\>"
syntax keyword muControl reply jump jump-if jump-unless loop loop-if loop-unless break-if break-unless | highlight link muControl Function
syntax keyword muControl reply reply-if reply-unless jump jump-if jump-unless loop loop-if loop-unless break-if break-unless | highlight link muControl Function
" common keywords
syntax keyword muFunc recipe default-space next-ingredient ingredient before after scenario run memory trace screen keyboard stalled finished | highlight link muFunc Statement

View File

@ -158,6 +158,8 @@ void tb_present(void)
int x,y,w,i;
struct tb_cell *back, *front;
assert(termw != -1);
/* invalidate cursor position */
lastx = LAST_COORD_INIT;
lasty = LAST_COORD_INIT;
@ -203,12 +205,11 @@ void tb_present(void)
void tb_set_cursor(int cx, int cy)
{
assert(termw != -1);
if (IS_CURSOR_HIDDEN(cursor_x, cursor_y) && !IS_CURSOR_HIDDEN(cx, cy))
bytebuffer_puts(&output_buffer, funcs[T_SHOW_CURSOR]);
if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y) && IS_CURSOR_HIDDEN(cx, cy))
bytebuffer_puts(&output_buffer, funcs[T_HIDE_CURSOR]);
cursor_x = cx;
cursor_y = cy;
if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))
@ -217,6 +218,7 @@ void tb_set_cursor(int cx, int cy)
void tb_change_cell(int x, int y, uint32_t ch, uint16_t fg, uint16_t bg)
{
assert(termw != -1);
if ((unsigned)x >= (unsigned)back_buffer.width)
return;
if ((unsigned)y >= (unsigned)back_buffer.height)
@ -232,6 +234,7 @@ struct tb_cell *tb_cell_buffer()
int tb_poll_event(struct tb_event *event)
{
assert(termw != -1);
return wait_fill_event(event, 0);
}
@ -240,21 +243,25 @@ int tb_peek_event(struct tb_event *event, int timeout)
struct timeval tv;
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000;
assert(termw != -1);
return wait_fill_event(event, &tv);
}
int tb_width(void)
{
assert(termw != -1);
return termw;
}
int tb_height(void)
{
assert(termw != -1);
return termh;
}
void tb_clear(void)
{
assert(termw != -1);
if (buffer_size_change_request) {
update_size();
buffer_size_change_request = 0;
@ -264,6 +271,7 @@ void tb_clear(void)
void tb_set_clear_attributes(uint16_t fg, uint16_t bg)
{
assert(termw != -1);
foreground = fg;
background = bg;
}