import { type NodeInput, type NodeExpr, type NodeIdentifier, type BuiltInFunctionTable, CharString, ComplexDecimal, AST } from 'mathjslab';
import { insertOutput } from './outputFunction';
import { PlotEngine } from './PlotEngine';
import { openFileDialog } from './openFileDialog';
import { Markdown } from './Markdown';
import { appEngine } from './appEngine';
const openFileOptionMathJSLab: OpenFilePickerOptions & { multiple?: false | undefined } = {
multiple: false,
types: [
{
description: 'MathJSLab files',
accept: { 'text/plain': ['.txt', '.m'] },
},
],
excludeAcceptAllOption: true,
};
const openFileOptionMarkdown: OpenFilePickerOptions & { multiple?: false | undefined } = {
multiple: false,
types: [
{
description: 'Markdown files',
accept: { 'text/plain': ['.txt', '.md'] },
},
],
excludeAcceptAllOption: true,
};
const externalFunctionTable: BuiltInFunctionTable = {
...PlotEngine.externalFunctionTable,
summation: {
type: 'BUILTIN',
mapper: false,
ev: [false, true, true, false],
func: (variable: NodeIdentifier, start: ComplexDecimal, end: ComplexDecimal, expr: NodeExpr): ComplexDecimal => {
if (!start.im.eq(0)) throw new Error('complex number sum index');
if (!end.im.eq(0)) throw new Error('complex number sum index');
let result: ComplexDecimal = ComplexDecimal.zero();
const sum_function_name = `summation_${globalThis.crypto.randomUUID()}`;
appEngine.evaluator.localTable[sum_function_name] = {};
for (let i = start.re.toNumber(); i <= end.re.toNumber(); i++) {
appEngine.evaluator.localTable[sum_function_name][variable.id] = ComplexDecimal.create(i, 0);
result = ComplexDecimal.add(result, appEngine.evaluator.Evaluator(expr, true, sum_function_name));
}
delete appEngine.evaluator.localTable[sum_function_name];
return result;
},
UnparserMathML: (tree: NodeInput): string => {
return (
'∑' +
appEngine.evaluator.UnparserMathML(tree.args[0]) +
'=' +
appEngine.evaluator.UnparserMathML(tree.args[1]) +
'' +
appEngine.evaluator.UnparserMathML(tree.args[2]) +
'' +
'' +
appEngine.evaluator.UnparserMathML(tree.args[3]) +
''
);
},
},
productory: {
type: 'BUILTIN',
mapper: false,
ev: [false, true, true, false],
func: (variable: NodeIdentifier, start: ComplexDecimal, end: ComplexDecimal, expr: NodeExpr): ComplexDecimal => {
if (!start.im.eq(0)) throw new Error('complex number prod index');
if (!end.im.eq(0)) throw new Error('complex number prod index');
let result: ComplexDecimal = ComplexDecimal.one();
const prod_function_name = `productory_${globalThis.crypto.randomUUID()}`;
appEngine.evaluator.localTable[prod_function_name] = {};
for (let i = start.re.toNumber(); i <= end.re.toNumber(); i++) {
appEngine.evaluator.localTable[prod_function_name][variable.id] = ComplexDecimal.create(i, 0);
result = ComplexDecimal.mul(result, appEngine.evaluator.Evaluator(expr, true, prod_function_name));
}
delete appEngine.evaluator.localTable[prod_function_name];
return result;
},
UnparserMathML: (tree: NodeInput): string => {
return (
'∏' +
appEngine.evaluator.UnparserMathML(tree.args[0]) +
'=' +
appEngine.evaluator.UnparserMathML(tree.args[1]) +
'' +
appEngine.evaluator.UnparserMathML(tree.args[2]) +
'' +
'' +
appEngine.evaluator.UnparserMathML(tree.args[3]) +
''
);
},
},
open: {
type: 'BUILTIN',
mapper: false,
ev: [true],
func: (url?: CharString): NodeExpr => {
const promptEntry = appEngine.shell.commandShell.element.promptSet.currentPrompt;
if (url) {
if (appEngine.shell.isFileProtocol) {
promptEntry.element.frameBox.className = 'bad';
promptEntry.element.output.innerHTML = 'open function unavailable offline.';
} else {
globalThis
.fetch(url.str)
.then((response) => {
if (response.ok) {
return response.text();
} else {
throw new URIError('Load error.');
}
})
.then((responseFile: string) => {
appEngine.shell.commandShell.load(responseFile);
})
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
.catch((error) => {
promptEntry.element.frameBox.className = 'bad';
promptEntry.element.output.innerHTML = `open: error loading ${url.str}`;
});
}
return AST.nodeIndexExpr(AST.nodeIdentifier('open'), AST.nodeList([url.str]));
} else {
openFileDialog((content: string) => {
appEngine.shell.commandShell.load(content);
}, openFileOptionMathJSLab);
return AST.nodeIndexExpr(AST.nodeIdentifier('open'), AST.nodeListFirst());
}
},
},
markdown: {
type: 'BUILTIN',
mapper: false,
ev: [true],
func: (url?: CharString): NodeExpr => {
const promptEntry = appEngine.shell.commandShell.element.promptSet.currentPrompt;
if (url) {
if (appEngine.shell.isFileProtocol) {
promptEntry.element.frameBox.className = 'bad';
promptEntry.element.output.innerHTML = 'markdown function unavailable offline.';
} else {
globalThis
.fetch(url.str)
.then((response) => {
if (response.ok) {
return response.text();
} else {
throw new URIError('Load error.');
}
})
.then((responseFile: string) => {
promptEntry.element.frameBox.className = 'doc';
promptEntry.element.output.innerHTML = Markdown.parse(responseFile);
Markdown.typeset(promptEntry.element.output);
})
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
.catch((error) => {
promptEntry.element.frameBox.className = 'bad';
promptEntry.element.output.innerHTML = `markdown: error loading ${url.str}`;
});
}
return AST.nodeIndexExpr(AST.nodeIdentifier('markdown'), AST.nodeList([url.str]));
} else {
openFileDialog((content: string) => {
promptEntry.element.frameBox.className = 'doc';
promptEntry.element.output.innerHTML = Markdown.parse(content);
Markdown.typeset(promptEntry.element.output);
}, openFileOptionMarkdown);
return AST.nodeIndexExpr(AST.nodeIdentifier('markdown'), AST.nodeListFirst());
}
},
},
load: {
type: 'BUILTIN',
mapper: false,
ev: [true],
func: (...url: CharString[]): NodeExpr => {
const promptEntry = appEngine.shell.commandShell.element.promptSet.currentPrompt;
const loadContent = (content: string, name: string) => {
let error: boolean = false;
let errorMessage: string = '';
insertOutput.type = '';
promptEntry.element.output.innerHTML = '';
try {
const tree = appEngine.evaluator.Parse(content);
if (tree) {
appEngine.evaluator.Evaluate(tree);
}
} catch (e) {
error = true;
errorMessage = `load: error loading ${name}: ${e}`;
}
if (error) {
promptEntry.element.frameBox.className = 'bad';
promptEntry.element.output.innerHTML = errorMessage;
} else {
promptEntry.element.frameBox.className = 'good';
promptEntry.element.output.innerHTML = `Loaded script from ${name} br>`;
}
appEngine.shell.commandShell.refreshNameList();
};
if (url.length > 0) {
if (appEngine.shell.isFileProtocol) {
promptEntry.element.frameBox.className = 'bad';
promptEntry.element.output.innerHTML = 'load function unavailable offline.';
} else {
url.forEach((file: CharString) => {
globalThis
.fetch(file.str)
.then((response) => {
if (response.ok) {
return response.text();
} else {
throw new URIError('Load error.');
}
})
.then((responseFile: string) => {
loadContent(responseFile, file.str);
})
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
.catch((error) => {
promptEntry.element.frameBox.className = 'bad';
promptEntry.element.output.innerHTML = `load: error loading ${file.str}`;
});
});
}
return AST.nodeIndexExpr(AST.nodeIdentifier('load'), AST.nodeList([...url.map((url) => AST.nodeString(url.str))]));
} else {
openFileDialog((content: string) => {
loadContent(content, 'file');
}, openFileOptionMathJSLab);
return AST.nodeIndexExpr(AST.nodeIdentifier('load'), AST.nodeListFirst());
}
},
},
};
export { externalFunctionTable };
export default { externalFunctionTable };