mirror of
https://github.com/captbaritone/webamp.git
synced 2026-08-02 15:03:53 +00:00
Start running basic test
This commit is contained in:
parent
28f5d0929b
commit
b1fdd62410
5 changed files with 113 additions and 13 deletions
|
|
@ -1,6 +1,7 @@
|
|||
const parse = require("./parser");
|
||||
const { getClass, getFormattedId } = require("./objects");
|
||||
const interpret = require("./virtualMachine");
|
||||
const { printCommand } = require("./prettyPrinter");
|
||||
|
||||
function main({ runtime, data, system, log }) {
|
||||
const program = parse(data);
|
||||
|
|
@ -32,6 +33,11 @@ function main({ runtime, data, system, log }) {
|
|||
});
|
||||
});
|
||||
|
||||
const { commands, variables } = program;
|
||||
commands.forEach((command, i) => {
|
||||
// printCommand({ i, command, stack: [], variables });
|
||||
});
|
||||
|
||||
// Set the System global
|
||||
// TODO: We could confirm that this variable has the "system" flag set.
|
||||
program.variables[0].setValue(system);
|
||||
|
|
|
|||
|
|
@ -4,10 +4,18 @@ const System = require("./runtime/System");
|
|||
const runtime = require("./runtime");
|
||||
const interpret = require("./interpreter");
|
||||
|
||||
const VERSIONS = {
|
||||
WINAMP_3_ALPHA: "v1.1.0.a9 (Winamp 3 alpha 8r)",
|
||||
WINAMP_3_BETA: "v1.1.1.b3 (Winamp 3.0 build 488d)",
|
||||
WINAMP_3_FULL: "v1.1.1.b3 (Winamp 3.0 full)",
|
||||
WINAMP_5_02: "v1.1.13 (Winamp 5.02)",
|
||||
WINAMP_5_66: "v1.2.0 (Winamp 5.66)",
|
||||
};
|
||||
|
||||
function runFile(relativePath) {
|
||||
const system = new System();
|
||||
const data = fs.readFileSync(path.join(__dirname, relativePath));
|
||||
interpret({ runtime, data, system, log: false });
|
||||
interpret({ runtime, data, system, log: true });
|
||||
}
|
||||
|
||||
let mockMessageBox;
|
||||
|
|
@ -22,11 +30,11 @@ beforeEach(() => {
|
|||
|
||||
describe("can call messageBox with hello World", () => {
|
||||
const versions = [
|
||||
// "v1.1.0.a9 (Winamp 3 alpha 8r)",
|
||||
"v1.1.1.b3 (Winamp 3.0 build 488d)",
|
||||
"v1.1.1.b3 (Winamp 3.0 full)",
|
||||
// "v1.1.13 (Winamp 5.02)",
|
||||
// "v1.2.0 (Winamp 5.66)"
|
||||
// VERSIONS.WINAMP_3_ALPHA,
|
||||
VERSIONS.WINAMP_3_BETA,
|
||||
VERSIONS.WINAMP_3_FULL,
|
||||
// VERSIONS.WINAMP_5_22,
|
||||
// VERSIONS.WINAMP_5.66
|
||||
];
|
||||
versions.forEach(version => {
|
||||
test(`with bytecode compiled by ${version}`, () => {
|
||||
|
|
@ -41,3 +49,35 @@ describe("can call messageBox with hello World", () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("can use basic operators", () => {
|
||||
const versions = [
|
||||
// jberg could not get the script to compile on this version
|
||||
// VERSIONS.WINAMP_3_ALPHA,
|
||||
VERSIONS.WINAMP_3_BETA,
|
||||
//VERSIONS.WINAMP_3_FULL,
|
||||
// VERSIONS.WINAMP_5_22,
|
||||
// VERSIONS.WINAMP_5.66
|
||||
];
|
||||
|
||||
// The basicTest.m file that jberg prepared follows a convention for what a
|
||||
// passing test looks like. This ultility function lets us just write the
|
||||
// message, since the rest is common for all success messages.
|
||||
function successOutputFromMessage(message) {
|
||||
return [message, "Success", 0, ""];
|
||||
}
|
||||
|
||||
versions.forEach(version => {
|
||||
test.only(`with bytecode compiled by ${version}`, () => {
|
||||
try {
|
||||
runFile(`./reference/maki_compiler/${version}/basicTests.maki`);
|
||||
} catch (e) {
|
||||
// Uncomment this next line to find the next bug to work on.
|
||||
console.error(e);
|
||||
}
|
||||
expect(mockMessageBox.mock.calls).toEqual(
|
||||
["2 + 2 = 4"].map(successOutputFromMessage)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -206,17 +206,20 @@ function decodeCode({ makiFile, classes, variables, methods }) {
|
|||
const commands = [];
|
||||
while (makiFile.getPosition() < start + length) {
|
||||
const pos = makiFile.getPosition() - start;
|
||||
commands.push(parseComand({ makiFile, length, pos, localFunctions }));
|
||||
commands.push(
|
||||
parseComand({ start, makiFile, length, pos, localFunctions })
|
||||
);
|
||||
}
|
||||
|
||||
return { commands, localFunctions };
|
||||
}
|
||||
|
||||
// TODO: Refactor this to consume bytes directly off the end of MakiFile
|
||||
function parseComand({ makiFile, length, pos, localFunctions }) {
|
||||
function parseComand({ start, makiFile, length, pos, localFunctions }) {
|
||||
const opcode = makiFile.readUInt8();
|
||||
const command = {
|
||||
offset: pos,
|
||||
start,
|
||||
pos,
|
||||
opcode,
|
||||
// TODO: This should just be a single nullable value I think
|
||||
|
|
@ -241,7 +244,7 @@ function parseComand({ makiFile, length, pos, localFunctions }) {
|
|||
break;
|
||||
}
|
||||
case "line": {
|
||||
arg = argValue + 5;
|
||||
arg = argValue + 5 + pos;
|
||||
break;
|
||||
}
|
||||
case "objFunc": {
|
||||
|
|
@ -345,6 +348,7 @@ function parse(buffer) {
|
|||
localFunctions,
|
||||
version,
|
||||
extraVersion,
|
||||
offsetToCommand,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,15 @@ function printValue(value) {
|
|||
case "INT":
|
||||
type = "INT";
|
||||
break;
|
||||
case "FLOAT":
|
||||
type = "FLOAT";
|
||||
break;
|
||||
case "DOUBLE":
|
||||
type = "DOUBLE";
|
||||
break;
|
||||
case "BOOLEAN":
|
||||
type = "BOOLEAN";
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown variable type ${variable.typeName}`);
|
||||
}
|
||||
|
|
@ -32,7 +41,7 @@ function printValue(value) {
|
|||
|
||||
function printCommand({ i, command, stack, variables }) {
|
||||
console.log(
|
||||
i,
|
||||
`${i} (${command.start} + ${command.offset})`,
|
||||
command.command.name.toUpperCase(),
|
||||
command.opcode,
|
||||
command.arguments.map(offset => {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
const { printCommand } = require("./prettyPrinter");
|
||||
|
||||
function interpret(start, program, { log = false }) {
|
||||
const { commands, methods, variables, classes } = program;
|
||||
const { commands, methods, variables, classes, offsetToCommand } = program;
|
||||
|
||||
// Run all the commands that are safe to run. Increment this number to find
|
||||
// the next bug.
|
||||
const stack = [];
|
||||
let i = start;
|
||||
let i = start - 1;
|
||||
while (i < commands.length) {
|
||||
i++;
|
||||
const command = commands[i];
|
||||
|
||||
switch (command.opcode) {
|
||||
|
|
@ -21,6 +23,32 @@ function interpret(start, program, { log = false }) {
|
|||
stack.pop();
|
||||
break;
|
||||
}
|
||||
// ==
|
||||
case 8: {
|
||||
const a = stack.pop();
|
||||
const b = stack.pop();
|
||||
stack.push(a.getValue() === b.getValue());
|
||||
break;
|
||||
}
|
||||
// jumpIf
|
||||
case 16: {
|
||||
const value = stack.pop();
|
||||
// This seems backwards. Seems like we're doing a "jump if not"
|
||||
if (value) {
|
||||
break;
|
||||
}
|
||||
const offset = command.arguments[0];
|
||||
const nextCommandIndex = offsetToCommand[offset];
|
||||
i = nextCommandIndex;
|
||||
break;
|
||||
}
|
||||
// jump
|
||||
case 18: {
|
||||
const offset = command.arguments[0];
|
||||
const nextCommandIndex = offsetToCommand[offset];
|
||||
i = nextCommandIndex;
|
||||
break;
|
||||
}
|
||||
// call
|
||||
case 24: {
|
||||
const methodOffset = command.arguments[0];
|
||||
|
|
@ -55,6 +83,20 @@ function interpret(start, program, { log = false }) {
|
|||
stack.push(a);
|
||||
break;
|
||||
}
|
||||
// + (add)
|
||||
case 64: {
|
||||
const a = stack.pop();
|
||||
const b = stack.pop();
|
||||
stack.push(a.getValue() + b.getValue());
|
||||
break;
|
||||
}
|
||||
// >>
|
||||
case 88: {
|
||||
const a = stack.pop();
|
||||
const b = stack.pop();
|
||||
stack.push(a >> b);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unhandled opcode ${command.opcode}`);
|
||||
}
|
||||
|
|
@ -63,7 +105,6 @@ function interpret(start, program, { log = false }) {
|
|||
if (log) {
|
||||
printCommand({ i, command, stack, variables });
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue