UNPKG

mathjslab

Version:

MathJSLab - An interpreter with language syntax like MATLAB®/Octave, ISBN 978-65-00-82338-7.

598 lines (544 loc) 13.1 kB
import { CharString } from './CharString'; import { ComplexDecimal } from './ComplexDecimal'; import { FunctionHandle } from './FunctionHandle'; import { type ElementType, MultiArray } from './MultiArray'; /** * AST (Abstract Syntax Tree). */ /** * Operator type. */ type TOperator = | '+' | '-' | '.*' | '*' | './' | '/' | '.\\' | '\\' | '.^' | '^' | '.**' | '**' | '<' | '<=' | '==' | '>=' | '>' | '!=' | '~=' | '&' | '|' | '&&' | '||' | '=' | '+=' | '-=' | '*=' | '/=' | '\\=' | '^=' | '**=' | '.*=' | './=' | '.\\=' | '.^=' | '.**=' | '&=' | '|=' | '()' | '!' | '~' | '+_' | '-_' | '++_' | '--_' | ".'" | "'" | '_++' | '_--'; /** * Common primary node. */ interface PrimaryNode { type: string | number; parent?: any; index?: number; omitOut?: boolean; start?: { line: number; column: number }; stop?: { line: number; column: number }; } type NodeInput = NodeExpr | NodeList | NodeDeclaration | NodeIf; /** * Expression node. */ type NodeExpr = ElementType | NodeIdentifier | NodeIndexExpr | NodeOperation | NodeRange | NodeIndirectRef | NodeReturnList | any; /** * Reserved node. */ interface NodeReserved extends PrimaryNode {} /** * Literal node. */ interface NodeLiteral extends PrimaryNode {} /** * Name node. */ interface NodeIdentifier extends PrimaryNode { type: 'IDENT'; id: string; } /** * Command word list node. */ interface NodeCmdWList extends PrimaryNode { type: 'CMDWLIST'; id: string; args: CharString[]; omitAns?: boolean; } /** * Expression and arguments node. */ interface NodeIndexExpr extends PrimaryNode { type: 'IDX'; expr: NodeExpr; args: NodeExpr[]; delim: '()' | '{}'; } /** * Range node. */ interface NodeRange extends PrimaryNode { type: 'RANGE'; start_: NodeExpr | null; stop_: NodeExpr | null; stride_: NodeExpr | null; } /** * Operation node. */ type NodeOperation = UnaryOperation | BinaryOperation; /** * Unary operation node. */ type UnaryOperation = UnaryOperationL | UnaryOperationR; /** * Right unary operation node. */ interface UnaryOperationR extends PrimaryNode { right: NodeExpr; } /** * Left unary operation node. */ interface UnaryOperationL extends PrimaryNode { left: NodeExpr; omitAns?: boolean; // To omit result to be stored in 'ans' variable. } /** * Binary operation. */ interface BinaryOperation extends PrimaryNode { left: NodeExpr; right: NodeExpr; omitAns?: boolean; // To omit result to be stored in 'ans' variable. } /** * List node */ interface NodeList extends PrimaryNode { type: 'LIST'; list: NodeInput[]; } interface NodeIndirectRef extends PrimaryNode { type: '.'; obj: NodeExpr; field: (string | NodeExpr)[]; } type ReturnSelector = (length: number, index: number) => any; /** * Return list node */ interface NodeReturnList extends PrimaryNode { type: 'RETLIST'; selector: ReturnSelector; } interface NodeFunction extends PrimaryNode { type: 'FCN'; id: string; return: NodeInput[]; parameter: NodeInput[]; arguments: NodeInput[]; statements: NodeInput[]; } interface NodeArgumentValidation { name: NodeIdentifier; size: NodeInput[]; class: NodeIdentifier | null; functions: NodeInput[]; default: NodeExpr; // NodeExpr can be null. } interface NodeArguments { attribute: NodeIdentifier | null; validation: NodeArgumentValidation[]; } interface NodeDeclaration extends PrimaryNode { type: 'GLOBAL' | 'PERSIST'; list: NodeExpr[]; } interface NodeIf extends PrimaryNode { type: 'IF'; expression: NodeExpr[]; then: NodeList[]; else: NodeList | null; omitAns?: boolean; } interface NodeElseIf { expression: NodeExpr; then: NodeList; } interface NodeElse { else: NodeList; } /** * AST (Abstract Syntax Tree) constructor methods. */ const nodeString = CharString.create; const nodeNumber = ComplexDecimal.parse; const firstRow = MultiArray.firstRow; const appendRow = MultiArray.appendRow; const emptyArray = MultiArray.emptyArray; /** * Create literal node. * @param type * @returns */ const nodeLiteral = (type: string): NodeLiteral => ({ type }); /** * Create name node. * @param nodeid * @returns */ const nodeIdentifier = (id: string): NodeIdentifier => { return { type: 'IDENT', id: id.replace(/(\r\n|[\n\r])|[\ ]/gm, ''), }; }; /** * Create command word list node. * @param nodename * @param nodelist * @returns */ const nodeCmdWList = (nodename: NodeIdentifier, nodelist: NodeList): NodeCmdWList => { return { type: 'CMDWLIST', id: nodename.id, args: nodelist ? (nodelist.list as CharString[]) : [], omitAns: true, }; }; /** * Create expression and arguments node. * @param nodeexpr * @param nodelist * @returns */ const nodeIndexExpr = (nodeexpr: NodeExpr, nodelist: NodeList | null = null, delimiter: '()' | '{}' = '()'): NodeIndexExpr => { return { type: 'IDX', expr: nodeexpr, args: nodelist ? (nodelist.list as NodeExpr[]) : [], delim: delimiter, }; }; /** * Create range node. * @param start_ * @param stop_ * @param stride_ * @returns NodeRange. */ const nodeRange = (start_: NodeExpr, stop_: NodeExpr, stride_?: NodeExpr): NodeRange => { return { type: 'RANGE', start_, stop_, stride_: stride_ ?? null, }; }; /** * Create operator node. * @param op * @param data1 * @param data2 * @returns */ const nodeOp = (op: TOperator, data1: NodeExpr, data2?: NodeExpr): NodeOperation => { switch (op) { case '+': case '-': case '.*': case '*': case './': case '/': case '.\\': case '\\': case '.^': case '^': case '.**': case '**': case '<': case '<=': case '==': case '>=': case '>': case '!=': case '~=': case '&': case '|': case '&&': case '||': return { type: op, left: data1, right: data2 }; case '=': case '+=': case '-=': case '*=': case '/=': case '\\=': case '^=': case '**=': case '.*=': case './=': case '.\\=': case '.^=': case '.**=': case '&=': case '|=': return { type: op, left: data1, right: data2, omitAns: true }; case '()': case '!': case '~': case '+_': case '-_': return { type: op, right: data1 }; case '++_': case '--_': return { type: op, right: data1, omitAns: true }; case ".'": case "'": return { type: op, left: data1 }; case '_++': case '_--': return { type: op, left: data1, omitAns: true }; default: return { type: `INVALID:${op}` } as NodeOperation; } }; /** * Create first element of list node. * @param node First element of list node. * @returns A NodeList. */ const nodeListFirst = (node?: NodeInput): NodeList => { if (node) { const result = { type: 'LIST', list: [node], }; node.parent = result; return result as NodeList; } else { return { type: 'LIST', list: [], }; } }; /** * Append node to list node. * @param lnode NodeList. * @param node Element to append to list. * @returns NodeList with element appended. */ const appendNodeList = (lnode: NodeList, node: NodeInput): NodeList => { node!.parent = lnode; lnode.list.push(node); return lnode; }; const nodeList = (list: NodeInput[]): NodeList => { return { type: 'LIST', list, }; }; /** * Create first row of a MultiArray. * @param row * @returns */ const nodeFirstRow = (row: NodeList | null = null, iscell?: boolean): MultiArray => { if (row) { return firstRow(row.list as ElementType[], iscell); } else { return emptyArray(iscell); } }; /** * Append row to MultiArray. * @param M * @param row * @returns */ const nodeAppendRow = (M: MultiArray, row: NodeList | null = null): MultiArray => { if (row) { return appendRow(M, row.list as ElementType[]); } else { return M; } }; const nodeIndirectRef = (left: NodeExpr, right: string | NodeExpr): NodeIndirectRef => { if (left.type === '.') { left.field.push(right); return left; } else { return { type: '.', obj: left, field: [right], }; } }; /** * Creates NodeReturnList (multiple assignment) * @param selector Left side selector function. * @returns Return list node. */ const nodeReturnList = (selector: ReturnSelector): NodeReturnList => { return { type: 'RETLIST', selector, }; }; const nodeFunctionHandle = (id: NodeIdentifier | null = null, parameter_list: NodeList | null = null, expression: NodeExpr = null): FunctionHandle => { return new FunctionHandle(id ? id.id : null, true, parameter_list ? parameter_list.list : [], expression); }; const nodeFunction = (id: NodeIdentifier, return_list: NodeList, parameter_list: NodeList, arguments_list: NodeList, statements_list: NodeList): NodeFunction => { return { type: 'FCN', id: id.id, return: return_list.list, parameter: parameter_list.list, arguments: arguments_list.list, statements: statements_list.list, }; }; const nodeArgumentValidation = (name: NodeIdentifier, size: NodeList, cl: NodeIdentifier | null = null, functions: NodeList, dflt: NodeExpr = null): NodeArgumentValidation => { return { name, size: size.list, class: cl, functions: functions.list, default: dflt, }; }; const nodeArguments = (attribute: NodeIdentifier | null, validationList: NodeList): NodeArguments => { return { attribute, validation: validationList.list as unknown as NodeArgumentValidation[], }; }; const nodeDeclarationFirst = (type: 'GLOBAL' | 'PERSIST'): NodeDeclaration => { return { type, list: [], }; }; const nodeAppendDeclaration = (node: NodeDeclaration, declaration: NodeExpr): NodeDeclaration => { node.list.push(declaration); return node; }; const nodeIfBegin = (expression: NodeExpr, then: NodeList): NodeIf => { return { type: 'IF', expression: [expression], then: [then], else: null, omitAns: true, }; }; const nodeIfAppendElse = (nodeIf: NodeIf, nodeElse: NodeElse): NodeIf => { nodeIf.else = nodeElse.else; return nodeIf; }; const nodeIfAppendElseIf = (nodeIf: NodeIf, nodeElseIf: NodeElseIf): NodeIf => { nodeIf.expression.push(nodeElseIf.expression); nodeIf.then.push(nodeElseIf.then); return nodeIf; }; const nodeElseIf = (expression: NodeExpr, then: NodeList): NodeElseIf => { return { expression, then, }; }; const nodeElse = (elseStmt: NodeList): NodeElse => { return { else: elseStmt, }; }; export type { TOperator, NodeInput, NodeExpr, NodeOperation, UnaryOperation, ReturnSelector }; export { PrimaryNode, NodeReserved, NodeLiteral, NodeIdentifier, NodeCmdWList, NodeIndexExpr, NodeRange, UnaryOperationR, UnaryOperationL, BinaryOperation, NodeList, NodeIndirectRef, NodeReturnList, NodeFunction, NodeArgumentValidation, NodeArguments, NodeDeclaration, NodeIf, NodeElseIf, NodeElse, nodeString, nodeNumber, firstRow, appendRow, emptyArray, nodeLiteral, nodeIdentifier, nodeCmdWList, nodeIndexExpr, nodeRange, nodeOp, nodeListFirst, appendNodeList, nodeList, nodeFirstRow, nodeAppendRow, nodeIndirectRef, nodeReturnList, nodeFunctionHandle, nodeFunction, nodeArgumentValidation, nodeArguments, nodeDeclarationFirst, nodeAppendDeclaration, nodeIfBegin, nodeIfAppendElse, nodeIfAppendElseIf, nodeElseIf, nodeElse, };