s4s-editor / local-scratch-vm /src /blocks /scratch3_operators.js
soiz1's picture
Upload 811 files
30c32c8 verified
raw
history blame
13.4 kB
const Cast = require('../util/cast.js');
const MathUtil = require('../util/math-util.js');
const SandboxRunner = require('../util/sandboxed-javascript-runner.js');
const { validateRegex } = require('../util/json-block-utilities');
class Scratch3OperatorsBlocks {
constructor (runtime) {
/**
* The runtime instantiating this block package.
* @type {Runtime}
*/
this.runtime = runtime;
}
/**
* Retrieve the block primitives implemented by this package.
* @return {object.<string, Function>} Mapping of opcode to Function.
*/
//
getPrimitives () {
return {
operator_add: this.add,
operator_subtract: this.subtract,
operator_multiply: this.multiply,
operator_divide: this.divide,
operator_power: this.power,
operator_lt: this.lt,
operator_equals: this.equals,
operator_notequal: this.notequals,
operator_gt: this.gt,
operator_ltorequal: this.ltorequal,
operator_gtorequal: this.gtorequal,
operator_and: this.and,
operator_nand: this.nand,
operator_nor: this.nor,
operator_xor: this.xor,
operator_xnor: this.xnor,
operator_or: this.or,
operator_not: this.not,
operator_random: this.random,
operator_join: this.join,
operator_join3: this.join3,
operator_letter_of: this.letterOf,
operator_length: this.length,
operator_contains: this.contains,
operator_mod: this.mod,
operator_round: this.round,
operator_mathop: this.mathop,
operator_advlog: this.advlog,
operator_regexmatch: this.regexmatch,
operator_replaceAll: this.replaceAll,
operator_replaceFirst: this.replaceFirst,
operator_getLettersFromIndexToIndexInText: this.getLettersFromIndexToIndexInText,
operator_getLettersFromIndexToIndexInTextFixed: this.getLettersFromIndexToIndexInTextFixed,
operator_readLineInMultilineText: this.readLineInMultilineText,
operator_newLine: this.newLine,
operator_tabCharacter: this.tabCharacter,
operator_stringify: this.stringify,
operator_boolify: this.boolify,
operator_lerpFunc: this.lerpFunc,
operator_advMath: this.advMath,
operator_advMathExpanded: this.advMathExpanded,
operator_constrainnumber: this.constrainnumber,
operator_trueBoolean: this.true,
operator_falseBoolean: this.false,
operator_randomBoolean: this.randomBoolean,
operator_indexOfTextInText: this.indexOfTextInText,
operator_lastIndexOfTextInText: this.lastIndexOfTextInText,
operator_toUpperLowerCase: this.toCase,
operator_character_to_code: this.charToCode,
operator_code_to_character: this.codeToChar,
operator_textStartsOrEndsWith: this.textStartsOrEndsWith,
operator_countAppearTimes: this.countAppearTimes,
operator_textIncludesLetterFrom: this.textIncludesLetterFrom,
operator_javascript_output: this.javascriptOutput,
operator_javascript_boolean: this.javascriptBoolean
};
}
javascriptOutput (args) {
return new Promise((resolve, reject) => {
const js = Cast.toString(args.JS);
SandboxRunner.execute(js).then(result => {
resolve(result.value)
})
})
}
javascriptBoolean(args) {
return new Promise((resolve, reject) => {
const js = Cast.toString(args.JS);
SandboxRunner.execute(js).then(result => {
resolve(result.value === true)
})
})
}
charToCode (args) {
const char = Cast.toString(args.ONE);
if (!char) return NaN;
return char.charCodeAt(0);
}
codeToChar (args) {
const code = Cast.toNumber(args.ONE);
return String.fromCharCode(code);
}
toCase (args) {
const text = Cast.toString(args.TEXT);
switch (args.OPTION) {
case 'upper':
return text.toUpperCase();
case 'lower':
return text.toLowerCase();
}
}
indexOfTextInText (args) {
const lookfor = Cast.toString(args.TEXT1);
const searchin = Cast.toString(args.TEXT2);
let index = 0;
if (searchin.includes(lookfor)) {
index = searchin.indexOf(lookfor) + 1;
}
return index;
}
lastIndexOfTextInText (args) {
const lookfor = Cast.toString(args.TEXT1);
const searchin = Cast.toString(args.TEXT2);
let index = 0;
if (searchin.includes(lookfor)) {
index = searchin.lastIndexOf(lookfor) + 1;
}
return index;
}
textStartsOrEndsWith (args) {
const text = Cast.toString(args.TEXT1);
const startsOrEnds = Cast.toString(args.OPTION);
const withh = Cast.toString(args.TEXT2);
return (startsOrEnds === "starts") ? (text.startsWith(withh)) : (text.endsWith(withh));
}
countAppearTimes (args) {
const text = Cast.toString(args.TEXT2);
const otherText = Cast.toString(args.TEXT1);
const aray = text.split(otherText);
if (aray.length <= 1) {
return 0;
}
return aray.length - 1;
}
textIncludesLetterFrom (args) {
const text = Cast.toString(args.TEXT1);
const from = Cast.toString(args.TEXT2);
let includes = false;
const aray = from.split("");
aray.forEach(i => {
if (text.includes(i)) includes = true;
})
return includes;
}
true () { return true; }
false () { return false; }
randomBoolean () { return Boolean(Math.round(Math.random())); }
constrainnumber (args) {
return Math.min(Math.max(args.min, args.inp), args.max);
}
lerpFunc (args) {
const one = Cast.toNumber(args.ONE);
const two = Cast.toNumber(args.TWO);
const amount = Cast.toNumber(args.AMOUNT);
return ((two - one) * amount) + one;
}
advMath (args) {
const one = isNaN(Cast.toNumber(args.ONE)) ? 0 : Cast.toNumber(args.ONE);
const two = isNaN(Cast.toNumber(args.TWO)) ? 0 : Cast.toNumber(args.TWO);
const operator = Cast.toString(args.OPTION);
switch (operator) {
case "^": return one ** two;
case "root": return one ** 1 / two;
case "log": return Math.log(two) / Math.log(one);
default: return 0;
}
}
advMathExpanded (args) {
const one = Cast.toNumber(args.ONE);
const two = Cast.toNumber(args.TWO);
const three = Cast.toNumber(args.THREE);
const operator = Cast.toString(args.OPTION);
switch (operator) {
case "root": return one * Math.pow(three, 1 / two);
case "log": return one * Math.log(three) / Math.log(two);
default: return 0;
}
}
stringify (args) { return Cast.toString(args.ONE); }
boolify (args) { return Cast.toBoolean(args.ONE); }
newLine () { return "\n"; }
tabCharacter () { return "\t"; }
readLineInMultilineText (args) {
const line = (Cast.toNumber(args.LINE) ? Cast.toNumber(args.LINE) : 1) - 1;
const text = Cast.toString(args.TEXT);
const readline = text.split("\n")[line] || "";
return readline;
}
getLettersFromIndexToIndexInTextFixed (args) {
const index1 = (Cast.toNumber(args.INDEX1) ? Cast.toNumber(args.INDEX1) : 1) - 1;
const index2 = (Cast.toNumber(args.INDEX2) ? Cast.toNumber(args.INDEX2) : 1);
const string = Cast.toString(args.TEXT);
const substring = string.substring(index1, index2);
return substring;
}
getLettersFromIndexToIndexInText (args) {
const index1 = (Cast.toNumber(args.INDEX1) ? Cast.toNumber(args.INDEX1) : 1) - 1;
const index2 = (Cast.toNumber(args.INDEX2) ? Cast.toNumber(args.INDEX2) : 1) - 1;
const string = Cast.toString(args.TEXT);
const substring = string.substring(index1, index2);
return substring;
}
replaceAll (args) {
return Cast.toString(args.text).replaceAll(args.term, args.res);
}
replaceFirst (args) {
return Cast.toString(args.text).replace(args.term, args.res);
}
regexmatch (args) {
if (!validateRegex(args.reg, args.regrule)) return "[]";
const regex = new RegExp(args.reg, args.regrule);
const matches = args.text.match(regex);
return JSON.stringify(matches ? matches : []);
}
add (args) {
return Cast.toNumber(args.NUM1) + Cast.toNumber(args.NUM2);
}
subtract (args) {
return Cast.toNumber(args.NUM1) - Cast.toNumber(args.NUM2);
}
multiply (args) {
return Cast.toNumber(args.NUM1) * Cast.toNumber(args.NUM2);
}
divide (args) {
return Cast.toNumber(args.NUM1) / Cast.toNumber(args.NUM2);
}
power (args) {
return Math.pow(Cast.toNumber(args.NUM1), Cast.toNumber(args.NUM2));
}
lt (args) {
return Cast.compare(args.OPERAND1, args.OPERAND2) < 0;
}
equals (args) {
return Cast.compare(args.OPERAND1, args.OPERAND2) === 0;
}
notequals (args) {
return !this.equals(args);
}
gt (args) {
return Cast.compare(args.OPERAND1, args.OPERAND2) > 0;
}
gtorequal (args) {
return !this.lt(args);
}
ltorequal (args) {
return !this.gt(args);
}
and (args) {
return Cast.toBoolean(args.OPERAND1) && Cast.toBoolean(args.OPERAND2);
}
nand (args) {
return !(Cast.toBoolean(args.OPERAND1) && Cast.toBoolean(args.OPERAND2));
}
nor (args) {
return !(Cast.toBoolean(args.OPERAND1) || Cast.toBoolean(args.OPERAND2));
}
xor (args) {
const op1 = Cast.toBoolean(args.OPERAND1);
const op2 = Cast.toBoolean(args.OPERAND2);
return (op1 ? !op2 : op2);
}
xnor (args) {
return !this.xor(args);
}
or (args) {
return Cast.toBoolean(args.OPERAND1) || Cast.toBoolean(args.OPERAND2);
}
not (args) {
return !Cast.toBoolean(args.OPERAND);
}
random (args) {
return this._random(args.FROM, args.TO);
}
_random (from, to) { // used by compiler
const nFrom = Cast.toNumber(from);
const nTo = Cast.toNumber(to);
const low = nFrom <= nTo ? nFrom : nTo;
const high = nFrom <= nTo ? nTo : nFrom;
if (low === high) return low;
// If both arguments are ints, truncate the result to an int.
if (Cast.isInt(from) && Cast.isInt(to)) {
return low + Math.floor(Math.random() * ((high + 1) - low));
}
return (Math.random() * (high - low)) + low;
}
join (args) {
return Cast.toString(args.STRING1) + Cast.toString(args.STRING2);
}
join3 (args) {
return Cast.toString(args.STRING1) + Cast.toString(args.STRING2) + Cast.toString(args.STRING3);
}
letterOf (args) {
const index = Cast.toNumber(args.LETTER) - 1;
const str = Cast.toString(args.STRING);
// Out of bounds?
if (index < 0 || index >= str.length) {
return '';
}
return str.charAt(index);
}
length (args) {
return Cast.toString(args.STRING).length;
}
contains (args) {
const format = function (string) {
return Cast.toString(string).toLowerCase();
};
return format(args.STRING1).includes(format(args.STRING2));
}
mod (args) {
const n = Cast.toNumber(args.NUM1);
const modulus = Cast.toNumber(args.NUM2);
let result = n % modulus;
// Scratch mod uses floored division instead of truncated division.
if (result / modulus < 0) result += modulus;
return result;
}
round (args) {
return Math.round(Cast.toNumber(args.NUM));
}
mathop (args) {
const operator = Cast.toString(args.OPERATOR).toLowerCase();
const n = Cast.toNumber(args.NUM);
switch (operator) {
case 'abs': return Math.abs(n);
case 'floor': return Math.floor(n);
case 'ceiling': return Math.ceil(n);
case 'sqrt': return Math.sqrt(n);
case 'sin': return Math.round(Math.sin((Math.PI * n) / 180) * 1e10) / 1e10;
case 'cos': return Math.round(Math.cos((Math.PI * n) / 180) * 1e10) / 1e10;
case 'tan': return MathUtil.tan(n);
case 'asin': return (Math.asin(n) * 180) / Math.PI;
case 'acos': return (Math.acos(n) * 180) / Math.PI;
case 'atan': return (Math.atan(n) * 180) / Math.PI;
case 'ln': return Math.log(n);
case 'log': return Math.log(n) / Math.LN10;
case 'log2': return Math.log2(n);
case 'e ^': return Math.exp(n);
case '10 ^': return Math.pow(10, n);
}
return 0;
}
advlog (args) {
return (Math.log(Cast.toNumber(args.NUM2)) / Math.log(Cast.toNumber(args.NUM1)));
}
}
module.exports = Scratch3OperatorsBlocks;