Improve variable parsing

This commit is contained in:
Jordan Eldredge 2019-06-23 22:15:21 -07:00
parent 5eacdc53b8
commit a4c8705bf9
4 changed files with 192 additions and 25 deletions

View file

@ -2,57 +2,99 @@ const fs = require("fs");
const path = require("path");
const { parse } = require("./");
const log = true;
function parseFile(relativePath) {
const buffer = fs.readFileSync(path.join(__dirname, relativePath));
return parse(buffer);
}
class Group {}
const System = {
getScriptGroup() {
return new Group();
}
};
function interpret({ commands: rawCommands, variables, types }) {
const commands = rawCommands.slice(0, 5);
const environment = {
System
};
// Debug utility to pretty print a value/variable
function prettyPrint(offset) {
const value = variables[offset] || offset;
if (value == null) {
return "NULL";
}
let name = value;
if (value.type) {
name = `Variable(${value.type.name}) ${
value.getValue() ? "" : "(empty)"
}`;
}
return name;
}
const commands = rawCommands.slice(0, 86);
const stack = [];
let i = 0;
while (i < commands.length) {
const command = commands[i];
switch (command.opcode) {
// push
case 1: {
// What are these? Do they have names?
const offsetIntoVariables = command.arguments[0];
const variable = variables[offsetIntoVariables];
console.log(variable);
stack.push(variable);
break;
}
// pop
case 2: {
stack.pop();
break;
}
// call
case 24: {
const funcDefinition = command.arguments[0];
const { name } = funcDefinition;
const { name, parameters } = funcDefinition;
const methodArgs = [];
// This might be the wrong order
parameters.forEach(() => {
methodArgs.push(stack.pop());
});
const variable = stack.pop();
const type = types[variable.type];
const obj = environment[type.name];
const obj = variable.getValue();
stack.push(obj[name]());
break;
}
// return
case 33: {
const posVar = stack.pop();
const pos = posVar.getValue();
// TODO: What is this supposed to do?
i += pos;
break;
}
// mov
case 48: {
const a = stack.pop();
const b = stack.pop();
b.setValue(a);
stack.push(a);
break;
}
default:
throw new Error(`Unhandled opcode ${command.opcode}`);
}
i++;
// Print some debug info
if (log) {
console.log(
i + 1,
command.command.name.toUpperCase(),
command.opcode,
command.arguments.map(offset => {
return prettyPrint(offset);
})
);
stack.forEach((value, i) => {
const name = prettyPrint(value);
console.log(" ", i + 1, name);
});
}
}
}

View file

@ -5,6 +5,14 @@ const Variable = require("./variable");
const MAGIC = "FG";
const ENCODING = "binary";
const PRIMITIVE_TYPES = {
5: "BOOLEAN",
2: "INT",
3: "FLOAT",
4: "DOUBLE",
6: "STRING"
};
class Parser {
_readMagic() {
const magic = this._readStringOfLength(MAGIC.length);
@ -97,25 +105,50 @@ class Parser {
}
variables.push(new Variable({ ...props, type }));
} else {
const type = types[typeOffset];
if (type == null) {
const typeName = PRIMITIVE_TYPES[typeOffset];
if (typeName == null) {
throw new Error("Invalid type");
}
console.log(type.name);
variables.push(new Variable({ ...props, type }));
let value = null;
switch (typeName) {
// BOOLEAN
case PRIMITIVE_TYPES[5]:
// INT
case PRIMITIVE_TYPES[2]:
value = uinit1;
break;
case PRIMITIVE_TYPES[3]:
case PRIMITIVE_TYPES[4]:
const exponent = (uint2 & 0xff80) >> 7;
const mantisse = ((0x80 | (uint2 & 0x7f)) << 16) | uint1;
value = mantisse * 2.0 ** (exponent - 0x96);
break;
case PRIMITIVE_TYPES[6]:
// This will likely get set by constants later on.
break;
}
if (value == null) {
// throw new Error("Failed to set value");
}
const variable = new Variable({ ...props, type: { name: typeName } });
variable.setValue(value);
variables.push(variable);
}
}
return variables;
}
_readConstants() {
_readConstants({ variables }) {
let count = this._readUInt32LE();
const constants = [];
while (count--) {
// This is an index into the variables array.
const varNum = this._readUInt32LE();
const i = this._readUInt32LE();
const variable = variables[i];
const value = this._readString();
constants.push({ varNum, value });
// TODO: Don't mutate
variable.setValue(value);
constants.push({ varNum: i, value });
}
return constants;
}
@ -230,7 +263,7 @@ class Parser {
const types = this._readTypes();
const functionNames = this._readFunctionsNames({ types });
const variables = this._readVariables({ types });
const constants = this._readConstants();
const constants = this._readConstants({ variables });
const functions = this._readFunctions();
const commands = this._decodeCode({
types,

View file

@ -76,6 +76,67 @@ describe("standardframe.maki", () => {
test("can read variables", () => {
expect(maki.variables.length).toBe(56);
expect(maki.variables.map(variable => variable.type.name))
.toMatchInlineSnapshot(`
Array [
"System",
"INT",
"Group",
"Group",
"Group",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"Layer",
"Button",
"STRING",
"STRING",
"INT",
"INT",
"INT",
"INT",
"INT",
"INT",
"INT",
"INT",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"INT",
"INT",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
"STRING",
]
`);
maki.variables.forEach(variable => {
expect(variable.type).not.toBe(undefined);
});

View file

@ -1,7 +1,38 @@
class GuiObject {}
class Group extends GuiObject {}
class PopupMenu {}
class Container {}
const System = {
getScriptGroup() {
return new Group();
},
getToken(str, separator, tokennum) {
return "Some Token String";
},
getParam() {
return "Some String";
}
};
const environment = {
System,
Group,
GuiObject,
PopupMenu,
Container
};
class Variable {
constructor(props) {
this._props = props;
this.type = props.type;
if (props.system) {
if (this.type.name != "System") {
throw new Error("Can variables other than System be system variables?");
}
this._value = System;
}
}
getValue() {