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) {
|
||||
// Open editor
|
||||
console.log(obj, obj.prototype)
|
||||
objectEditor.obj = obj
|
||||
objectEditor.objType = obj.type
|
||||
objectEditor.objIndex = Objects.currentObjects[obj.type].indexOf(obj)
|
||||
|
|
|
@ -433,7 +433,7 @@ Item {
|
|||
itemSelected: parent.itemSelected
|
||||
categoryItems: Parsing.CONSTANTS_LIST
|
||||
autocompleteGenerator: (item) => {return {
|
||||
'text': item, 'annotation': Parsing.CONSTANTS[item],
|
||||
'text': item, 'annotation': '',
|
||||
'autocomplete': item + " ", 'cursorFinalOffset': 0
|
||||
}}
|
||||
baseText: parent.visible ? parent.currentToken.value : ""
|
||||
|
|
|
@ -27,7 +27,7 @@ var ADDITIONAL_VARCHARS = [
|
|||
"ₜ","¹","²","³","⁴","⁵","⁶",
|
||||
"⁷","⁸","⁹","⁰","₁","₂","₃",
|
||||
"₄","₅","₆","₇","₈","₉","₀",
|
||||
"∞","π"
|
||||
"∞"
|
||||
]
|
||||
|
||||
function Instruction(type, value) {
|
||||
|
@ -1117,7 +1117,7 @@ ParserState.prototype.parseExpression = function (instr) {
|
|||
if (this.parseUntilEndStatement(instr, exprInstr)) {
|
||||
return;
|
||||
}
|
||||
this.parseConditionalExpression(exprInstr);
|
||||
this.parseVariableAssignmentExpression(exprInstr);
|
||||
if (this.parseUntilEndStatement(instr, exprInstr)) {
|
||||
return;
|
||||
}
|
||||
|
@ -1157,6 +1157,37 @@ ParserState.prototype.parseArrayList = function (instr) {
|
|||
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) {
|
||||
this.parseOrExpression(instr);
|
||||
while (this.accept(TOP, '?')) {
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
.import "../utils.js" as Utils
|
||||
.import "latex.js" as Latex
|
||||
|
||||
const DERIVATION_PRECISION = 0.1
|
||||
|
||||
var evalVariables = { // Variables not provided by expr-eval.js, needs to be provided manually
|
||||
"pi": Math.PI,
|
||||
"PI": Math.PI,
|
||||
|
@ -44,56 +46,16 @@ const parser = new ExprEval.Parser()
|
|||
|
||||
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
|
||||
parser.functions.integral = function(a, b, ...args) {
|
||||
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)))
|
||||
|
||||
parser.functions.integral = function(a, b, f, variable) {
|
||||
// https://en.wikipedia.org/wiki/Simpson%27s_rule
|
||||
// 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))
|
||||
}
|
||||
|
||||
parser.functions.derivative = function(...args) {
|
||||
let usage1 = qsTranslate('usage', 'derivative(<f: ExecutableObject>, <x: variable>)')
|
||||
let usage2 = qsTranslate('usage', 'derivative(<f: string>, <variable: string>, <x: variable>)')
|
||||
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
|
||||
parser.functions.derivative = function(f, variable, x) {
|
||||
f = parser.parse(f).toJSFunction(variable, currentVars)
|
||||
return (f(x+DERIVATION_PRECISION/2)-f(x-DERIVATION_PRECISION/2))/DERIVATION_PRECISION
|
||||
}
|
||||
|
||||
|
|
|
@ -73,16 +73,10 @@ function parif(elem, contents) {
|
|||
function functionToLatex(f, args) {
|
||||
switch(f) {
|
||||
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}';
|
||||
else
|
||||
return '\\frac{d' + args[0] + '}{dx}(x)';
|
||||
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}';
|
||||
break;
|
||||
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);
|
||||
else
|
||||
return '\\int\\limits_{' + args[0] + '}^{' + args[1] + '}' + args[2] + '(t) dt';
|
||||
return '\\int\\limits_{' + args[0] + '}^{' + args[1] + '}' + args[2].substr(1, args[2].length-2) + ' d' + args[3].substr(1, args[3].length-2);
|
||||
break;
|
||||
case "sqrt":
|
||||
return '\\sqrt\\left(' + args.join(', ') + '\\right)';
|
||||
|
|
|
@ -85,90 +85,33 @@ const FUNCTIONS = {
|
|||
'gamma': Polyfill.gamma,
|
||||
'Γ': Polyfill.gamma,
|
||||
'roundTo': (x, exp) => Number(x).toFixed(exp),
|
||||
// 'map': Polyfill.arrayMap,
|
||||
// 'fold': Polyfill.arrayFold,
|
||||
// 'filter': Polyfill.arrayFilter,
|
||||
// 'indexOf': Polyfill.indexOf,
|
||||
// 'join': Polyfill.arrayJoin,
|
||||
'map': Polyfill.arrayMap,
|
||||
'fold': Polyfill.arrayFold,
|
||||
'filter': Polyfill.arrayFilter,
|
||||
'indexOf': Polyfill.indexOf,
|
||||
'join': Polyfill.arrayJoin,
|
||||
// Integral & derivative (only here for autocomplete).
|
||||
'integral': () => 0, // TODO: Implement
|
||||
'derivative': () => 0,
|
||||
}
|
||||
const FUNCTIONS_LIST = Object.keys(FUNCTIONS);
|
||||
|
||||
class P {
|
||||
// Parameter class.
|
||||
constructor(type, name = '', optional = false, multipleAllowed = false) {
|
||||
this.name = name
|
||||
this.type = type
|
||||
this.optional = optional
|
||||
this.multipleAllowed = multipleAllowed
|
||||
}
|
||||
|
||||
toString() {
|
||||
base_string = this.type
|
||||
if(this.name != '')
|
||||
base_string = `${this.name}: ${base_string}`
|
||||
if(this.multipleAllowed)
|
||||
base_string += '...'
|
||||
if(!this.optional)
|
||||
base_string = `<${base_string}>`
|
||||
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')],
|
||||
// TODO: Complete
|
||||
const DERIVATIVES = {
|
||||
"abs": "abs(<1>)/<1>",
|
||||
"acos": "-derivate(<1>)/sqrt(1-(<1>)^2)",
|
||||
"acosh": "derivate(<1>)/sqrt((<1>)^2-1)",
|
||||
"asin": "derivate(<1>)/sqrt(1-(<1>)^2)",
|
||||
"asinh": "derivate(<1>)/sqrt((<1>)^2+1)",
|
||||
"atan": "derivate(<1>)/(1+(<1>)^2)",
|
||||
"atan2": "",
|
||||
}
|
||||
const INTEGRALS = {
|
||||
"abs": "integrate(<1>)*sign(<1>)",
|
||||
"acos": "",
|
||||
"acosh": "",
|
||||
"asin": "",
|
||||
"asinh": "",
|
||||
"atan": "",
|
||||
"atan2": "",
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue