Lay out more of the compiler groundwork

- add CompiledInput, will allow more complex operations later
- add eval.js to handle runtime data & methods
- move compiler executor to own file
- simplify compiler execute, scripts are now responsible for all thread status on their own
- add type annotations for compilers
- add math_angle
- add math_integer
- add math_positive_number
- add data_variable
This commit is contained in:
Thomas Weber committed 2020-06-11 01:01:35 -05:00
1 parent 92da52e4fc
commit f95b12dc7e
16 files changed
+176 -24

No files matched your search

+13 -2
View File
@@ -1,18 +1,29 @@
const { InputUtil, StatementUtil, CompiledInput } = require('../compiler');
/**
* @returns {Object.<string, (util: StatementUtil) => void>}
*/
module.exports.getStatements = () => {
return {
};
};
/**
* @returns {Object.<string, (util: InputUtil) => CompiledInput>}
*/
module.exports.getInputs = () => {
return {
math_angle: number,
math_number: number,
math_integer: number,
math_positive_number: number,
math_whole_number: number,
};
};
const number = (util) => {
const number = /** @param {InputUtil} util */ (util) => {
const NUM = util.getFieldUnsafe('NUM');
const number = Number(NUM);
return number;
return util.number(number);
};
@@ -1,3 +1,8 @@
const { InputUtil, StatementUtil, CompiledInput } = require('../compiler');
/**
* @returns {Object.<string, (util: StatementUtil) => void>}
*/
module.exports.getStatements = () => {
return {
control_repeat: repeat,
@@ -5,6 +10,9 @@ module.exports.getStatements = () => {
};
};
/**
* @returns {Object.<string, (util: InputUtil) => CompiledInput>}
*/
module.exports.getInputs = () => {
return {
+15 -1
View File
@@ -1,11 +1,25 @@
const { InputUtil, StatementUtil, CompiledInput } = require('../compiler');
/**
* @returns {Object.<string, (util: StatementUtil) => void>}
*/
module.exports.getStatements = () => {
return {
};
};
/**
* @returns {Object.<string, (util: InputUtil) => CompiledInput>}
*/
module.exports.getInputs = () => {
return {
data_variable: getVariable,
};
};
const getVariable = /** @param {InputUtil} util */ (util) => {
// TODO: this is unsafe.
// TODO: lookupOrCreateVariable has overhead, use target.variables directly instead if possible
return util.number(`target.lookupOrCreateVariable("${util.block.fields.VARIABLE.id}", "${util.block.fields.VARIABLE.value}")`);
};
@@ -1,9 +1,17 @@
const { InputUtil, StatementUtil, CompiledInput } = require('../compiler');
/**
* @returns {Object.<string, (util: StatementUtil) => void>}
*/
module.exports.getStatements = () => {
return {
};
};
/**
* @returns {Object.<string, (util: InputUtil) => CompiledInput>}
*/
module.exports.getInputs = () => {
return {
@@ -1,9 +1,17 @@
const { InputUtil, StatementUtil, CompiledInput } = require('../compiler');
/**
* @returns {Object.<string, (util: StatementUtil) => void>}
*/
module.exports.getStatements = () => {
return {
looks_changeeffectby: changeEffect,
};
};
/**
* @returns {Object.<string, (util: InputUtil) => CompiledInput>}
*/
module.exports.getInputs = () => {
return {
@@ -1,9 +1,17 @@
const { InputUtil, StatementUtil, CompiledInput } = require('../compiler');
/**
* @returns {Object.<string, (util: StatementUtil) => void>}
*/
module.exports.getStatements = () => {
return {
};
};
/**
* @returns {Object.<string, (util: InputUtil) => CompiledInput>}
*/
module.exports.getInputs = () => {
return {
@@ -1,9 +1,17 @@
const { InputUtil, StatementUtil, CompiledInput } = require('../compiler');
/**
* @returns {Object.<string, (util: StatementUtil) => void>}
*/
module.exports.getStatements = () => {
return {
};
};
/**
* @returns {Object.<string, (util: InputUtil) => CompiledInput>}
*/
module.exports.getInputs = () => {
return {
@@ -1,9 +1,17 @@
const { InputUtil, StatementUtil, CompiledInput } = require('../compiler');
/**
* @returns {Object.<string, (util: StatementUtil) => void>}
*/
module.exports.getStatements = () => {
return {
};
};
/**
* @returns {Object.<string, (util: InputUtil) => CompiledInput>}
*/
module.exports.getInputs = () => {
return {
@@ -1,9 +1,17 @@
const { InputUtil, StatementUtil, CompiledInput } = require('../compiler');
/**
* @returns {Object.<string, (util: StatementUtil) => void>}
*/
module.exports.getStatements = () => {
return {
};
};
/**
* @returns {Object.<string, (util: InputUtil) => CompiledInput>}
*/
module.exports.getInputs = () => {
return {
@@ -1,9 +1,17 @@
const { InputUtil, StatementUtil, CompiledInput } = require('../compiler');
/**
* @returns {Object.<string, (util: StatementUtil) => void>}
*/
module.exports.getStatements = () => {
return {
};
};
/**
* @returns {Object.<string, (util: InputUtil) => CompiledInput>}
*/
module.exports.getInputs = () => {
return {
+54 -10
View File
@@ -1,5 +1,6 @@
const log = require('../util/log');
const Thread = require('../engine/thread');
const _eval = require('./eval');
const statements = {};
const inputs = {};
@@ -34,6 +35,9 @@ defaultExtensions.forEach((ext) => {
});
class BlockUtil {
/**
* @param {Compiler} compiler
*/
constructor(compiler, block) {
this.compiler = compiler;
this.block = block;
@@ -49,8 +53,8 @@ class BlockUtil {
}
class InputUtil extends BlockUtil {
constructor(compiler, block) {
super(compiler, block);
number(source) {
return new CompiledInput(source);
}
}
@@ -61,7 +65,7 @@ class StatementUtil extends BlockUtil {
}
yieldLoop() {
this.writeLn('yield;');
this.writeLn(`yield;`);
}
writeLn(s) {
@@ -88,28 +92,53 @@ class StatementUtil extends BlockUtil {
}
}
class CompiledInput {
/**
* @param {string} source The input's source code.
*/
constructor(source) {
this.source = source;
}
toString() {
return this.source;
}
}
class Compiler {
/**
* @param {Thread} thread
*/
constructor(thread) {
this.thread = thread;
this.target = thread.target;
this.variables = 0;
}
/**
* @returns {CompiledInput}
*/
compileInput(parentBlock, inputName) {
const input = parentBlock.inputs[inputName];
const inputId = input.block;
const block = this.thread.target.blocks.getBlock(inputId);
const util = new InputUtil(this, block);
let compiler = inputs[block.opcode];
if (!compiler) {
log.error('unknown opcode', block);
throw new Error('unknown opcode: ' + block.opcode);
}
const util = new InputUtil(this, block);
const result = compiler(util);
return result;
}
/**
* @param {string} startingId The ID of the first block in the stack.
* @returns {string}
*/
compileStack(startingId) {
let blockId = startingId;
let source = '';
@@ -120,36 +149,51 @@ class Compiler {
throw new Error('no block');
}
const util = new StatementUtil(this, block);
let compiler = statements[block.opcode];
if (!compiler) {
log.error('unknown opcode', block);
throw new Error('unknown opcode: ' + block.opcode);
}
const util = new StatementUtil(this, block);
compiler(util);
source += util.source;
blockId = block.next;
}
return source;
}
compile() {
const target = this.thread.target;
const target = this.target;
if (!target) throw new Error('no target');
const topBlockId = this.thread.topBlock;
const topBlock = this.thread.target.blocks.getBlock(topBlockId);
const topBlock = this.target.blocks.getBlock(topBlockId);
if (!topBlock) throw new Error('not a hat');
const script = this.compileStack(topBlock.next);
if (script.length === 0) {
throw new Error('generated script was empty');
}
log.info('compiled script', script);
// TODO: move this to execute.js
var fn = eval('(function* compiled_script() { ' + script + ' })');
return fn;
try {
const fn = _eval(this, `(function* compiled_script() {\n${script}\nthread.status = 4;\n})`);
if (typeof fn !== 'function') {
throw new Error('fn is not a function');
}
return fn;
} catch (e) {
log.error('error evaling', e, script);
throw e;
}
}
}
Compiler.InputUtil = InputUtil;
Compiler.StatementUtil = StatementUtil;
Compiler.CompiledInput = CompiledInput;
module.exports = Compiler;
+16
View File
@@ -0,0 +1,16 @@
// eval.js is responsible for eval()ing the generated JS and providing the proper runtime methods and data.
// `eval` is reserved in strict mode
const _eval = (compiler, _source) => {
// init data
const thread = compiler.thread;
const target = compiler.target;
// no reason to access compiler
compiler = null;
// eval will grab references to all variables in this context
return eval(_source);
};
module.exports = _eval;
+12
View File
@@ -0,0 +1,12 @@
const Thread = require('../engine/thread');
const Sequencer = require('../engine/thread');
/**
* @param {Sequencer} sequencer
* @param {Thread} thread
*/
const execute = function (sequencer, thread) {
thread.generator.next();
};
module.exports = execute;
-9
View File
@@ -1,9 +0,0 @@
const execute = function (sequencer, thread) {
const result = thread.generator.next();
if (result.done) {
sequencer.retireThread(thread);
}
};
module.exports = execute;
+1 -1
View File
@@ -4,7 +4,7 @@ const log = require('../util/log');
const Thread = require('./thread');
const {Map} = require('immutable');
const cast = require('../util/cast');
const compilerExecute = require('../compiler/executor');
const compilerExecute = require('../compiler/execute');
/**
* Single BlockUtility instance reused by execute for every pritimive ran.
+1 -1
View File
@@ -1,7 +1,7 @@
const Timer = require('../util/timer');
const Thread = require('./thread');
const execute = require('./execute.js');
const compilerExecute = require('../compiler/executor');
const compilerExecute = require('../compiler/execute');
/**
* Profiler frame name for stepping a single thread.