mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2025-01-24 16:40:11 -05:00
AST create / change / move / delete from blockly
This commit is contained in:
parent
53779220b7
commit
981b85e78f
22 changed files with 11858 additions and 381 deletions
38
README.md
38
README.md
|
@ -32,6 +32,44 @@ make build
|
||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Abstract Syntax Tree
|
||||||
|
|
||||||
|
#### Overview
|
||||||
|
|
||||||
|
#### Anatomy of a Block
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "^1r~63Gdl7;Dh?I*OP3_",
|
||||||
|
"opcode": "wedo_motorclockwise",
|
||||||
|
"next": null,
|
||||||
|
"fields": {
|
||||||
|
"DURATION": {
|
||||||
|
"name": "DURATION",
|
||||||
|
"value": null,
|
||||||
|
"blocks": {
|
||||||
|
"1?P=eV(OiDY3vMk!24Ip": {
|
||||||
|
"id": "1?P=eV(OiDY3vMk!24Ip",
|
||||||
|
"opcode": "math_number",
|
||||||
|
"next": null,
|
||||||
|
"fields": {
|
||||||
|
"NUM": {
|
||||||
|
"name": "NUM",
|
||||||
|
"value": "10",
|
||||||
|
"blocks": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SUBSTACK": {
|
||||||
|
"name": "SUBSTACK",
|
||||||
|
"value": "@1ln(HsUO4!]*2*%BrE|",
|
||||||
|
"blocks": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
```bash
|
```bash
|
||||||
make test
|
make test
|
||||||
|
|
|
@ -13,10 +13,14 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "make test"
|
"test": "make test"
|
||||||
},
|
},
|
||||||
"dependencies": {},
|
"dependencies": {
|
||||||
|
"htmlparser2": "3.9.0",
|
||||||
|
"memoizee": "0.3.10"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"benchmark": "2.1.0",
|
"benchmark": "2.1.0",
|
||||||
"eslint": "2.7.0",
|
"eslint": "2.7.0",
|
||||||
|
"json-loader": "0.5.4",
|
||||||
"tap": "5.7.1",
|
"tap": "5.7.1",
|
||||||
"webpack": "1.13.0"
|
"webpack": "1.13.0"
|
||||||
}
|
}
|
||||||
|
|
87
src/engine/adapter.js
Normal file
87
src/engine/adapter.js
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
var html = require('htmlparser2');
|
||||||
|
var memoize = require('memoizee');
|
||||||
|
var parseDOM = memoize(html.parseDOM, {
|
||||||
|
length: 1,
|
||||||
|
resolvers: [String],
|
||||||
|
max: 200
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter between block creation events and block representation which can be
|
||||||
|
* used by the Scratch runtime.
|
||||||
|
*
|
||||||
|
* @param {Object} `Blockly.events.create`
|
||||||
|
*
|
||||||
|
* @return {Object}
|
||||||
|
*/
|
||||||
|
module.exports = function (e) {
|
||||||
|
// Validate input
|
||||||
|
if (typeof e !== 'object') return;
|
||||||
|
if (typeof e.blockId !== 'string') return;
|
||||||
|
if (typeof e.xml !== 'object') return;
|
||||||
|
|
||||||
|
// Storage object
|
||||||
|
var obj = {
|
||||||
|
id: e.blockId,
|
||||||
|
opcode: null,
|
||||||
|
next: null,
|
||||||
|
fields: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set opcode
|
||||||
|
if (typeof e.xml.attributes === 'object') {
|
||||||
|
obj.opcode = e.xml.attributes.type.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract fields from event's `innerHTML`
|
||||||
|
if (typeof e.xml.innerHTML !== 'string') return obj;
|
||||||
|
if (e.xml.innerHTML === '') return obj;
|
||||||
|
obj.fields = extract(parseDOM(e.xml.innerHTML));
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts fields from a block's innerHTML.
|
||||||
|
* @todo Extend this to support vertical grammar / nested blocks.
|
||||||
|
*
|
||||||
|
* @param {Object} DOM representation of block's innerHTML
|
||||||
|
*
|
||||||
|
* @return {Object}
|
||||||
|
*/
|
||||||
|
function extract (dom) {
|
||||||
|
// Storage object
|
||||||
|
var fields = {};
|
||||||
|
|
||||||
|
// Field
|
||||||
|
var field = dom[0];
|
||||||
|
var fieldName = field.attribs.name;
|
||||||
|
fields[fieldName] = {
|
||||||
|
name: fieldName,
|
||||||
|
value: null,
|
||||||
|
blocks: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Shadow block
|
||||||
|
var shadow = field.children[0];
|
||||||
|
var shadowId = shadow.attribs.id;
|
||||||
|
var shadowOpcode = shadow.attribs.type;
|
||||||
|
fields[fieldName].blocks[shadowId] = {
|
||||||
|
id: shadowId,
|
||||||
|
opcode: shadowOpcode,
|
||||||
|
next: null,
|
||||||
|
fields: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Primitive
|
||||||
|
var primitive = shadow.children[0];
|
||||||
|
var primitiveName = primitive.attribs.name;
|
||||||
|
var primitiveValue = primitive.children[0].data;
|
||||||
|
fields[fieldName].blocks[shadowId].fields[primitiveName] = {
|
||||||
|
name: primitiveName,
|
||||||
|
value: primitiveValue,
|
||||||
|
blocks: null
|
||||||
|
};
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
|
@ -1,41 +1,5 @@
|
||||||
function Primitives () {
|
function Primitives () {
|
||||||
|
// @todo
|
||||||
}
|
}
|
||||||
|
|
||||||
Primitives.prototype.event_whenflagclicked = function (thread, runtime) {
|
|
||||||
// No-op: flags are started by the interpreter but don't do any action
|
|
||||||
// Take 1/3 second to show running state
|
|
||||||
if (Date.now() - thread.blockFirstTime < 300) {
|
|
||||||
thread.yield = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Primitives.prototype.control_repeat = function (thread, runtime) {
|
|
||||||
// Take 1/3 second to show running state
|
|
||||||
if (Date.now() - thread.blockFirstTime < 300) {
|
|
||||||
thread.yield = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (thread.repeatCounter == -1) {
|
|
||||||
thread.repeatCounter = 10; // @todo from the arg
|
|
||||||
}
|
|
||||||
if (thread.repeatCounter > 0) {
|
|
||||||
thread.repeatCounter -= 1;
|
|
||||||
runtime.interpreter.startSubstack(thread);
|
|
||||||
} else {
|
|
||||||
thread.repeatCounter = -1;
|
|
||||||
thread.nextBlock = runtime.getNextBlock(thread.blockPointer);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Primitives.prototype.control_forever = function (thread, runtime) {
|
|
||||||
// Take 1/3 second to show running state
|
|
||||||
if (Date.now() - thread.blockFirstTime < 300) {
|
|
||||||
thread.yield = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
runtime.interpreter.startSubstack(thread);
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = Primitives;
|
module.exports = Primitives;
|
||||||
|
|
|
@ -1,26 +1,19 @@
|
||||||
var EventEmitter = require('events');
|
var EventEmitter = require('events');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
|
||||||
var Primitives = require('./primatives');
|
|
||||||
var Sequencer = require('./sequencer');
|
|
||||||
var Thread = require('./thread');
|
|
||||||
|
|
||||||
var STEP_THREADS_INTERVAL = 1000 / 30;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple runtime for blocks.
|
* A simple runtime for blocks.
|
||||||
*/
|
*/
|
||||||
function Runtime () {
|
function Runtime () {
|
||||||
// Bind event emitter
|
// Bind event emitter
|
||||||
EventEmitter.call(instance);
|
EventEmitter.call(this);
|
||||||
|
|
||||||
// Instantiate sequencer and primitives
|
|
||||||
this.sequencer = new Sequencer(this);
|
|
||||||
this.primitives = new Primitives();
|
|
||||||
|
|
||||||
// State
|
// State
|
||||||
this.blocks = {};
|
this.blocks = {};
|
||||||
this.stacks = [];
|
this.stacks = [];
|
||||||
|
|
||||||
|
window._BLOCKS = this.blocks;
|
||||||
|
window._STACKS = this.stacks;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,19 +21,34 @@ function Runtime () {
|
||||||
*/
|
*/
|
||||||
util.inherits(Runtime, EventEmitter);
|
util.inherits(Runtime, EventEmitter);
|
||||||
|
|
||||||
Runtime.prototype.createBlock = function (e) {
|
Runtime.prototype.createBlock = function (block) {
|
||||||
// Create new block
|
// Create new block
|
||||||
this.blocks[e.id] = {
|
this.blocks[block.id] = block;
|
||||||
id: e.id,
|
|
||||||
opcode: e.opcode,
|
// Walk each field and add any shadow blocks
|
||||||
next: null,
|
// @todo Expand this to cover vertical / nested blocks
|
||||||
inputs: {}
|
for (var i in block.fields) {
|
||||||
|
var shadows = block.fields[i].blocks;
|
||||||
|
for (var y in shadows) {
|
||||||
|
var shadow = shadows[y];
|
||||||
|
this.blocks[shadow.id] = shadow;
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Push block id to stacks array. New blocks are always a stack even if only
|
// Push block id to stacks array. New blocks are always a stack even if only
|
||||||
// momentary. If the new block is added to an existing stack this stack will
|
// momentary. If the new block is added to an existing stack this stack will
|
||||||
// be removed by the `moveBlock` method below.
|
// be removed by the `moveBlock` method below.
|
||||||
this.stacks.push(e.id);
|
this.stacks.push(block.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
Runtime.prototype.changeBlock = function (args) {
|
||||||
|
// Validate
|
||||||
|
if (args.element !== 'field') return;
|
||||||
|
if (typeof this.blocks[args.id] === 'undefined') return;
|
||||||
|
if (typeof this.blocks[args.id].fields[args.name] === 'undefined') return;
|
||||||
|
|
||||||
|
// Update block value
|
||||||
|
this.blocks[args.id].fields[args.name].value = args.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
Runtime.prototype.moveBlock = function (e) {
|
Runtime.prototype.moveBlock = function (e) {
|
||||||
|
@ -52,10 +60,14 @@ Runtime.prototype.moveBlock = function (e) {
|
||||||
_this._deleteStack(e.id);
|
_this._deleteStack(e.id);
|
||||||
|
|
||||||
// Update new parent
|
// Update new parent
|
||||||
if (e.newInput === undefined) {
|
if (e.newField === undefined) {
|
||||||
_this.blocks[e.newParent].next = e.id;
|
_this.blocks[e.newParent].next = e.id;
|
||||||
} else {
|
} else {
|
||||||
_this.blocks[e.newParent].inputs[e.newInput] = e.id;
|
_this.blocks[e.newParent].fields[e.newField] = {
|
||||||
|
name: e.newField,
|
||||||
|
value: e.id,
|
||||||
|
blocks: {}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,30 +77,34 @@ Runtime.prototype.moveBlock = function (e) {
|
||||||
_this.stacks.push(e.id);
|
_this.stacks.push(e.id);
|
||||||
|
|
||||||
// Update old parent
|
// Update old parent
|
||||||
if (e.oldInput === undefined) {
|
if (e.oldField === undefined) {
|
||||||
_this.blocks[e.oldParent].next = null;
|
_this.blocks[e.oldParent].next = null;
|
||||||
} else {
|
} else {
|
||||||
delete _this.blocks[e.oldParent].inputs[e.oldInput];
|
delete _this.blocks[e.oldParent].fields[e.oldField];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Runtime.prototype.changeBlock = function (e) {
|
|
||||||
// @todo
|
|
||||||
};
|
|
||||||
|
|
||||||
Runtime.prototype.deleteBlock = function (e) {
|
Runtime.prototype.deleteBlock = function (e) {
|
||||||
// @todo Stop threads running on this stack
|
// @todo Stop threads running on this stack
|
||||||
|
|
||||||
// Delete children
|
// Get block
|
||||||
var block = this.blocks[e.id];
|
var block = this.blocks[e.id];
|
||||||
|
|
||||||
|
// Delete children
|
||||||
if (block.next !== null) {
|
if (block.next !== null) {
|
||||||
this.deleteBlock({id: block.next});
|
this.deleteBlock({id: block.next});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete inputs
|
// Delete substacks and fields
|
||||||
for (var i in block.inputs) {
|
for (var field in block.fields) {
|
||||||
this.deleteBlock({id: block.inputs[i]});
|
if (field === 'SUBSTACK') {
|
||||||
|
this.deleteBlock({id: block.fields[field].value});
|
||||||
|
} else {
|
||||||
|
for (var shadow in block.fields[field].blocks) {
|
||||||
|
this.deleteBlock({id: shadow});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete stack
|
// Delete stack
|
||||||
|
@ -98,19 +114,6 @@ Runtime.prototype.deleteBlock = function (e) {
|
||||||
delete this.blocks[e.id];
|
delete this.blocks[e.id];
|
||||||
};
|
};
|
||||||
|
|
||||||
Runtime.prototype.runAllStacks = function () {
|
|
||||||
// @todo
|
|
||||||
};
|
|
||||||
|
|
||||||
Runtime.prototype.runStack = function (e) {
|
|
||||||
// @todo
|
|
||||||
console.dir(e);
|
|
||||||
};
|
|
||||||
|
|
||||||
Runtime.prototype.stopAllStacks = function () {
|
|
||||||
// @todo
|
|
||||||
};
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -126,7 +129,7 @@ Runtime.prototype._getNextBlock = function (id) {
|
||||||
|
|
||||||
Runtime.prototype._getSubstack = function (id) {
|
Runtime.prototype._getSubstack = function (id) {
|
||||||
if (typeof this.blocks[id] === 'undefined') return null;
|
if (typeof this.blocks[id] === 'undefined') return null;
|
||||||
return this.blocks[id].inputs['SUBSTACK'];
|
return this.blocks[id].fields['SUBSTACK'];
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = Runtime;
|
module.exports = Runtime;
|
||||||
|
|
|
@ -1,29 +1,5 @@
|
||||||
var Timer = require('../util/timer');
|
function Sequencer () {
|
||||||
|
// @todo
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*/
|
|
||||||
function Sequencer (runtime) {
|
|
||||||
// Bi-directional binding for runtime
|
|
||||||
this.runtime = runtime;
|
|
||||||
|
|
||||||
// State
|
|
||||||
this.runningThreads = [];
|
|
||||||
this.workTime = 30;
|
|
||||||
this.timer = new Timer();
|
|
||||||
this.currentTime = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Sequencer.prototype.stepAllThreads = function () {
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
Sequencer.prototype.stepThread = function (thread) {
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
Sequencer.prototype.startSubstack = function (thread) {
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = Sequencer;
|
module.exports = Sequencer;
|
||||||
|
|
|
@ -1,19 +1,5 @@
|
||||||
/**
|
function Thread () {
|
||||||
* Thread is an internal data structure used by the interpreter. It holds the
|
// @todo
|
||||||
* state of a thread so it can continue from where it left off, and it has
|
|
||||||
* a stack to support nested control structures and procedure calls.
|
|
||||||
*
|
|
||||||
* @param {String} Unique block identifier
|
|
||||||
*/
|
|
||||||
function Thread (id) {
|
|
||||||
this.topBlockId = id;
|
|
||||||
this.blockPointer = id;
|
|
||||||
this.blockFirstTime = -1;
|
|
||||||
this.nextBlock = null;
|
|
||||||
this.waiting = null;
|
|
||||||
this.runningDeviceBlock = false;
|
|
||||||
this.stack = [];
|
|
||||||
this.repeatCounter = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Thread;
|
module.exports = Thread;
|
||||||
|
|
47
src/index.js
47
src/index.js
|
@ -2,6 +2,7 @@ var EventEmitter = require('events');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
|
||||||
var Runtime = require('./engine/runtime');
|
var Runtime = require('./engine/runtime');
|
||||||
|
var adapter = require('./engine/adapter');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles connections between blocks, stage, and extensions.
|
* Handles connections between blocks, stage, and extensions.
|
||||||
|
@ -30,23 +31,23 @@ function VirtualMachine () {
|
||||||
// Blocks
|
// Blocks
|
||||||
switch (e.type) {
|
switch (e.type) {
|
||||||
case 'create':
|
case 'create':
|
||||||
instance.runtime.createBlock({
|
instance.runtime.createBlock(adapter(e));
|
||||||
|
break;
|
||||||
|
case 'change':
|
||||||
|
instance.runtime.changeBlock({
|
||||||
id: e.blockId,
|
id: e.blockId,
|
||||||
opcode: e.xml.attributes.type.value
|
element: e.element,
|
||||||
|
name: e.name,
|
||||||
|
value: e.newValue
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'move':
|
case 'move':
|
||||||
instance.runtime.moveBlock({
|
instance.runtime.moveBlock({
|
||||||
id: e.blockId,
|
id: e.blockId,
|
||||||
oldParent: e.oldParentId,
|
oldParent: e.oldParentId,
|
||||||
oldInput: e.oldInputName,
|
oldField: e.oldInputName,
|
||||||
newParent: e.newParentId,
|
newParent: e.newParentId,
|
||||||
newInput: e.newInputName
|
newField: e.newInputName
|
||||||
});
|
|
||||||
break;
|
|
||||||
case 'change':
|
|
||||||
instance.runtime.changeBlock({
|
|
||||||
id: e.blockId
|
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'delete':
|
case 'delete':
|
||||||
|
@ -55,19 +56,7 @@ function VirtualMachine () {
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// UI
|
|
||||||
if (typeof e.element === 'undefined') return;
|
|
||||||
switch (e.element) {
|
|
||||||
case 'click':
|
|
||||||
instance.runtime.runStack({
|
|
||||||
id: e.blockId
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// @todo Forward runtime events
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -75,22 +64,6 @@ function VirtualMachine () {
|
||||||
*/
|
*/
|
||||||
util.inherits(VirtualMachine, EventEmitter);
|
util.inherits(VirtualMachine, EventEmitter);
|
||||||
|
|
||||||
VirtualMachine.prototype.start = function () {
|
|
||||||
// @todo Run all green flags
|
|
||||||
};
|
|
||||||
|
|
||||||
VirtualMachine.prototype.stop = function () {
|
|
||||||
// @todo Stop all threads
|
|
||||||
};
|
|
||||||
|
|
||||||
VirtualMachine.prototype.save = function () {
|
|
||||||
// @todo Serialize runtime state
|
|
||||||
};
|
|
||||||
|
|
||||||
VirtualMachine.prototype.load = function () {
|
|
||||||
// @todo Deserialize and apply runtime state
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export and bind to `window`
|
* Export and bind to `window`
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
* @todo Swap out Date.now() with microtime module that works in node & browsers
|
|
||||||
*/
|
*/
|
||||||
function Timer () {
|
function Timer () {
|
||||||
this.startTime = 0;
|
this.startTime = 0;
|
||||||
|
|
0
test/benchmark/ast.js
Normal file
0
test/benchmark/ast.js
Normal file
26
test/fixtures/blocks.js
vendored
26
test/fixtures/blocks.js
vendored
|
@ -1,26 +0,0 @@
|
||||||
var events = require('events');
|
|
||||||
var util = require('util');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simulates event emitter / listener patterns from Scratch Blocks.
|
|
||||||
*
|
|
||||||
* @author Andrew Sliwinski <ascii@media.mit.edu>
|
|
||||||
*/
|
|
||||||
function Blocks () {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inherit from EventEmitter to enable messaging.
|
|
||||||
*/
|
|
||||||
util.inherits(VirtualMachine, events.EventEmitter);
|
|
||||||
|
|
||||||
Blocks.prototype.spaghetti = function () {
|
|
||||||
this.emit('');
|
|
||||||
};
|
|
||||||
|
|
||||||
Blocks.prototype.spam = function () {
|
|
||||||
this.emit('');
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = Blocks;
|
|
20
test/fixtures/events.json
vendored
Normal file
20
test/fixtures/events.json
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"create": {
|
||||||
|
"blockId": "z!+#Nqr,_(V=xz0y7a@d",
|
||||||
|
"workspaceId": "7Luws3lyb*Z98~Kk+IG|",
|
||||||
|
"group": ";OswyM#@%`%,xOrhOXC=",
|
||||||
|
"recordUndo": true,
|
||||||
|
"xml": {
|
||||||
|
"attributes": {
|
||||||
|
"type": {
|
||||||
|
"value": "wedo_motorclockwise"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"innerHTML": "<value name=\"DURATION\"><shadow type=\"math_number\" id=\"!6Ahqg4f}Ljl}X5Hws?Z\"><field name=\"NUM\">10</field></shadow></value>"
|
||||||
|
},
|
||||||
|
"ids": [
|
||||||
|
"z!+#Nqr,_(V=xz0y7a@d",
|
||||||
|
"!6Ahqg4f}Ljl}X5Hws?Z"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
22
test/integration/index.js
Normal file
22
test/integration/index.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
var test = require('tap').test;
|
||||||
|
var VirtualMachine = require('../../src/index');
|
||||||
|
|
||||||
|
test('spec', function (t) {
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('create', function (t) {
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('move', function (t) {
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('change', function (t) {
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('delete', function (t) {
|
||||||
|
t.end();
|
||||||
|
});
|
20
test/unit/adapter.js
Normal file
20
test/unit/adapter.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
var test = require('tap').test;
|
||||||
|
var adapter = require('../../src/engine/adapter');
|
||||||
|
var events = require('../fixtures/events.json');
|
||||||
|
|
||||||
|
test('spec', function (t) {
|
||||||
|
t.type(adapter, 'function');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('create event', function (t) {
|
||||||
|
var result = adapter(events.create);
|
||||||
|
|
||||||
|
t.type(result, 'object');
|
||||||
|
t.type(result.id, 'string');
|
||||||
|
t.type(result.opcode, 'string');
|
||||||
|
t.type(result.fields, 'object');
|
||||||
|
t.type(result.fields['DURATION'], 'object');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
8
test/unit/primatives.js
Normal file
8
test/unit/primatives.js
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
var test = require('tap').test;
|
||||||
|
var Primatives = require('../../src/engine/primatives');
|
||||||
|
|
||||||
|
test('spec', function (t) {
|
||||||
|
t.type(Primatives, 'function');
|
||||||
|
// @todo
|
||||||
|
t.end();
|
||||||
|
});
|
|
@ -24,7 +24,9 @@ test('create', function (t) {
|
||||||
var r = new Runtime();
|
var r = new Runtime();
|
||||||
r.createBlock({
|
r.createBlock({
|
||||||
id: 'foo',
|
id: 'foo',
|
||||||
opcode: 'TEST_BLOCK'
|
opcode: 'TEST_BLOCK',
|
||||||
|
next: null,
|
||||||
|
fields: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
t.type(r.blocks['foo'], 'object');
|
t.type(r.blocks['foo'], 'object');
|
||||||
|
@ -37,11 +39,15 @@ test('move', function (t) {
|
||||||
var r = new Runtime();
|
var r = new Runtime();
|
||||||
r.createBlock({
|
r.createBlock({
|
||||||
id: 'foo',
|
id: 'foo',
|
||||||
opcode: 'TEST_BLOCK'
|
opcode: 'TEST_BLOCK',
|
||||||
|
next: null,
|
||||||
|
fields: {}
|
||||||
});
|
});
|
||||||
r.createBlock({
|
r.createBlock({
|
||||||
id: 'bar',
|
id: 'bar',
|
||||||
opcode: 'TEST_BLOCK'
|
opcode: 'TEST_BLOCK',
|
||||||
|
next: null,
|
||||||
|
fields: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Attach 'bar' to the end of 'foo'
|
// Attach 'bar' to the end of 'foo'
|
||||||
|
@ -69,7 +75,9 @@ test('delete', function (t) {
|
||||||
var r = new Runtime();
|
var r = new Runtime();
|
||||||
r.createBlock({
|
r.createBlock({
|
||||||
id: 'foo',
|
id: 'foo',
|
||||||
opcode: 'TEST_BLOCK'
|
opcode: 'TEST_BLOCK',
|
||||||
|
next: null,
|
||||||
|
fields: {}
|
||||||
});
|
});
|
||||||
r.deleteBlock({
|
r.deleteBlock({
|
||||||
id: 'foo'
|
id: 'foo'
|
||||||
|
|
8
test/unit/sequencer.js
Normal file
8
test/unit/sequencer.js
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
var test = require('tap').test;
|
||||||
|
var Sequencer = require('../../src/engine/sequencer');
|
||||||
|
|
||||||
|
test('spec', function (t) {
|
||||||
|
t.type(Sequencer, 'function');
|
||||||
|
// @todo
|
||||||
|
t.end();
|
||||||
|
});
|
|
@ -2,17 +2,10 @@ var test = require('tap').test;
|
||||||
var VirtualMachine = require('../../src/index');
|
var VirtualMachine = require('../../src/index');
|
||||||
|
|
||||||
test('spec', function (t) {
|
test('spec', function (t) {
|
||||||
var vm = new VirtualMachine('foo');
|
var vm = new VirtualMachine();
|
||||||
|
|
||||||
t.type(VirtualMachine, 'function');
|
t.type(VirtualMachine, 'function');
|
||||||
t.type(vm, 'object');
|
t.type(vm, 'object');
|
||||||
|
|
||||||
t.type(vm.blockListener, 'function');
|
t.type(vm.blockListener, 'function');
|
||||||
// t.type(vm.uiListener, 'function');
|
|
||||||
// t.type(vm.start, 'function');
|
|
||||||
// t.type(vm.stop, 'function');
|
|
||||||
// t.type(vm.save, 'function');
|
|
||||||
// t.type(vm.load, 'function');
|
|
||||||
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,9 +2,7 @@ var test = require('tap').test;
|
||||||
var Thread = require('../../src/engine/thread');
|
var Thread = require('../../src/engine/thread');
|
||||||
|
|
||||||
test('spec', function (t) {
|
test('spec', function (t) {
|
||||||
var thread = new Thread('foo');
|
|
||||||
|
|
||||||
t.type(Thread, 'function');
|
t.type(Thread, 'function');
|
||||||
t.type(thread, 'object');
|
// @todo
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
12
vm.min.js
vendored
12
vm.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -9,10 +9,21 @@ module.exports = {
|
||||||
path: __dirname,
|
path: __dirname,
|
||||||
filename: '[name].js'
|
filename: '[name].js'
|
||||||
},
|
},
|
||||||
|
module: {
|
||||||
|
loaders: [
|
||||||
|
{
|
||||||
|
test: /\.json$/,
|
||||||
|
loader: 'json-loader'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new webpack.optimize.UglifyJsPlugin({
|
new webpack.optimize.UglifyJsPlugin({
|
||||||
include: /\.min\.js$/,
|
include: /\.min\.js$/,
|
||||||
minimize: true
|
minimize: true,
|
||||||
|
compress: {
|
||||||
|
warnings: false
|
||||||
|
}
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue