Skip to content

Commit

Permalink
Fixed a bug with the Select Statement (#20)
Browse files Browse the repository at this point in the history
* Add time.Sleep()

* Temporarily bandaid GC tests

Note that adding to the standard library increases the min number of words that Ooga needs to operate

* Make select block if no cases match

We previously assumed there was always a default case but that's actually not the case at all

* Optimise the compilation by not placing BLOCK_THREAD and GOTO start if has default

If there is a default case, these machine instructions will never be reached.
  • Loading branch information
JothamWong authored Apr 16, 2024
1 parent a87a161 commit 9ee648a
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 14 deletions.
20 changes: 8 additions & 12 deletions src/parser/ooga.pegjs
Original file line number Diff line number Diff line change
Expand Up @@ -1217,7 +1217,7 @@ ChannelWriteExpression
channel: channel,
value: value
};
}
}

// Select
// These are select statements as per the Go spec
Expand All @@ -1243,18 +1243,14 @@ ChannelOperation
= ChannelReadExpression
/ ChannelWriteExpression


SelectCaseBlock
= "{" __ clauses:(SelectClause)* __ def:SelectDefaultClause? __ "}" {
// Add a default case if none is present
return optionalList(clauses)
.concat(
def || {
tag: "SelectDefaultCase",
body: {tag: "BlockStatement", body: []}
}
);
}
= "{" __ clauses:(SelectClause)* __ def:SelectDefaultClause? __ "}" {
if (def) {
return optionalList(clauses).concat(def);
} else {
return optionalList(clauses);
}
}

// This should be either
// 1. var x = <-channel:
Expand Down
43 changes: 43 additions & 0 deletions src/tests/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1979,3 +1979,46 @@ func foo() int {
5 / foo();
`, 'Division by 0 error!', '', defaultNumWords);

// Test that select blocks if no cases match
testProgram(`
x := make(chan int); // unbuffered
select {
case <-x:
print("This won't happen");
}
10;
`, 'Stuck forever!', '', defaultNumWords);

testProgram(`
x := make(chan int); // unbuffered
go func() {
select {
case <-x:
print("This won't happen");
}
}();
for i := 0; i < 100; i++ {
}
10;
`, 10, '', defaultNumWords);

testProgram(`
x := make(chan int); // unbuffered
go func() {
select {
case <-x:
print("This won't happen");
default:
print("Print once");
}
}();
for i := 0; i < 100; i++ {
}
10;
`, 10, '"Print once"', defaultNumWords);
14 changes: 12 additions & 2 deletions src/vm/oogavm-compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,8 @@ const compileComp = {
// there is no real "randomness"
// we complete the select case on a single successful case so we need to jump to the end
let jumps: number[] = [];
let start: number = wc;
let hasDefault: boolean = false;
for (let i = 0; i < comp.cases.length; i++) {
const compCase = comp.cases[i];
log('Compiling: ' + unparse(compCase));
Expand Down Expand Up @@ -391,13 +393,21 @@ const compileComp = {
// jump to the next select case if possible
jof.addr = wc;
} else if (compCase.tag === 'SelectDefaultCase') {
// default has no checks and is always the last case as per our parser rules
console.log("Default case");
hasDefault = true;
compile(compCase.body, ce);
// no need to throw GOTO cos it's the last anyways
jumps.push(wc);
instrs[wc++] = { tag: Opcodes.GOTO, addr: 0};
} else {
throw new CompilerError('Unsupported select case in SelectStatement');
}
}
// If none of the cases were handled and there is no default, this is when we reach this compile section
// we will push a BLOCK_THREAD instruction followed by a GOTO to the start of the case
if (!hasDefault) {
instrs[wc++] = { tag: Opcodes.BLOCK_THREAD };
instrs[wc++] = { tag: Opcodes.GOTO, addr: start };
}
for (let jumpInstr of jumps) {
instrs[jumpInstr].addr = wc;
}
Expand Down
3 changes: 3 additions & 0 deletions src/vm/oogavm-machine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1064,6 +1064,9 @@ const microcode = {
}
tempRoots.pop();
},
BLOCK_THREAD: instr => {
blockThread();
},
BREAKPOINT: instr => {
// Print heap visualization
// log('Breakpoint');
Expand Down
1 change: 1 addition & 0 deletions src/vm/opcodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const enum OpCodes {
CHECK_CHANNEL = 'CHECK_CHANNEL',
CHECK_WRITE = 'CHECK_WRITE',
CHECK_READ = 'CHECK_READ',
BLOCK_THREAD = 'BLOCK_THREAD',
BREAKPOINT = 'BREAKPOINT',
APPEND = 'APPEND',
}
Expand Down

0 comments on commit 9ee648a

Please sign in to comment.