Compare commits
No commits in common. "803416d08de0ee559aa3ab7a286c45c700cddf1e" and "4b692894f2aebcfc59cb7439f160cf8ca970acff" have entirely different histories.
803416d08d
...
4b692894f2
6 changed files with 67 additions and 136 deletions
|
@ -42,6 +42,7 @@ Column {
|
||||||
*/
|
*/
|
||||||
function openEditorDialog(obj) {
|
function openEditorDialog(obj) {
|
||||||
// Open editor
|
// Open editor
|
||||||
|
console.log(obj, obj.prototype)
|
||||||
objectEditor.obj = obj
|
objectEditor.obj = obj
|
||||||
objectEditor.objType = obj.type
|
objectEditor.objType = obj.type
|
||||||
objectEditor.objIndex = Objects.currentObjects[obj.type].indexOf(obj)
|
objectEditor.objIndex = Objects.currentObjects[obj.type].indexOf(obj)
|
||||||
|
|
|
@ -433,7 +433,7 @@ Item {
|
||||||
itemSelected: parent.itemSelected
|
itemSelected: parent.itemSelected
|
||||||
categoryItems: Parsing.CONSTANTS_LIST
|
categoryItems: Parsing.CONSTANTS_LIST
|
||||||
autocompleteGenerator: (item) => {return {
|
autocompleteGenerator: (item) => {return {
|
||||||
'text': item, 'annotation': Parsing.CONSTANTS[item],
|
'text': item, 'annotation': '',
|
||||||
'autocomplete': item + " ", 'cursorFinalOffset': 0
|
'autocomplete': item + " ", 'cursorFinalOffset': 0
|
||||||
}}
|
}}
|
||||||
baseText: parent.visible ? parent.currentToken.value : ""
|
baseText: parent.visible ? parent.currentToken.value : ""
|
||||||
|
|
|
@ -27,7 +27,7 @@ var ADDITIONAL_VARCHARS = [
|
||||||
"ₜ","¹","²","³","⁴","⁵","⁶",
|
"ₜ","¹","²","³","⁴","⁵","⁶",
|
||||||
"⁷","⁸","⁹","⁰","₁","₂","₃",
|
"⁷","⁸","⁹","⁰","₁","₂","₃",
|
||||||
"₄","₅","₆","₇","₈","₉","₀",
|
"₄","₅","₆","₇","₈","₉","₀",
|
||||||
"∞","π"
|
"∞"
|
||||||
]
|
]
|
||||||
|
|
||||||
function Instruction(type, value) {
|
function Instruction(type, value) {
|
||||||
|
@ -1117,7 +1117,7 @@ ParserState.prototype.parseExpression = function (instr) {
|
||||||
if (this.parseUntilEndStatement(instr, exprInstr)) {
|
if (this.parseUntilEndStatement(instr, exprInstr)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.parseConditionalExpression(exprInstr);
|
this.parseVariableAssignmentExpression(exprInstr);
|
||||||
if (this.parseUntilEndStatement(instr, exprInstr)) {
|
if (this.parseUntilEndStatement(instr, exprInstr)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1157,6 +1157,37 @@ ParserState.prototype.parseArrayList = function (instr) {
|
||||||
return argCount;
|
return argCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ParserState.prototype.parseVariableAssignmentExpression = function (instr) {
|
||||||
|
this.parseConditionalExpression(instr);
|
||||||
|
while (this.accept(TOP, '=')) {
|
||||||
|
var varName = instr.pop();
|
||||||
|
var varValue = [];
|
||||||
|
var lastInstrIndex = instr.length - 1;
|
||||||
|
if (varName.type === IFUNCALL) {
|
||||||
|
if (!this.tokens.isOperatorEnabled('()=')) {
|
||||||
|
throw new Error(qsTranslate('error', 'Function definition is not permitted.'));
|
||||||
|
}
|
||||||
|
for (var i = 0, len = varName.value + 1; i < len; i++) {
|
||||||
|
var index = lastInstrIndex - i;
|
||||||
|
if (instr[index].type === IVAR) {
|
||||||
|
instr[index] = new Instruction(IVARNAME, instr[index].value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.parseVariableAssignmentExpression(varValue);
|
||||||
|
instr.push(new Instruction(IEXPR, varValue));
|
||||||
|
instr.push(new Instruction(IFUNDEF, varName.value));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (varName.type !== IVAR && varName.type !== IMEMBER) {
|
||||||
|
throw new Error(qsTranslate('error', 'Expected variable for assignment.'));
|
||||||
|
}
|
||||||
|
this.parseVariableAssignmentExpression(varValue);
|
||||||
|
instr.push(new Instruction(IVARNAME, varName.value));
|
||||||
|
instr.push(new Instruction(IEXPR, varValue));
|
||||||
|
instr.push(binaryInstruction('='));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
ParserState.prototype.parseConditionalExpression = function (instr) {
|
ParserState.prototype.parseConditionalExpression = function (instr) {
|
||||||
this.parseOrExpression(instr);
|
this.parseOrExpression(instr);
|
||||||
while (this.accept(TOP, '?')) {
|
while (this.accept(TOP, '?')) {
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
.import "../utils.js" as Utils
|
.import "../utils.js" as Utils
|
||||||
.import "latex.js" as Latex
|
.import "latex.js" as Latex
|
||||||
|
|
||||||
|
const DERIVATION_PRECISION = 0.1
|
||||||
|
|
||||||
var evalVariables = { // Variables not provided by expr-eval.js, needs to be provided manually
|
var evalVariables = { // Variables not provided by expr-eval.js, needs to be provided manually
|
||||||
"pi": Math.PI,
|
"pi": Math.PI,
|
||||||
"PI": Math.PI,
|
"PI": Math.PI,
|
||||||
|
@ -44,56 +46,16 @@ const parser = new ExprEval.Parser()
|
||||||
|
|
||||||
parser.consts = Object.assign({}, parser.consts, evalVariables)
|
parser.consts = Object.assign({}, parser.consts, evalVariables)
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses arguments for a function, returns the corresponding JS function if it exists.
|
|
||||||
* Throws either usage error otherwise.
|
|
||||||
* @param {array} args - Arguments of the function, either [ ExecutableObject ] or [ string, variable ].
|
|
||||||
* @param {string} usage1 - Usage for executable object.
|
|
||||||
* @param {string} usage2 - Usage for string function.
|
|
||||||
* @return {callable} JS function to call..
|
|
||||||
*/
|
|
||||||
function parseArgumentsForFunction(args, usage1, usage2) {
|
|
||||||
let f, target, variable
|
|
||||||
if(args.length == 1) {
|
|
||||||
// Parse object
|
|
||||||
f = args[0]
|
|
||||||
if(typeof f != 'object' || !f.execute)
|
|
||||||
throw EvalError(qsTranslate('usage', 'Usage: %1').arg(usage1))
|
|
||||||
let target = f
|
|
||||||
f = (x) => target.execute(x)
|
|
||||||
} else if(args.length == 2) {
|
|
||||||
// Parse variable
|
|
||||||
[f,variable] = args
|
|
||||||
if(typeof f != 'string' || typeof variable != 'number')
|
|
||||||
throw EvalError(qsTranslate('usage', 'Usage: %1').arg(usage2))
|
|
||||||
f = parser.parse(f).toJSFunction(variable, currentVars)
|
|
||||||
} else
|
|
||||||
throw EvalError(qsTranslate('usage', 'Usage: %1 or\n%2').arg(usage1).arg(usage2)))
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function definition
|
// Function definition
|
||||||
parser.functions.integral = function(a, b, ...args) {
|
parser.functions.integral = function(a, b, f, variable) {
|
||||||
let usage1 = qsTranslate('usage', 'integral(<from: number>, <to: number>, <f: ExecutableObject>)')
|
|
||||||
let usage2 = qsTranslate('usage', 'integral(<from: number>, <to: number>, <f: string>, <variable: string>)')
|
|
||||||
let f = parseArgumentsForFunction(args, usage1, usage2)
|
|
||||||
if(a == null || b == null)
|
|
||||||
throw EvalError(qsTranslate('usage', 'Usage: %1 or\n%2').arg(usage1).arg(usage2)))
|
|
||||||
|
|
||||||
// https://en.wikipedia.org/wiki/Simpson%27s_rule
|
// https://en.wikipedia.org/wiki/Simpson%27s_rule
|
||||||
// Simpler, faster than tokenizing the expression
|
// Simpler, faster than tokenizing the expression
|
||||||
|
f = parser.parse(f).toJSFunction(variable, currentVars)
|
||||||
return (b-a)/6*(f(a)+4*f((a+b)/2)+f(b))
|
return (b-a)/6*(f(a)+4*f((a+b)/2)+f(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
parser.functions.derivative = function(...args) {
|
parser.functions.derivative = function(f, variable, x) {
|
||||||
let usage1 = qsTranslate('usage', 'derivative(<f: ExecutableObject>, <x: variable>)')
|
f = parser.parse(f).toJSFunction(variable, currentVars)
|
||||||
let usage2 = qsTranslate('usage', 'derivative(<f: string>, <variable: string>, <x: variable>)')
|
return (f(x+DERIVATION_PRECISION/2)-f(x-DERIVATION_PRECISION/2))/DERIVATION_PRECISION
|
||||||
let x = args.pop()
|
|
||||||
let f = parseArgumentsForFunction(args, usage1, usage2)
|
|
||||||
if(x == null)
|
|
||||||
throw EvalError(qsTranslate('usage', 'Usage: %1 or\n%2').arg(usage1).arg(usage2)))
|
|
||||||
|
|
||||||
let derivative_precision = x/10
|
|
||||||
return (f(x+derivative_precision/2)-f(x-derivative_precision/2))/derivative_precision
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,16 +73,10 @@ function parif(elem, contents) {
|
||||||
function functionToLatex(f, args) {
|
function functionToLatex(f, args) {
|
||||||
switch(f) {
|
switch(f) {
|
||||||
case "derivative":
|
case "derivative":
|
||||||
if(args.length == 3)
|
|
||||||
return '\\frac{d' + args[0].substr(1, args[0].length-2).replace(new RegExp(args[1].substr(1, args[1].length-2), 'g'), 'x') + '}{dx}';
|
return '\\frac{d' + args[0].substr(1, args[0].length-2).replace(new RegExp(args[1].substr(1, args[1].length-2), 'g'), 'x') + '}{dx}';
|
||||||
else
|
|
||||||
return '\\frac{d' + args[0] + '}{dx}(x)';
|
|
||||||
break;
|
break;
|
||||||
case "integral":
|
case "integral":
|
||||||
if(args.length == 4)
|
|
||||||
return '\\int\\limits_{' + args[0] + '}^{' + args[1] + '}' + args[2].substr(1, args[2].length-2) + ' d' + args[3].substr(1, args[3].length-2);
|
return '\\int\\limits_{' + args[0] + '}^{' + args[1] + '}' + args[2].substr(1, args[2].length-2) + ' d' + args[3].substr(1, args[3].length-2);
|
||||||
else
|
|
||||||
return '\\int\\limits_{' + args[0] + '}^{' + args[1] + '}' + args[2] + '(t) dt';
|
|
||||||
break;
|
break;
|
||||||
case "sqrt":
|
case "sqrt":
|
||||||
return '\\sqrt\\left(' + args.join(', ') + '\\right)';
|
return '\\sqrt\\left(' + args.join(', ') + '\\right)';
|
||||||
|
|
|
@ -85,90 +85,33 @@ const FUNCTIONS = {
|
||||||
'gamma': Polyfill.gamma,
|
'gamma': Polyfill.gamma,
|
||||||
'Γ': Polyfill.gamma,
|
'Γ': Polyfill.gamma,
|
||||||
'roundTo': (x, exp) => Number(x).toFixed(exp),
|
'roundTo': (x, exp) => Number(x).toFixed(exp),
|
||||||
// 'map': Polyfill.arrayMap,
|
'map': Polyfill.arrayMap,
|
||||||
// 'fold': Polyfill.arrayFold,
|
'fold': Polyfill.arrayFold,
|
||||||
// 'filter': Polyfill.arrayFilter,
|
'filter': Polyfill.arrayFilter,
|
||||||
// 'indexOf': Polyfill.indexOf,
|
'indexOf': Polyfill.indexOf,
|
||||||
// 'join': Polyfill.arrayJoin,
|
'join': Polyfill.arrayJoin,
|
||||||
// Integral & derivative (only here for autocomplete).
|
// Integral & derivative (only here for autocomplete).
|
||||||
'integral': () => 0, // TODO: Implement
|
'integral': () => 0, // TODO: Implement
|
||||||
'derivative': () => 0,
|
'derivative': () => 0,
|
||||||
}
|
}
|
||||||
const FUNCTIONS_LIST = Object.keys(FUNCTIONS);
|
const FUNCTIONS_LIST = Object.keys(FUNCTIONS);
|
||||||
|
// TODO: Complete
|
||||||
class P {
|
const DERIVATIVES = {
|
||||||
// Parameter class.
|
"abs": "abs(<1>)/<1>",
|
||||||
constructor(type, name = '', optional = false, multipleAllowed = false) {
|
"acos": "-derivate(<1>)/sqrt(1-(<1>)^2)",
|
||||||
this.name = name
|
"acosh": "derivate(<1>)/sqrt((<1>)^2-1)",
|
||||||
this.type = type
|
"asin": "derivate(<1>)/sqrt(1-(<1>)^2)",
|
||||||
this.optional = optional
|
"asinh": "derivate(<1>)/sqrt((<1>)^2+1)",
|
||||||
this.multipleAllowed = multipleAllowed
|
"atan": "derivate(<1>)/(1+(<1>)^2)",
|
||||||
}
|
"atan2": "",
|
||||||
|
}
|
||||||
toString() {
|
const INTEGRALS = {
|
||||||
base_string = this.type
|
"abs": "integrate(<1>)*sign(<1>)",
|
||||||
if(this.name != '')
|
"acos": "",
|
||||||
base_string = `${this.name}: ${base_string}`
|
"acosh": "",
|
||||||
if(this.multipleAllowed)
|
"asin": "",
|
||||||
base_string += '...'
|
"asinh": "",
|
||||||
if(!this.optional)
|
"atan": "",
|
||||||
base_string = `<${base_string}>`
|
"atan2": "",
|
||||||
else
|
|
||||||
base_string = `[${base_string}]`
|
|
||||||
return base_string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let string = new P('string')
|
|
||||||
let bool = new P('boolean')
|
|
||||||
let number = new P('number')
|
|
||||||
let array = new P('array')
|
|
||||||
|
|
||||||
const FUNCTIONS_USAGE = {
|
|
||||||
'length': [string],
|
|
||||||
'not': [bool],
|
|
||||||
// Math functions
|
|
||||||
'abs': [number],
|
|
||||||
'acos': [number],
|
|
||||||
'acosh': [number],
|
|
||||||
'asin': [number],
|
|
||||||
'asinh': [number],
|
|
||||||
'atan': [number],
|
|
||||||
'atan2': [number],
|
|
||||||
'atanh': [number],
|
|
||||||
'cbrt': [number],
|
|
||||||
'ceil': [number],
|
|
||||||
//'clz32': [number],
|
|
||||||
'cos': [number],
|
|
||||||
'cosh': [number],
|
|
||||||
'exp': [number],
|
|
||||||
'expm1': [number],
|
|
||||||
'floor': [number],
|
|
||||||
//'fround': [number],
|
|
||||||
'hypot': [number],
|
|
||||||
//'imul': [number],
|
|
||||||
'lg': [number],
|
|
||||||
'ln': [number],
|
|
||||||
'log': [number],
|
|
||||||
'log10': [number],
|
|
||||||
'log1p': [number],
|
|
||||||
'log2': [number],
|
|
||||||
'max': [number, number, new P('number', '', true, null, true)],
|
|
||||||
'min': [number, number, new P('number', '', true, null, true)],
|
|
||||||
'pow': [number, new P('number', 'exponent')],
|
|
||||||
'random': [number, number],
|
|
||||||
'round': [number],
|
|
||||||
'sign': [number],
|
|
||||||
'sin': [number],
|
|
||||||
'sinh': [number],
|
|
||||||
'sqrt': [number],
|
|
||||||
'tan': [number],
|
|
||||||
'tanh': [number],
|
|
||||||
'trunc': [number],
|
|
||||||
// Functions in expr-eval, ported here.
|
|
||||||
'fac': [number],
|
|
||||||
'gamma': [number],
|
|
||||||
'Γ': [number],
|
|
||||||
'roundTo': [number, new P('number')],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue