|
@@ -0,0 +1,420 @@
|
|
|
+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)) {
|
|
|
+
|
|
|
+ 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)
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ for (let i = 0; i < this.ivprogAST.functions.length; i += 1) {
|
|
|
+ const func = this.ivprogAST.functions[i];
|
|
|
+ if (func.isMain) {
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ convertExpression (expression) {
|
|
|
+ const type = this.evaluateExpressionType(expression);
|
|
|
+ if (type instanceof ArrayType) {
|
|
|
+
|
|
|
+ } else if (Types.STRING.isCompatible(type)) {
|
|
|
+ return this.convertStringExpression(expression);
|
|
|
+ } else if (Types.BOOLEAN.isCompatible(type)) {
|
|
|
+ return this.convertBooleanExpression(expression);
|
|
|
+ } else {
|
|
|
+
|
|
|
+ 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);
|
|
|
+
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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) {
|
|
|
+
|
|
|
+ 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)
|
|
|
+ ) {
|
|
|
+
|
|
|
+
|
|
|
+ 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)) {
|
|
|
+
|
|
|
+ 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 {
|
|
|
+
|
|
|
+ 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
|
|
|
+ )}.`
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|