blockbench/lib/molang.umd.js
2020-10-07 19:38:31 +02:00

394 lines
10 KiB
JavaScript

(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Molang = factory());
}(this, (function () { 'use strict';
var MathUtil = {
random(a, b) {
return a + Math.random() * (b-a)
},
clamp(number, min, max) {
if (number > max) number = max;
if (number < min || isNaN(number)) number = min;
return number;
}
};
/**
* Author: JannisX11
* License: MIT
*/
// Util
function trimInput(string) {
string = string.toLowerCase().trim();
if (string.includes(';')) {
string = string.replace(/;\s+/g, ';').replace(/;\s*$/, '');
}
return string;
}
function Molang() {
const self = this;
this.global_variables = {};
this.cache_enabled = true;
this.use_radians = false;
let cached = {};
let current_variables = {};
// Tree Types
function Expression(string) {
this.lines = string.split(';').map(line => {
return iterateString(line);
});
}
function Comp(operator, a, b, c) {
this.operator = operator;
this.a = iterateString(a);
if (b !== undefined) this.b = iterateString(b);
if (c !== undefined) this.c = iterateString(c);
}
function Allocation(name, value) {
this.value = iterateString(value);
this.name = name;
}
function Statement(type, value) {
this.value = iterateString(value);
this.type = type;
}
let angleFactor = () => this.use_radians ? 1 : (Math.PI/180);
function calculate(expression, variables) {
current_variables = variables||{};
var i = 0;
for (var line of expression.lines) {
let result = iterateExp(line);
i++;
if (i == expression.lines.length || (line instanceof Statement && line.type === 'return')) {
return result;
}
}
return 0;
}
function iterateString(s) {
//Iterates through string, returns float, string or comp;
if (!s) return 0;
if (!isNaN(s)) return parseFloat(s);
s = s.replace(/\s/g, '');
while (canTrimParentheses(s)) {
s = s.substr(1, s.length-2);
}
//Statement
var match = s.length > 5 && s.match(/^return/);
if (match) {
return new Statement(match[0], s.substr(match[0].length))
}
//allocation
var match = s.length > 6 && s.match(/(temp|variable)\.\w+=/);
if (match) {
let name = match[0].replace(/=$/, '');
let value = s.substr(match.index + match[0].length);
return new Allocation(name, value)
}
//ternary
var split = splitString(s, '?');
if (split) {
let ab = splitString(split[1], ':');
if (ab && ab.length) {
return new Comp(10, split[0], ab[0], ab[1]);
}
}
//2 part operators
var comp = (
testOp(s, '&&', 11) ||
testOp(s, '||', 12) ||
testOp(s, '<', 13) ||
testOp(s, '<=', 14) ||
testOp(s, '>', 15) ||
testOp(s, '>=', 16) ||
testOp(s, '==', 17) ||
testOp(s, '!=', 18) ||
testOp(s, '+', 1, true) ||
testMinus(s, '-', 2, true) ||
testOp(s, '*', 3) ||
testOp(s, '/', 4)
);
if (comp) return comp;
if (s.substr(0, 5) === 'math.') {
if (s.substr(0, 7) === 'math.pi') {
return Math.PI
}
let begin = s.search(/\(/);
let operator = s.substr(5, begin-5);
let inner = s.substr(begin+1, s.length-begin-2);
let params = splitString(inner, ',')||[inner];
if (params.length > 1) {
var last2 = splitString(params[1], ',');
if (last2 && last2.length > 1) {
params[1] = last2[0];
params[2] = last2[1];
}
}
switch (operator) {
case 'abs':
return new Comp(20, params[0]);
case 'sin':
return new Comp(21, params[0]);
case 'cos':
return new Comp(22, params[0]);
case 'exp':
return new Comp(23, params[0]);
case 'ln':
return new Comp(24, params[0]);
case 'pow':
return new Comp(25, params[0], params[1]);
case 'sqrt':
return new Comp(26, params[0]);
case 'random':
return new Comp(27, params[0], params[1]);
case 'ceil':
return new Comp(28, params[0]);
case 'round':
return new Comp(29, params[0]);
case 'trunc':
return new Comp(30, params[0]);
case 'floor':
return new Comp(31, params[0]);
case 'mod':
return new Comp(32, params[0], params[1]);
case 'min':
return new Comp(33, params[0], params[1]);
case 'max':
return new Comp(34, params[0], params[1]);
case 'clamp':
return new Comp(35, params[0], params[1], params[2]);
case 'lerp':
return new Comp(36, params[0], params[1], params[2]);
case 'lerprotate':
return new Comp(37, params[0], params[1], params[2]);
}
}
split = s.match(/[a-zA-Z0-9._]{2,}/g);
if (split && split.length === 1) {
return s;
}
return 0;
}
function canTrimParentheses(s) {
if (s.substr(0, 1) === '(' && s.substr(-1) === ')') {
let level = 0;
for (var i = 0; i < s.length-1; i++) {
switch (s[i]) {
case '(': level++; break;
case ')': level--; break;
}
if (level == 0) return false;
}
return true;
}
}
function testOp(s, char, operator, inverse) {
var split = splitString(s, char, inverse);
if (split) {
return new Comp(operator, split[0], split[1])
}
}
function testMinus(s, char, operator, inverse) {
var split = splitString(s, char, inverse);
if (split) {
if (split[0].length === 0) {
return new Comp(operator, 0, split[1])
} else if ('+*/<>=|&?:'.includes(split[0].substr(-1)) === false) {
return new Comp(operator, split[0], split[1])
}
}
}
function splitString(s, char, inverse) {
var direction = inverse ? -1 : 1;
var i = inverse ? s.length-1 : 0;
var level = 0;
var is_string = typeof char === 'string';
while (inverse ? i >= 0 : i < s.length) {
if (s[i] === '(') {
level += direction;
} else if (s[i] === ')') {
level -= direction;
} else if (level === 0) {
var letters = s.substr(i, char.length);
if (is_string && letters === char) {
return [
s.substr(0, i),
s.substr(i+char.length)
];
} else if (!is_string) {
for (var xi = 0; xi < char.length; xi++) {
if (char[xi] === letters) {
return [
s.substr(0, i),
s.substr(i+char[xi].length)
];
}
}
}
}
i += direction;
}
}
function iterateExp(T) {
if (typeof T === 'number') {
return T
} else if (typeof T === 'string') {
var val = current_variables[T];
if (val === undefined) {
if (T === 'true') {
return 1;
} else if (T === 'false') {
return 0;
} else {
val = self.global_variables[T];
}
}
if (val === undefined && typeof self.variableHandler === 'function') {
val = self.variableHandler(T, current_variables);
}
if (typeof val === 'string') {
val = self.parse(val, current_variables);
}
return val||0;
} else if (T instanceof Statement) {
return iterateExp(T.value);
} else if (T instanceof Allocation) {
return current_variables[T.name] = iterateExp(T.value);
} else if (T instanceof Comp) {
switch (T.operator) {
//Basic
case 1:
return iterateExp(T.a) + iterateExp(T.b);
case 2:
return iterateExp(T.a) - iterateExp(T.b);
case 3:
return iterateExp(T.a) * iterateExp(T.b);
case 4:
return iterateExp(T.a) / iterateExp(T.b);
//Logical
case 10:
return iterateExp(T.a) ? iterateExp(T.b) : iterateExp(T.c);
case 11:
return iterateExp(T.a) && iterateExp(T.b) ? 1 : 0;
case 12:
return iterateExp(T.a) || iterateExp(T.b) ? 1 : 0;
case 13:
return iterateExp(T.a) < iterateExp(T.b) ? 1 : 0;
case 14:
return iterateExp(T.a) <= iterateExp(T.b) ? 1 : 0;
case 15:
return iterateExp(T.a) > iterateExp(T.b) ? 1 : 0;
case 16:
return iterateExp(T.a) >= iterateExp(T.b) ? 1 : 0;
case 17:
return iterateExp(T.a) === iterateExp(T.b) ? 1 : 0;
case 18:
return iterateExp(T.a) !== iterateExp(T.b) ? 1 : 0;
//Math
case 20:
return Math.abs(iterateExp(T.a));
case 21:
return Math.sin(iterateExp(T.a) * angleFactor());
case 22:
return Math.cos(iterateExp(T.a) * angleFactor());
case 23:
return Math.exp(iterateExp(T.a));
case 24:
return Math.log(iterateExp(T.a));
case 25:
return Math.pow(iterateExp(T.a), iterateExp(T.b));
case 26:
return Math.sqrt(iterateExp(T.a));
case 27:
return MathUtil.random(iterateExp(T.a), iterateExp(T.b), iterateExp(T.c));
case 28:
return Math.ceil(iterateExp(T.a));
case 29:
return Math.round(iterateExp(T.a));
case 30:
return Math.trunc(iterateExp(T.a));
case 31:
return Math.floor(iterateExp(T.a));
case 32:
return iterateExp(T.a) % iterateExp(T.b);
case 33:
return Math.min(iterateExp(T.a), iterateExp(T.b));
case 34:
return Math.max(iterateExp(T.a), iterateExp(T.b));
case 35:
return MathUtil.clamp(iterateExp(T.a), iterateExp(T.b), iterateExp(T.c));
case 36:
let n1 = iterateExp(T.a);
return n1 + (iterateExp(T.b) - n1) * iterateExp(T.c);
case 37:
let radify = n => (((n + 180) % 360) +180) % 360;
let a = radify(iterateExp(T.a));
let b = radify(iterateExp(T.b));
let i = iterateExp(T.c);
if (a > b) [a, b] = [b, a];
var diff = b-a;
if (diff > 180) {
return radify(b + i * (360-diff));
} else {
return a + i * diff;
}
}
}
return 0;
}
this.parse = (input, variables) => {
if (typeof input === 'number') {
return isNaN(input) ? 0 : input
}
if (typeof input !== 'string') return 0;
input = trimInput(input);
if (this.cache_enabled && cached[input]) {
var expression = cached[input];
} else {
var expression = new Expression(input);
if (this.cache_enabled) {
cached[input] = expression;
}
}
var value = calculate(expression, variables);
return value;
};
}
return Molang;
})));