import { IVProgParser } from "../ast/ivprogParser"; import * as Expressions from "../ast/expressions"; import { Types } from "../typeSystem/types"; import { convertBoolToString } from "../typeSystem/parsers"; import * as Commands from "../ast/commands"; import { ArrayType } from "../typeSystem/array_type"; import { Literal } from "../ast/expressions/literal"; const TYPES = { VARIABLE: "var", CONST: "const", FUNCTION: "function", RELATIONAL: "relational", LOGIC: "logic", ARITHMETIC: "arithmetic", }; function translateOp (type, op) { switch (type) { case TYPES.ARITHMETIC: return op.value; case TYPES.RELATIONAL: return op.value; case TYPES.LOGIC: { if (op.ord === 11) { return "and"; } else if (op.ord === 12) { return "or"; } else { return "not"; } } } } function getOpType (op) { switch (op.ord) { case 0: case 1: case 2: case 3: case 4: return TYPES.ARITHMETIC; case 5: case 6: case 7: case 8: case 9: case 10: return TYPES.RELATIONAL; default: return TYPES.LOGIC; } } /** * @param {Commands.Case} switchCase * */ function switchCaseWalker (switchCase) { const commands = switchCase.commands.map(commandWalker); const expression = switchCase.isDefault ? null : expressionWalker(switchCase.expression); return { type: "switchcase", line: switchCase.sourceInfo.line, expression, commands, }; } /** * @param {Commands.Switch} switchCommand * */ function switchWalker (switchCommand) { const expression = expressionWalker(switchCommand.expression); const cases = switchCommand.cases.map(switchCaseWalker); return { type: "switch", expression, cases, }; } /** * @param {Commands.Return} returnCommand * */ function returnWalker (returnCommand) { const expression = expressionWalker(returnCommand.expression); return { type: "return", expression, }; } function breakWalker (_) { return { type: "break" }; } /** * @param {Commands.For} forLoop * */ function forWalker (forLoop) { const var_attribution = expressionWalker(forLoop.for_id); const var_initial = expressionWalker(forLoop.for_from); const condition = expressionWalker(forLoop.for_to); const step_expression = forLoop.for_pass ? expressionWalker(forLoop.for_pass) : []; const commands = forLoop.commands.map(commandWalker); return { type: "repeatNtimes", var_attribution, var_initial, condition, step_expression, commands, }; } /** * @param {Commands.While} whileLoop * */ function whileWalker (whileLoop) { const expression = expressionWalker(whileLoop.expression); const commands = whileLoop.commands.map(commandWalker); let type = whileLoop.testFirst ? "whiletrue" : "dowhiletrue"; return { type, expression, commands, }; } /** * @param {Commands.IfThenElse} ifthenelse * */ function ifThenElseWalker (ifthenelse) { //ifthenelse. const expression = expressionWalker(ifthenelse.condition); const ifTrue = ifthenelse.ifTrue.commands.map(commandWalker); let ifFalse = []; if (ifthenelse.ifFalse) { if (ifthenelse.ifFalse instanceof Commands.CommandBlock) { ifFalse = ifthenelse.ifFalse.commands.map(commandWalker); } else { ifFalse = [ifThenElseWalker(ifthenelse.ifFalse)]; } } return { type: "iftrue", expression, ifTrue, ifFalse, }; } /** * @param {Commands.Assign} assingment * */ function assignmentWalker (assingment) { let variable = null; if (assingment instanceof Commands.ArrayIndexAssign) { const line = expressionWalker(assingment.line); let arrayClass = "vector"; let column = null; if (assingment.column) { arrayClass = "matrix"; column = expressionWalker(assingment.column); } variable = [ { instance: "expression", type: TYPES.VARIABLE, class: arrayClass, column: column, line: line, value: assingment.id, }, ]; } else { variable = [ { instance: "expression", type: TYPES.VARIABLE, value: assingment.id }, ]; } const expression = expressionWalker(assingment.expression); return { type: "attribution", variable, expression, }; } /** * @param {Command} command * */ function commandWalker (command) { let parsedCommand = null; if (command instanceof Commands.FunctionCall) { parsedCommand = functionCallWalker(command); } else if (command instanceof Commands.Assign) { parsedCommand = assignmentWalker(command); } else if (command instanceof Commands.IfThenElse) { parsedCommand = ifThenElseWalker(command); } else if (command instanceof Commands.While) { parsedCommand = whileWalker(command); } else if (command instanceof Commands.Break) { parsedCommand = breakWalker(command); } else if (command instanceof Commands.Return) { parsedCommand = returnWalker(command); } else if (command instanceof Commands.Switch) { parsedCommand = switchWalker(command); } else if (command instanceof Commands.For) { parsedCommand = forWalker(command); } else { throw new Error("not implemented"); } parsedCommand.line = command.sourceInfo.line; return parsedCommand; } /** * @param {Commands.FunctionCall} functionCall * */ function functionCallWalker (functionCall) { let name = functionCall.id; if (name.indexOf(".") !== -1) { name = name.split(".")[1]; } const parameters = functionCall.actualParameters.map(expressionWalker); if (name === "$write") { const lastInput = parameters[parameters.length - 1][0]; // if lastInput is an object with value === '\n', newLine is true const newLine = lastInput.value && lastInput.value.match(/^\n$/) !== null; const content = newLine ? parameters.slice(0, parameters.length - 1) : parameters; return { type: "writer", newLine, content, }; } if (name === "$read") { return { type: "reader", variable: parameters[0], }; } return { type: "functioncall", parameters_list: parameters, name: functionCall.id, }; } /** * @param {Commands.Function} func * */ function functionWalker (func) { const funcDeclaration = { name: func.name, line: func.sourceInfo.line, return_type: "", return_dimensions: 0, parameters_list: [], variables_list: [], commands: [], }; if (func.returnType instanceof ArrayType) { funcDeclaration.return_type = func.returnType.innerType.value; funcDeclaration.return_dimensions = func.returnType.dimensions; } else { funcDeclaration.return_type = func.returnType.value; } funcDeclaration.parameters_list = func.formalParameters.map( functionParameterWalker ); funcDeclaration.variables_list = func.variablesDeclarations.map( variableDeclarationWalker ); funcDeclaration.commands = func.commands.map(commandWalker); return funcDeclaration; } /** * @param {Commands.FormalParameter} formalParameter * */ function functionParameterWalker (formalParameter) { const variable = { name: formalParameter.id, line: formalParameter.sourceInfo.line, type: "", rows: 0, columns: 0, dimension: 0, value: 0, is_const: false, reference: formalParameter.byRef, }; if (formalParameter.type instanceof ArrayType) { variable.type = formalParameter.type.innerType.value; variable.dimension = formalParameter.type.dimensions; } else { variable.type = formalParameter.type.value; } return variable; } /** * @param {Commands.Declaration} command * @param {boolean} global * */ function variableDeclarationWalker (command, global = false) { const variable = { name: command.id, line: command.sourceInfo.line, type: "", rows: 0, columns: 0, dimension: 0, value: 0, is_const: false, }; variable.is_const = global && command.isConst; if (command instanceof Commands.ArrayDeclaration) { // array const lines = expressionWalker(command.lines).pop(); variable.type = command.type.innerType.value; if (command.isVector) { variable.columns = lines.value; variable.dimension = 1; const values = command.initial.value.map((exp) => variableInitialWalker(exp) ); variable.value = values; } else { const columns = expressionWalker(command.columns).pop(); variable.dimension = 2; variable.rows = lines.value; variable.columns = columns.value; const values = command.initial.value.map((rows) => rows.value.map((exp) => variableInitialWalker(exp)) ); variable.value = values; } } else { // atomic variable.type = command.type.value; variable.value = variableInitialWalker(command.initial); } return variable; } /** * @param {any} expression * */ function variableInitialWalker (expression) { if (expression instanceof Expressions.UnaryApp) { const left = variableInitialWalker(expression.left); const opType = getOpType(expression.op); if (opType !== TYPES.ARITHMETIC) { throw new Error( "invalid variable initial value: " + expression.toString() ); } return `${expression.op.value}${left}`; } else if (expression instanceof Expressions.BoolLiteral) { const value = expression.value; return convertBoolToString(value); } else if (expression instanceof Literal) { let value = expression.value; if (expression.value.toNumber) { if ( Types.REAL.isCompatible(expression.type) && expression.value.decimalPlaces() == 0 ) { value = Number(expression.value.toFixed(2)); } else { value = expression.value.toNumber(); } } return value; } throw new Error("invalid variable initial value: " + expression.toString()); } /** * * @return {[]} **/ function expressionWalker (expression) { let result; if (expression instanceof Expressions.VariableLiteral) { result = [ { instance: "expression", type: TYPES.VARIABLE, value: expression.id }, ]; } else if (expression instanceof Expressions.FunctionCall) { const funcObj = { instance: "expression", type: TYPES.FUNCTION, value: expression.id, }; const paramsList = expression.actualParameters.map((e) => expressionWalker(e) ); //const params = Array.prototype.concat.apply([], paramsList); funcObj.params = paramsList; result = [funcObj]; } else if (expression instanceof Expressions.UnaryApp) { const left = expressionWalker(expression.left); const opType = getOpType(expression.op); const opValue = translateOp(opType, expression.op); result = [{ instance: "operator", type: opType, value: opValue }, ...left]; } else if (expression instanceof Expressions.InfixApp) { const left = expressionWalker(expression.left); const right = expressionWalker(expression.right); const opType = getOpType(expression.op); const opValue = translateOp(opType, expression.op); result = [ ...left, { instance: "operator", type: opType, value: opValue }, ...right, ]; } else if (expression instanceof Expressions.ArrayAccess) { const line = expressionWalker(expression.line); let arrayClass = "vector"; let column = null; if (expression.column) { arrayClass = "matrix"; column = expressionWalker(expression.column); } result = [ { instance: "expression", type: TYPES.VARIABLE, class: arrayClass, column: column, line: line, value: expression.id, }, ]; } else if (expression instanceof Expressions.BoolLiteral) { const value = expression.value; result = [ { instance: "expression", class: "simple", type: TYPES.CONST, value: convertBoolToString(value), }, ]; } else { let value = expression.value; if (expression.value.toNumber) { if ( Types.REAL.isCompatible(expression.type) && expression.value.decimalPlaces() == 0 ) { value = Number(expression.value.toFixed(2)); } else { value = expression.value.toNumber(); } } result = [ { instance: "expression", class: "simple", type: TYPES.CONST, value: value, }, ]; } if (expression.parenthesis) return ["(", ...result, ")"]; else return result; } export function parseExpression (text) { const parser = IVProgParser.createParser(text); const expressionAST = parser.parseExpressionOR(); return expressionWalker(expressionAST); } /** * @param {string} text * */ export function parseCode (text) { const parser = IVProgParser.createParser(text, false); const codeLinesMap = new Map(); const tokens = Array.from(parser.lexer.reset(text)); const tokenStream = []; for (const token of tokens) { if (token.type === parser.ruleNames.ERROR) { return null; } if (token.type === parser.ruleNames.COMMENTS) { for (let i = 0; i <= token.lineBreaks; i++) { if (codeLinesMap.has(i + token.line)) codeLinesMap.get(i + token.line).push(token); else codeLinesMap.set(i + token.line, [token]); } continue; } if (token.type !== parser.ruleNames.WHITESPACE) { tokenStream.push(token); } } parser.fill(tokenStream); try { const program = parser.parseTree(); const globals = program.global.map((decl) => variableDeclarationWalker(decl, true) ); const functions = program.functions.map(functionWalker); return { globals, functions }; } catch (e) { console.error(e); return null; } }