Skip to content

Commit

Permalink
Breakpoints (#14)
Browse files Browse the repository at this point in the history
* Breakpoints

* Add server

* Fix tests by removing circular dependency

* Fix ts errors

* Final changes to breakpoint

* Make tests pass
  • Loading branch information
arnav-ag authored Apr 15, 2024
1 parent e9bfadd commit 25cd85d
Show file tree
Hide file tree
Showing 11 changed files with 439 additions and 206 deletions.
12 changes: 12 additions & 0 deletions src/parser/ooga.pegjs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ Keyword
/ CaseToken
/ DefaultToken
/ SelectToken
/ BreakpointToken
/ AppendToken


Expand Down Expand Up @@ -276,6 +277,7 @@ SwitchToken = "switch" !IdentifierPart
CaseToken = "case" !IdentifierPart
DefaultToken = "default" !IdentifierPart
SelectToken = "select" !IdentifierPart
BreakpointToken = "breakpoint" !IdentifierPart
AppendToken = "append" !IdentifierPart


Expand Down Expand Up @@ -650,6 +652,7 @@ Statement
/ MethodDeclaration
/ CallExpression
/ ChannelWriteExpression
/ BreakpointStatement

BlockStatement
= "{" __ body:(StatementList __)? "}" {
Expand Down Expand Up @@ -1279,3 +1282,12 @@ SelectStatement
cases: cases
};
}

// Breakpoints

BreakpointStatement
= BreakpointToken EOS {
return {
tag: "BreakpointStatement"
};
}
23 changes: 23 additions & 0 deletions src/server/debug.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
let heaps: any[][];
let stacks: any[][];

export const appendHeap = (heap: any[]) => {
heaps.push(heap);
};

export const appendStack = (stack: any[]) => {
stacks.push(stack);
};

export const resetHeapsAndStacks = () => {
heaps = [];
stacks = [];
};

export const getHeaps = () => {
return heaps;
};

export const getStacks = () => {
return stacks;
};
22 changes: 15 additions & 7 deletions src/server/runOogaLang.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { run } from '../vm/oogavm-machine.js';
import debug from 'debug';
import { readFileSync } from 'fs';
import { prepare_and_compile } from '../vm/oogavm-toolchain.js';
import { getHeaps, getStacks, resetHeapsAndStacks } from './debug.js';

const log = debug('ooga:runOogaLang');
const standardSource = readFileSync('std/ooga-std.ooga', 'utf8');
Expand All @@ -12,9 +13,13 @@ const standardSource = readFileSync('std/ooga-std.ooga', 'utf8');
* @param {string} code The ooga-lang code to execute.
* @returns {Promise<string>} The captured output (including any errors).
*/
export function runOogaLangCode(code: string): Promise<string> {
export function runOogaLangCode(
code: string
): Promise<{ capturedOutput: string; heaps: any[]; stacks: any[] }> {
// debug.disable();
// debug.enable('ooga:runOogaLang');
log(code);
debug.disable();
resetHeapsAndStacks();
return new Promise((resolve, reject) => {
// Redirect console.log to capture output
const originalConsoleLog = console.log;
Expand All @@ -32,16 +37,19 @@ export function runOogaLangCode(code: string): Promise<string> {
processByteCode(bytecode);
let value = run();
capturedOutput += 'Output: ' + value + '\n';
// Restore console.log
console.log = originalConsoleLog;

const heaps = getHeaps();
const stacks = getStacks();
// Resolve the promise with the captured output
resolve(capturedOutput);
resolve({ capturedOutput, heaps, stacks });
} catch (error: any) {
// Restore console.log before rejecting
console.log = originalConsoleLog;
log(error);
// Reject the promise with the error message
reject(`Error: ${error.message}\n${capturedOutput}`);
} finally {
// Restore console.log
console.log = originalConsoleLog;
debug.enable('*');
}
});
}
4 changes: 2 additions & 2 deletions src/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ app.post(
async (req: express.Request<{}, {}, RunRequest>, res: express.Response<RunResponse>) => {
try {
const { code } = req.body;
const output = await runOogaLangCode(code); // This will run the code and catch errors internally
res.json({ success: true, output });
const { capturedOutput, heaps, stacks } = await runOogaLangCode(code); // This will run the code and catch errors internally
res.json({ success: true, output: capturedOutput, heaps, stacks });
} catch (error) {
res.json({ success: false, error: error.message });
}
Expand Down
142 changes: 91 additions & 51 deletions src/tests/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1670,49 +1670,68 @@ for {
defaultNumWords
);


// Testing slices
testProgram(`
testProgram(
`
var x []int = make([]int, 5, 10); // create a slice of len 5 and capacity 10
x = append(x, 5);
for i := 0; i < len(x); i++ {
print(x[i]);
}
x[5];
`, 5, '0\n0\n0\n0\n0\n5\n', defaultNumWords);
`,
5,
'0\n0\n0\n0\n0\n5\n',
defaultNumWords
);

// Testing re-allocation
testProgram(`
testProgram(
`
var x []int = make([]int, 5, 5); // create a slice of len 5 and capacity 5
var y []int = append(x, 10); // this should point to a new y
print(y[5]); // shud be 10
print(len(x)); // should be 5
y[0]; // should be 0
`, 0, '10\n5\n', defaultNumWords);
`,
0,
'10\n5\n',
defaultNumWords
);

// Testing default initialization
testProgram(`
testProgram(
`
var x []bool = make([]bool, 5, 5); // create a slice of len 5 and capacity 5
for i := 0; i < len(x); i++ {
print(x[i]);
}
0;
`, 0, 'false\nfalse\nfalse\nfalse\nfalse\n', defaultNumWords);

`,
0,
'false\nfalse\nfalse\nfalse\nfalse\n',
defaultNumWords
);

testProgram(`
testProgram(
`
var x []string = make([]string, 5, 5); // create a slice of len 5 and capacity 5
x = append(x, "Jotham");
for i := 0; i < len(x); i++ {
print(x[i]);
}
0;
`, 0, '""\n""\n""\n""\n""\n"Jotham"\n', defaultNumWords);
`,
0,
'""\n""\n""\n""\n""\n"Jotham"\n',
defaultNumWords
);

testProgram(`
testProgram(
`
type Vector struct {
x int
y int
Expand All @@ -1723,23 +1742,37 @@ for i := 0; i < len(vs); i++ {
print(vs[i]); // null 5 times
}
10;
`, 10, '"nil"\n"nil"\n"nil"\n"nil"\n"nil"\n', defaultNumWords);

`,
10,
'"nil"\n"nil"\n"nil"\n"nil"\n"nil"\n',
defaultNumWords
);

// Test out of bounds error
testProgram(`
testProgram(
`
var x []int = make([]int, 5, 5);
x[6];
`, 'Array out of bounds error!', '', defaultNumWords);
`,
'Array out of bounds error!',
'',
defaultNumWords
);

testProgram(`
testProgram(
`
var x []int = make([]int, 5, 10);
x[6]; // still garbage data
`, 'Array out of bounds error!', '', defaultNumWords);
`,
'Array out of bounds error!',
'',
defaultNumWords
);

// Test the standard library mutex. It forces the thread that fails to lock to yield and pass on control
// to the next thread. It also demonstrates that the WaitGroup works as intended.
testProgram(`
testProgram(
`
var x int = 0;
wg := WaitGroup{10};
Expand All @@ -1762,41 +1795,44 @@ for i := 0; i < 10; i++ {
wg.Wait();
print(x);
`, 50,
`,
50,
'"0 just started"\n' +
'"0 is locking"\n' +
'"1 just started"\n' +
'"0 is incrementing"\n' +
'"1 is locking"\n' +
'"2 just started"\n' +
'"1 is incrementing"\n' +
'"2 is locking"\n' +
'"3 just started"\n' +
'"2 is incrementing"\n' +
'"3 is locking"\n' +
'"4 just started"\n' +
'"3 is incrementing"\n' +
'"4 is locking"\n' +
'"5 just started"\n' +
'"4 is incrementing"\n' +
'"5 is locking"\n' +
'"6 just started"\n' +
'"5 is incrementing"\n' +
'"6 is locking"\n' +
'"7 just started"\n' +
'"6 is incrementing"\n' +
'"7 is locking"\n' +
'"8 just started"\n' +
'"7 is incrementing"\n' +
'"8 is locking"\n' +
'"9 just started"\n' +
'"8 is incrementing"\n' +
'"9 is locking"\n' +
'"9 is incrementing"\n50', defaultNumWords);

'"0 is locking"\n' +
'"1 just started"\n' +
'"0 is incrementing"\n' +
'"1 is locking"\n' +
'"2 just started"\n' +
'"1 is incrementing"\n' +
'"2 is locking"\n' +
'"3 just started"\n' +
'"2 is incrementing"\n' +
'"3 is locking"\n' +
'"4 just started"\n' +
'"3 is incrementing"\n' +
'"4 is locking"\n' +
'"5 just started"\n' +
'"4 is incrementing"\n' +
'"5 is locking"\n' +
'"6 just started"\n' +
'"5 is incrementing"\n' +
'"6 is locking"\n' +
'"7 just started"\n' +
'"6 is incrementing"\n' +
'"7 is locking"\n' +
'"8 just started"\n' +
'"7 is incrementing"\n' +
'"8 is locking"\n' +
'"9 just started"\n' +
'"8 is incrementing"\n' +
'"9 is locking"\n' +
'"9 is incrementing"\n50',
defaultNumWords
);

// test default initialization
testProgram(`
testProgram(
`
type Vector struct {
x int
y int
Expand Down Expand Up @@ -1826,4 +1862,8 @@ print(b.z); // "<struct>"
print(nil); // "nil"
print(a.z == nil); // true
5;
`, 5, '0\nfalse\n""\n0\n0\n"nil"\n1\n2\n"<struct>"\n"nil"\ntrue', defaultNumWords);
`,
5,
'0\nfalse\n""\n0\n0\n"nil"\n1\n2\n"<struct>"\n"nil"\ntrue',
defaultNumWords
);
Loading

0 comments on commit 25cd85d

Please sign in to comment.