import { BaseConverter } from "./../baseConverter"; import { Declaration, ArrayDeclaration, FunctionCall } from "../../ast/commands"; import { Types } from "../../typeSystem/types"; import { ArrayType } from "../../typeSystem/array_type"; import { InfixApp, StringLiteral, VariableLiteral, UnaryApp, BoolLiteral } from "../../ast/expressions"; import { TextNode } from "./ast/textNode"; import { Pointer } from "./types/pointer"; import { Operators } from "../../ast/operators"; import { LocalizedStrings } from "./../../services/localizedStringsService"; import { LanguageDefinedFunction } from "../../processor/definedFunctions"; const FORBIDDEN_FUNCS = [ "$isReal", "$isInt", "$isBool", "$castReal", "$castInt", "$castBool", "$castString", "$max", "$min", "$invert" ]; export class CConverter extends BaseConverter { constructor (ivprogAST) { super(ivprogAST); this.globalVars = []; this.cAST = { includes: new Set(), globalVars: [], functionDecl: [], functions: [] }; this.currentVars = []; this.currentCommands = []; this.auxCounter = 1; } createAuxID () { let id = null; let symbol = true; while (symbol) { id = `aux_${this.auxCounter}`; this.auxCounter += 1; symbol = this.findSymbol(id); } return id; } resetContext () { this.currentVars = []; this.currentCommands = []; } toText () { this.doConvertion(); } doConversion () { this.ivprogAST.global.forEach(decl => { if (decl instanceof ArrayDeclaration) { if (Types.STRING.isCompatible(decl.type.innerType)) { throw new Error( "Cannot convert a program that contains an array of text" ); } } this.insertGlobalSymbol(decl.id, decl); if (decl instanceof ArrayDeclaration) { let initial = decl.initial; if (initial) { initial = this.convertArrayInit(initial); } const lines = this.convertExpression(decl.lines); if (decl.isVector) { this.cAST.globalVars.push( new ArrayDeclaration( decl.id, decl.type, lines, null, initial, decl.isConst ) ); } else { const columns = this.convertExpression(decl.lines); this.cAST.globalVars.push( new ArrayDeclaration( decl.id, decl.type, lines, columns, initial, decl.isConst ) ); } } else { if (decl.type.isCompatible(Types.STRING)) { if ( decl.initial == null || !(decl.initial instanceof StringLiteral) ) { throw new Error( "String must have a literal as its initial value to be converted to C" ); } } let initial = decl.initial; if (initial) { initial = this.convertExpression(initial); } if (decl.type.isCompatible(Types.STRING)) { // Must be a pointer since concatenation under the same symbol is expected (str = str + "abc") this.cAST.globalVars.push( new Declaration( decl.id, new Pointer(decl.type), initial, decl.isConst ) ); } else { this.cAST.globalVars.push( new Declaration(decl.id, decl.type, initial, decl.isConst) ); } } }); // convert every function... for (let i = 0; i < this.ivprogAST.functions.length; i += 1) { const func = this.ivprogAST.functions[i]; if (func.isMain) { // deal with main function } else { // TODO } } } convertExpression (expression) { const type = this.evaluateExpressionType(expression); if (type instanceof ArrayType) { // TODO } else if (Types.STRING.isCompatible(type)) { return this.convertStringExpression(expression); } else if (Types.BOOLEAN.isCompatible(type)) { return this.convertBooleanExpression(expression); } else { // It's a numeric expression return this.convertNumericExpression(expression); } } convertArrayInit (decl) { return null; } convertNumericExpression (expression) { if (expression instanceof UnaryApp) { const leftExp = this.convertNumericExpression(expression.left); return new TextNode(`${expression.op.value}${leftExp.toString()}`); } else if (expression instanceof InfixApp) { const leftExp = this.convertNumericExpression(expression.left); const rightExp = this.convertNumericExpression(expression.right); return new TextNode( `${leftExp.toString()} ${expression.op.value} ${rightExp.toString()}` ); } else if (expression instanceof FunctionCall) { let funcName = expression.id; const langFun = LanguageDefinedFunction.getFunction(funcName); if (langFun) { this.isValidFunction(funcName); // function is valid, do we need to import any c lib? // tan, cos, sen, sqrt, pow ... const funcInfo = this.convertFunctionCall(funcName); this.cAST.includes.add(funcInfo.lib); funcName = funcInfo.name; } // need to verify every parameter... const paramList = []; for (let i = 0; i < expression.parametersSize; i += 1) { const param = expression.actualParameters[i]; const paramExp = this.convertExpression(param); paramList.push(paramExp.toString()); } const paramString = paramList.join(", "); return TextNode(`${funcName}(${paramString})`); } else { return expression; } } convertBooleanExpression (expression) { if (expression instanceof UnaryApp) { const left = this.convertExpression(expression.left); return new TextNode(`!${left.toString()}`); } else if (expression instanceof InfixApp) { const leftExp = this.convertExpression(expression.left); const rightExp = this.convertExpression(expression.right); const op = expression.op; switch (op.ord) { case Operators.AND.ord: return new TextNode( `${leftExp.toString()} && ${rightExp.toString()}` ); case Operators.OR.ord: return new TextNode( `${leftExp.toString()} || ${rightExp.toString()}` ); default: return new TextNode( `${leftExp.toString()} ${op.value} ${rightExp.toString()}` ); } } else if (expression instanceof BoolLiteral) { this.cAST.includes.add("stdbool.h"); const value = expression.value ? "true" : "false"; return new TextNode(value); } else if (expression instanceof FunctionCall) { // Function call... if it's one of the convert/check functions DO NOT convert the code let funcName = expression.id; const langFun = LanguageDefinedFunction.getFunction(funcName); if (langFun) { this.isValidFunction(funcName); const funcInfo = this.convertFunctionCall(funcName); this.cAST.includes.add(funcInfo.lib); funcName = funcInfo.name; } const paramList = []; for (let i = 0; i < expression.parametersSize; i += 1) { const param = expression.actualParameters[i]; const paramExp = this.convertExpression(param); paramList.push(paramExp.toString()); } const paramString = paramList.join(", "); return TextNode(`${funcName}(${paramString})`); } else { return expression; } } convertStringExpression (expression) { if (expression instanceof InfixApp) { const leftType = this.evaluateExpressionType(expression.left); const rightType = this.evaluateExpressionType(expression.right); if ( leftType.isCompatible(Types.STRING) && rightType.isCompatible(Types.STRING) ) { // BOTH strings.... // create a temp var with size equals to left + right + 1 const leftExp = this.convertStringExpression(expression.left); const rightExp = this.convertStringExpression(expression.right); this.currentCommands.push({ value: "As 3 linhas seguintes convertem a expressão " + expression.toString() }); const auxID = this.createAuxID(); this.createCharPointer(auxID, true, leftExp, rightExp); this.createCharCopy(auxID, leftExp); this.createCharConcat(auxID, rightExp); return new VariableLiteral(auxID); } else if (leftType.isCompatible(Types.STRING)) { // left is a string, right needs conversion const leftExp = this.convertStringExpression(expression.left); const rightExp = this.convertExpression(expression.right); const formatString = this.getFormatStringFromType(rightType); const auxConvert = this.createAuxID(); this.convertToString(auxConvert, formatString, rightExp); const auxID = this.createAuxID(); this.createCharPointer(auxID, true, leftExp, auxConvert); this.createCharCopy(auxID, leftExp); this.createCharConcat(auxID, auxConvert); return new VariableLiteral(auxID); } else { // right is a string, left needs conversion const leftExp = this.convertExpression(expression.left); const rightExp = this.convertStringExpression(expression.right); const formatString = this.getFormatStringFromType(leftType); const auxConvert = this.createAuxID(); this.convertToString(auxConvert, formatString, leftExp); const auxID = this.createAuxID(); this.createCharPointer(auxID, true, auxConvert, rightExp); this.createCharCopy(auxID, auxConvert); this.createCharConcat(auxID, rightExp); return new VariableLiteral(auxID); } } else if (expression instanceof StringLiteral) { return expression; } else if (expression instanceof FunctionCall) { let funcName = expression.id; const langFun = LanguageDefinedFunction.getFunction(funcName); if (langFun) { this.isValidFunction(funcName); const funcInfo = this.convertFunctionCall(funcName); this.cAST.includes.add(funcInfo.lib); funcName = funcInfo.name; } const paramList = []; for (let i = 0; i < expression.parametersSize; i += 1) { const param = expression.actualParameters[i]; const paramExp = this.convertExpression(param); paramList.push(paramExp.toString()); } const paramString = paramList.join(", "); return TextNode(`${funcName}(${paramString})`); } else { return expression; } } getFormatStringFromType (type) { switch (type.ord) { case Types.INTEGER.ord: case Types.BOOLEAN.ord: return "%d"; case Types.STRING.ord: return "%s"; default: return "%g"; } } convertToString (id, format, expression) { this.currentCommands.push({ value: "As 3 linhas seguintes convertem a expressão " + expression.toString() }); const lenID = this.createAuxID(); this.currentCommands.push( new TextNode( `int ${lenID} = snprintf(NULL, 0, ${format}, ${expression.toString()})` ) ); this.createCharPointer(id, true, lenID); this.currentCommands.push( new TextNode( `snprintf(${id}, ${lenID} + 1, ${format}, ${expression.toString()})` ) ); } createCharPointer (id, plusOne, ...args) { this.cAST.includes.add("stdlib.h"); const funArgs = []; for (let i = 0; i < args.length; i += 1) { funArgs.push(`sizeof(${args[i].toString()})`); } if (plusOne) { funArgs.push("1"); } const params = funArgs.join(" + "); const initial = new TextNode(`malloc(${params})`); const decl = new Declaration(id, new Pointer(Types.STRING), initial); this.currentCommands.push(decl); } createCharCopy (id, arg) { this.cAST.includes.add("string.h"); const cmd = new TextNode(`strcpy(${id}, ${arg.toString()})`); this.currentCommands.push(cmd); } createCharConcat (id, arg) { this.cAST.includes.add("string.h"); const cmd = new TextNode(`strcat(${id}, ${arg.toString()})`); this.currentCommands.push(cmd); } convertFunctionCall (name) { let id = name; if (name.indexOf(".") !== -1) { const names = name.split("."); id = names[1]; } switch (id) { case "$log": return { lib: "math.h", name: "log10" }; case "$sin": case "$cos": case "$tan": case "$sqrt": case "$pow": return { lib: "math.h", name: id.substring(1) }; case "$rand": return { lib: "stdlib.h", name: id.substring(1) }; case "$abs": return { lib: "stdlib.h", name: id.substring(1) }; } } isValidFunction (id) { for (const name in FORBIDDEN_FUNCS) { if (id.indexOf(name) !== -1) { throw new Error( `Não é possível converter um código que faz uso da função ${LocalizedStrings.translateInternalFunction( name )}.` ); } } } }