mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2024-12-24 06:52:40 -05:00
First cut at turning lists into typed variables
This commit is contained in:
parent
a735459427
commit
70959cc7f5
9 changed files with 55 additions and 80 deletions
|
@ -52,8 +52,8 @@ class Scratch3DataBlocks {
|
||||||
// If it is, report contents joined together with no separator.
|
// If it is, report contents joined together with no separator.
|
||||||
// If it's not, report contents joined together with a space.
|
// If it's not, report contents joined together with a space.
|
||||||
let allSingleLetters = true;
|
let allSingleLetters = true;
|
||||||
for (let i = 0; i < list.contents.length; i++) {
|
for (let i = 0; i < list.value.length; i++) {
|
||||||
const listItem = list.contents[i];
|
const listItem = list.value[i];
|
||||||
if (!((typeof listItem === 'string') &&
|
if (!((typeof listItem === 'string') &&
|
||||||
(listItem.length === 1))) {
|
(listItem.length === 1))) {
|
||||||
allSingleLetters = false;
|
allSingleLetters = false;
|
||||||
|
@ -61,73 +61,73 @@ class Scratch3DataBlocks {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (allSingleLetters) {
|
if (allSingleLetters) {
|
||||||
return list.contents.join('');
|
return list.value.join('');
|
||||||
}
|
}
|
||||||
return list.contents.join(' ');
|
return list.value.join(' ');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addToList (args, util) {
|
addToList (args, util) {
|
||||||
const list = util.target.lookupOrCreateList(args.LIST);
|
const list = util.target.lookupOrCreateList(args.LIST);
|
||||||
list.contents.push(args.ITEM);
|
list.value.push(args.ITEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteOfList (args, util) {
|
deleteOfList (args, util) {
|
||||||
const list = util.target.lookupOrCreateList(args.LIST);
|
const list = util.target.lookupOrCreateList(args.LIST);
|
||||||
const index = Cast.toListIndex(args.INDEX, list.contents.length);
|
const index = Cast.toListIndex(args.INDEX, list.value.length);
|
||||||
if (index === Cast.LIST_INVALID) {
|
if (index === Cast.LIST_INVALID) {
|
||||||
return;
|
return;
|
||||||
} else if (index === Cast.LIST_ALL) {
|
} else if (index === Cast.LIST_ALL) {
|
||||||
list.contents = [];
|
list.value = [];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
list.contents.splice(index - 1, 1);
|
list.value.splice(index - 1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
insertAtList (args, util) {
|
insertAtList (args, util) {
|
||||||
const item = args.ITEM;
|
const item = args.ITEM;
|
||||||
const list = util.target.lookupOrCreateList(args.LIST);
|
const list = util.target.lookupOrCreateList(args.LIST);
|
||||||
const index = Cast.toListIndex(args.INDEX, list.contents.length + 1);
|
const index = Cast.toListIndex(args.INDEX, list.value.length + 1);
|
||||||
if (index === Cast.LIST_INVALID) {
|
if (index === Cast.LIST_INVALID) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
list.contents.splice(index - 1, 0, item);
|
list.value.splice(index - 1, 0, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceItemOfList (args, util) {
|
replaceItemOfList (args, util) {
|
||||||
const item = args.ITEM;
|
const item = args.ITEM;
|
||||||
const list = util.target.lookupOrCreateList(args.LIST);
|
const list = util.target.lookupOrCreateList(args.LIST);
|
||||||
const index = Cast.toListIndex(args.INDEX, list.contents.length);
|
const index = Cast.toListIndex(args.INDEX, list.value.length);
|
||||||
if (index === Cast.LIST_INVALID) {
|
if (index === Cast.LIST_INVALID) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
list.contents.splice(index - 1, 1, item);
|
list.value.splice(index - 1, 1, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
getItemOfList (args, util) {
|
getItemOfList (args, util) {
|
||||||
const list = util.target.lookupOrCreateList(args.LIST);
|
const list = util.target.lookupOrCreateList(args.LIST);
|
||||||
const index = Cast.toListIndex(args.INDEX, list.contents.length);
|
const index = Cast.toListIndex(args.INDEX, list.value.length);
|
||||||
if (index === Cast.LIST_INVALID) {
|
if (index === Cast.LIST_INVALID) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
return list.contents[index - 1];
|
return list.value[index - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
lengthOfList (args, util) {
|
lengthOfList (args, util) {
|
||||||
const list = util.target.lookupOrCreateList(args.LIST);
|
const list = util.target.lookupOrCreateList(args.LIST);
|
||||||
return list.contents.length;
|
return list.value.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
listContainsItem (args, util) {
|
listContainsItem (args, util) {
|
||||||
const item = args.ITEM;
|
const item = args.ITEM;
|
||||||
const list = util.target.lookupOrCreateList(args.LIST);
|
const list = util.target.lookupOrCreateList(args.LIST);
|
||||||
if (list.contents.indexOf(item) >= 0) {
|
if (list.value.indexOf(item) >= 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Try using Scratch comparison operator on each item.
|
// Try using Scratch comparison operator on each item.
|
||||||
// (Scratch considers the string '123' equal to the number 123).
|
// (Scratch considers the string '123' equal to the number 123).
|
||||||
for (let i = 0; i < list.contents.length; i++) {
|
for (let i = 0; i < list.value.length; i++) {
|
||||||
if (Cast.compare(list.contents[i], item) === 0) {
|
if (Cast.compare(list.value[i], item) === 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -260,7 +260,7 @@ class Blocks {
|
||||||
// If not, create it on the stage.
|
// If not, create it on the stage.
|
||||||
// TODO create global and local variables when UI provides a way.
|
// TODO create global and local variables when UI provides a way.
|
||||||
if (!optRuntime.getEditingTarget().lookupVariableById(e.varId)) {
|
if (!optRuntime.getEditingTarget().lookupVariableById(e.varId)) {
|
||||||
stage.createVariable(e.varId, e.varName);
|
stage.createVariable(e.varId, e.varName, e.varType);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'var_rename':
|
case 'var_rename':
|
||||||
|
@ -310,7 +310,7 @@ class Blocks {
|
||||||
case 'field':
|
case 'field':
|
||||||
// Update block value
|
// Update block value
|
||||||
if (!block.fields[args.name]) return;
|
if (!block.fields[args.name]) return;
|
||||||
if (args.name === 'VARIABLE') {
|
if (args.name === 'VARIABLE' || args.name === 'LIST') {
|
||||||
// Get variable name using the id in args.value.
|
// Get variable name using the id in args.value.
|
||||||
const variable = optRuntime.getEditingTarget().lookupVariableById(args.value);
|
const variable = optRuntime.getEditingTarget().lookupVariableById(args.value);
|
||||||
if (variable) {
|
if (variable) {
|
||||||
|
|
|
@ -138,7 +138,7 @@ const execute = function (sequencer, thread) {
|
||||||
// Add all fields on this block to the argValues.
|
// Add all fields on this block to the argValues.
|
||||||
for (const fieldName in fields) {
|
for (const fieldName in fields) {
|
||||||
if (!fields.hasOwnProperty(fieldName)) continue;
|
if (!fields.hasOwnProperty(fieldName)) continue;
|
||||||
if (fieldName === 'VARIABLE') {
|
if (fieldName === 'VARIABLE' || fieldName === 'LIST') {
|
||||||
argValues[fieldName] = fields[fieldName].id;
|
argValues[fieldName] = fields[fieldName].id;
|
||||||
} else {
|
} else {
|
||||||
argValues[fieldName] = fields[fieldName].value;
|
argValues[fieldName] = fields[fieldName].value;
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
/**
|
|
||||||
* @fileoverview
|
|
||||||
* Object representing a Scratch list.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {!string} name Name of the list.
|
|
||||||
* @param {Array} contents Contents of the list, as an array.
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
class List {
|
|
||||||
constructor (name, contents) {
|
|
||||||
this.name = name;
|
|
||||||
this.contents = contents;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = List;
|
|
|
@ -2,7 +2,6 @@ const EventEmitter = require('events');
|
||||||
|
|
||||||
const Blocks = require('./blocks');
|
const Blocks = require('./blocks');
|
||||||
const Variable = require('../engine/variable');
|
const Variable = require('../engine/variable');
|
||||||
const List = require('../engine/list');
|
|
||||||
const uid = require('../util/uid');
|
const uid = require('../util/uid');
|
||||||
const {Map} = require('immutable');
|
const {Map} = require('immutable');
|
||||||
|
|
||||||
|
@ -88,7 +87,7 @@ class Target extends EventEmitter {
|
||||||
const variable = this.lookupVariableById(id);
|
const variable = this.lookupVariableById(id);
|
||||||
if (variable) return variable;
|
if (variable) return variable;
|
||||||
// No variable with this name exists - create it locally.
|
// No variable with this name exists - create it locally.
|
||||||
const newVariable = new Variable(id, name, 0, false);
|
const newVariable = new Variable(id, name, "", false);
|
||||||
this.variables[id] = newVariable;
|
this.variables[id] = newVariable;
|
||||||
return newVariable;
|
return newVariable;
|
||||||
}
|
}
|
||||||
|
@ -117,24 +116,16 @@ class Target extends EventEmitter {
|
||||||
/**
|
/**
|
||||||
* Look up a list object for this target, and create it if one doesn't exist.
|
* Look up a list object for this target, and create it if one doesn't exist.
|
||||||
* Search begins for local lists; then look for globals.
|
* Search begins for local lists; then look for globals.
|
||||||
|
* @param {!string} id Id of the list.
|
||||||
* @param {!string} name Name of the list.
|
* @param {!string} name Name of the list.
|
||||||
* @return {!List} List object.
|
* @return {!List} List object.
|
||||||
*/
|
*/
|
||||||
lookupOrCreateList (name) {
|
lookupOrCreateList (id, name) {
|
||||||
// If we have a local copy, return it.
|
const list = this.lookupVariableById(id);
|
||||||
if (this.lists.hasOwnProperty(name)) {
|
if (list) return list;
|
||||||
return this.lists[name];
|
// No variable with this name exists - create it locally.
|
||||||
}
|
const newList = new Variable(id, name, "list", false);
|
||||||
// If the stage has a global copy, return it.
|
this.variables[id] = newList;
|
||||||
if (this.runtime && !this.isStage) {
|
|
||||||
const stage = this.runtime.getTargetForStage();
|
|
||||||
if (stage.lists.hasOwnProperty(name)) {
|
|
||||||
return stage.lists[name];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// No list with this name exists - create it locally.
|
|
||||||
const newList = new List(name, []);
|
|
||||||
this.lists[name] = newList;
|
|
||||||
return newList;
|
return newList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,11 +134,11 @@ class Target extends EventEmitter {
|
||||||
* dictionary of variables.
|
* dictionary of variables.
|
||||||
* @param {string} id Id of variable
|
* @param {string} id Id of variable
|
||||||
* @param {string} name Name of variable.
|
* @param {string} name Name of variable.
|
||||||
|
* @param {string} type Type of variable, one of string, number, list
|
||||||
*/
|
*/
|
||||||
createVariable (id, name) {
|
createVariable (id, name, type) {
|
||||||
if (!this.variables.hasOwnProperty(id)) {
|
if (!this.variables.hasOwnProperty(id)) {
|
||||||
const newVariable = new Variable(id, name, 0,
|
const newVariable = new Variable(id, name, type, false);
|
||||||
false);
|
|
||||||
this.variables[id] = newVariable;
|
this.variables[id] = newVariable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,19 +9,27 @@ class Variable {
|
||||||
/**
|
/**
|
||||||
* @param {string} id Id of the variable.
|
* @param {string} id Id of the variable.
|
||||||
* @param {string} name Name of the variable.
|
* @param {string} name Name of the variable.
|
||||||
* @param {(string|number)} value Value of the variable.
|
* @param {(string|number)} type Type of the variable, one of "" or "list"
|
||||||
* @param {boolean} isCloud Whether the variable is stored in the cloud.
|
* @param {boolean} isCloud Whether the variable is stored in the cloud.
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
constructor (id, name, value, isCloud) {
|
constructor (id, name, type, isCloud) {
|
||||||
this.id = id || uid();
|
this.id = id || uid();
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.value = value;
|
this.type = type;
|
||||||
this.isCloud = isCloud;
|
this.isCloud = isCloud;
|
||||||
|
switch (this.type) {
|
||||||
|
case "":
|
||||||
|
this.value = 0;
|
||||||
|
break;
|
||||||
|
case "list":
|
||||||
|
this.value = [];
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toXML () {
|
toXML () {
|
||||||
return `<variable type="" id="${this.id}">${this.name}</variable>`;
|
return `<variable type="${this.type}" id="${this.id}">${this.name}</variable>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ const log = require('../util/log');
|
||||||
const uid = require('../util/uid');
|
const uid = require('../util/uid');
|
||||||
const specMap = require('./sb2_specmap');
|
const specMap = require('./sb2_specmap');
|
||||||
const Variable = require('../engine/variable');
|
const Variable = require('../engine/variable');
|
||||||
const List = require('../engine/list');
|
|
||||||
|
|
||||||
const {loadCostume} = require('../import/load-costume.js');
|
const {loadCostume} = require('../import/load-costume.js');
|
||||||
const {loadSound} = require('../import/load-sound.js');
|
const {loadSound} = require('../import/load-sound.js');
|
||||||
|
@ -235,9 +234,10 @@ const parseScratchObject = function (object, runtime, extensions, topLevel) {
|
||||||
const newVariable = new Variable(
|
const newVariable = new Variable(
|
||||||
getVariableId(variable.name),
|
getVariableId(variable.name),
|
||||||
variable.name,
|
variable.name,
|
||||||
variable.value,
|
"",
|
||||||
variable.isPersistent
|
variable.isPersistent
|
||||||
);
|
);
|
||||||
|
newVariable.value = variable.value;
|
||||||
target.variables[newVariable.id] = newVariable;
|
target.variables[newVariable.id] = newVariable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -251,10 +251,14 @@ const parseScratchObject = function (object, runtime, extensions, topLevel) {
|
||||||
for (let k = 0; k < object.lists.length; k++) {
|
for (let k = 0; k < object.lists.length; k++) {
|
||||||
const list = object.lists[k];
|
const list = object.lists[k];
|
||||||
// @todo: monitor properties.
|
// @todo: monitor properties.
|
||||||
target.lists[list.listName] = new List(
|
const newVariable = new Variable(
|
||||||
|
getVariableId(list.listName),
|
||||||
list.listName,
|
list.listName,
|
||||||
list.contents
|
"list",
|
||||||
|
list.isPersistent
|
||||||
);
|
);
|
||||||
|
newVariable.value = list.contents;
|
||||||
|
target.variables[newVariable.id] = newVariable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (object.hasOwnProperty('scratchX')) {
|
if (object.hasOwnProperty('scratchX')) {
|
||||||
|
|
|
@ -1252,7 +1252,7 @@ const specMap = {
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
'contentsOfList:': {
|
'contentsOfList:': {
|
||||||
opcode: 'data_list',
|
opcode: 'data_listcontents',
|
||||||
argMap: [
|
argMap: [
|
||||||
{
|
{
|
||||||
type: 'field',
|
type: 'field',
|
||||||
|
|
|
@ -8,7 +8,6 @@ const vmPackage = require('../../package.json');
|
||||||
const Blocks = require('../engine/blocks');
|
const Blocks = require('../engine/blocks');
|
||||||
const Sprite = require('../sprites/sprite');
|
const Sprite = require('../sprites/sprite');
|
||||||
const Variable = require('../engine/variable');
|
const Variable = require('../engine/variable');
|
||||||
const List = require('../engine/list');
|
|
||||||
|
|
||||||
const {loadCostume} = require('../import/load-costume.js');
|
const {loadCostume} = require('../import/load-costume.js');
|
||||||
const {loadSound} = require('../import/load-sound.js');
|
const {loadSound} = require('../import/load-sound.js');
|
||||||
|
@ -125,22 +124,13 @@ const parseScratchObject = function (object, runtime, extensions) {
|
||||||
const newVariable = new Variable(
|
const newVariable = new Variable(
|
||||||
variable.id,
|
variable.id,
|
||||||
variable.name,
|
variable.name,
|
||||||
variable.value,
|
variable.type,
|
||||||
variable.isPersistent
|
variable.isPersistent
|
||||||
);
|
);
|
||||||
|
newVariable.value = variable.value;
|
||||||
target.variables[newVariable.id] = newVariable;
|
target.variables[newVariable.id] = newVariable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (object.hasOwnProperty('lists')) {
|
|
||||||
for (let k = 0; k < object.lists.length; k++) {
|
|
||||||
const list = object.lists[k];
|
|
||||||
// @todo: monitor properties.
|
|
||||||
target.lists[list.listName] = new List(
|
|
||||||
list.listName,
|
|
||||||
list.contents
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (object.hasOwnProperty('x')) {
|
if (object.hasOwnProperty('x')) {
|
||||||
target.x = object.x;
|
target.x = object.x;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue