/** * @author jakoboberbuchner * */ package Converters; import java.io.*; import java.util.ArrayList; import java.util.Arrays; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.time.OffsetDateTime; import java.time.format.DateTimeFormatter; public class C_Swift_Converter { public static String topLevelComments( String inName, String outName ) { String[] tokens = inName.split("[\\|/]"); inName = tokens[tokens.length - 1]; tokens = outName.split("[\\|/]"); outName = tokens[tokens.length - 1]; DateTimeFormatter datef = DateTimeFormatter.ofPattern( "uuuu-MM-dd HH:mm:ss O" ); return "///\n/// V2.1 - C to Swift Convertor Created by: Jakob A. Oberbuchner ©2024\n///\n" + "/// Converted Info:\n" + "/// \t" + inName + " --> " + outName + "\n/// \tDate: " + OffsetDateTime.now().format( datef ) + "\n///\n"; } /** * @param args */ public static void main(String[] args) { if (args.length == 2) { String inputFile = args[0]; //Create the file if it does not exist, or open it to overwrite. try { //Init the PrintWriter for the output file BufferedReader sourceFile = new BufferedReader( new FileReader(inputFile) ); String line = sourceFile.readLine(); ArrayList mainBlocks = new ArrayList(); ArrayList defines = new ArrayList(); mainBlocks.add(null); boolean hasBlockComment = false; while (line != null) { //Parsing Loop line.replace("typedef", ""); String[] codeComments = line.split("//"); //Split up comments if they exist // Check if the block comment starts on this line if (line.contains("/*")) { hasBlockComment = true; } if (!hasBlockComment && codeComments.length != 0) { //If there is any data on this line parseLine(codeComments[0], mainBlocks, defines); } //Check if the line has the end of the block comment if (line.contains("*/")) { hasBlockComment = false; } //TODO Comments pass through optional line = sourceFile.readLine(); //Read the Next line } sourceFile.close(); //Save changes } catch (IOException e) { System.out.println("Error: " + e.getMessage()); e.printStackTrace(); } } else { System.out.println("\nError: No Input and/or Output file specified!!\n"); } }//main(String[] args) public static String hexToDec(String code) { while(code.contains("0x")) { int inx = code.indexOf("0x"); int end = code.indexOf("\\s+", inx); Pattern p = Pattern.compile("0x[0-9]|[a-f]"); // insert your pattern here Matcher m = p.matcher(code.substring(inx)); if (m.find()) { end = m.start(); } else { end = -1; } // String strvalue = code.substring(code.indexOf("=") + 1).strip().replace(",", ""); long value = 0; // value = Long.decode(strvalue); end = 2; while( (end + inx < code.length()) && ( Character.isDigit(code.charAt(inx + end)) || code.charAt(inx + end) >= 'a' && code.charAt(inx + end) <= 'f' || code.charAt(inx + end) >= 'A' && code.charAt(inx + end) <= 'F') ) { end++; } if (end > 0) { // value = Long.parseLong(code.substring(inx+2, inx+end), 16); //Convert from hex value = Long.decode(code.substring(inx, inx+end)); code = code.substring(0, inx) + value + code.substring(inx+end); } else { // value = Long.parseLong(code.substring(inx+2), 16); //Convert from hex value = Long.decode(code.substring(inx)); code = code.substring(0, inx) + value; } } return code; } /***********************************************************************************************************/ public static void parseLine(String code, ArrayList mainItems, ArrayList defines) { if (code.contains("(") && !code.contains("#") ) { //It is a function definition and we ignore it return; } if (!code.contains("#define") && code.contains("#")) { //is a #if, #endif, #include, etc... return; } //Check for any hex values, change to decimal code = hexToDec(code); code = code.replace("typedef", "").replace("__attribute__((packed))", ""); code = code.strip(); //remove leading and trailing whitespace code = code.replace(";", ""); //.replace("\t", "") if needed but strip chould remove tabs String[] tokens = code.split("\\s+"); //splits at any whitespace for( int i = 0; i < tokens.length; i++) { tokens[i] = tokens[i].strip( ); //remove leading and trailing whitespace for each part } boolean mainComplete = false; switch (tokens.length) { case 1: //A single word or character mainComplete = case1(tokens, mainItems); break; case 2: //Variable or Type definition ie. "struct Hello", "uint32_t var;, struct {", "} hello; mainComplete = case2(tokens, mainItems, defines); break; case 3: //ie. "struct Hello {", or an enum case "hello = 1" mainComplete = case3(tokens, mainItems, defines); break; default: System.out.println("Error: parseLine Error, missing a case!!!!"); } if (mainComplete) { mainItems.add(null); //Add empty non-initialized Item } } /***********************************************************************************************************/ public static boolean case1(String[] tokens, ArrayList mainItems) { //The argument must have 1 arguments minimum extra will be ignored if (tokens.length < 1) { return false; } if (tokens[0].length() == 0) { //skips empty lines like '' return false; } CodeItem main = mainItems.get(mainItems.size()-1); //The reference to the main Item boolean isComplete = false; switch (tokens[0].charAt(0)) { case '{': //Doesn't do anything here break; case '}': //Ending the current section if ( main.setFinishedItem() ) { //tell the main Item that it or a sub item is complete, get if the main is completed isComplete = true; } break; //Cases for structures, enums, unions. case 's': case 'u': case 'e': Type type = getType(mainItems, tokens[0].strip()); String name = "notNamed"; if (main == null) { mainItems.set(mainItems.size()-1, new CodeItem(type, name)); //Override the null value } else { main.addSubItem(new CodeItem(type, name)); //add the item to the main item } break; default: System.out.println("Error: parseLine case 1: error"); } return isComplete; } /***********************************************************************************************************/ /** * Purpose: * @param tokens: An Array of Strings to be parsed * @param main: The Encompassing Item, null if there is not yet an item * @return Boolean representing if the main item is complete (aka found a '}') */ public static boolean case2(String[] tokens, ArrayList mainItems, ArrayList defines) { //The argument must have 1 arguments minimum extra will be ignored if (tokens.length < 2) { return false; } boolean isComplete = false; CodeItem main = mainItems.get(mainItems.size()-1); //The reference to the main Item if ( tokens[0].strip().equals("}") ) { main.getArrayValues(defines, tokens[1]); //Must be before main.setFinishedItem() int bracketInx = tokens[1].indexOf("["); //Try to get the brackets index if (bracketInx == -1) { //if there is no bracket bracketInx = tokens[1].length(); //Set to the max index excluded } main.setName(tokens[1].substring(0, bracketInx)); //Set the name of the Item if ( main.setFinishedItem() ) { //tell the main Item that it or a sub item is complete, get if the main is completed isComplete = true; //The main (encompassing) item is complete } } else if ( tokens[1].strip().equals("{") ) { //This is like struct {, enum {, uinion Type type = getType(mainItems, tokens[0].strip()); String name = "noName"; //Add the Item to the if (main == null) { mainItems.set(mainItems.size()-1, new CodeItem(type, name)); //Override the null value } else { main.addSubItem(new CodeItem(type, name)); //add the item to the main item } } else { //Internal Item not the main Type type = getType(mainItems, tokens[0].strip()); String name = "noName"; if (type == null) { //does not recognize the type isComplete = false; return isComplete; } CodeItem item = new CodeItem(type, name); //Initialize the Item and finally add it to the main later. item.getArrayValues(defines, tokens[1]); //Must be before main.setFinishedItem() int bracketInx = tokens[1].indexOf("["); //Try to get the brackets index if (bracketInx == -1) { //if there is no bracket bracketInx = tokens[1].length(); //Set to the max index excluded } name = tokens[1].substring(0, bracketInx); item.setName(name); //Set the name of the Item if (main == null) { mainItems.set(mainItems.size()-1, item); //Override the null value } else { main.addSubItem(item); //add the item to the main item } // System.out.println("TEST this Section: else: case2()"); isComplete = false; } return isComplete; } /***********************************************************************************************************/ public static boolean case3(String[] tokens, ArrayList mainItems, ArrayList defines) { //The argument must have 1 arguments minimum extra will be ignored if (tokens.length < 3) { return false; } CodeItem main = mainItems.get(mainItems.size()-1); //The reference to the main Item Type type = getType(mainItems, tokens[0].strip()); String name = tokens[1].strip(); if (type == null) { type = new Type(Types.EnumItem); name = tokens[0].strip(); long eVal = Long.parseLong(tokens[2].replace(",", "").strip()); main.addSubItem( new CodeItem(type, name, eVal) ); //Add the enum value to the Enum Code Item, false becasue enum case cannot contain sub items } else if (type.type() == Types.Definition) { Definition define = new Definition(name, findType(mainItems, tokens[2]), findValue(tokens[2]) ); defines.add(define); //add the definition to the array list return false; } else if (main != null) { System.out.println("\nERROR: Should never get here: In 'case3()' main is not null and is being initialized??\n"); // main = new CodeItem(type, name); //Set the main Item } else { //Must set the first main Item mainItems.set(mainItems.size()-1, new CodeItem(type, name)); //Override the null value } return false; //Always returns false } /***********************************************************************************************************/ public static Type getType(ArrayList customTypes, String word) { Type type = null; switch(word) { case "DateTime_t": //Custom Type System.out.println("INFO: Using a custom type 'DateTime_t' treated as a swift 'Date' Type."); type = new Type(Types.Date); break; case "#define": type = new Type(Types.Definition); break; case "double": type = new Type(Types.Double); break; case "float": type = new Type(Types.Float); break; case "int": type = new Type(Types.Int); break; case "int8_t": type = new Type(Types.Int8); break; case "int16_t": type = new Type(Types.Int16); break; case "int32_t": type = new Type(Types.Int32); break; case "int64_t": type = new Type(Types.Int64); break; case "uint": System.out.println("NO SUCH Type as 'uint' in C"); type = new Type(Types.UInt); break; case "uint8_t": type = new Type(Types.UInt8); break; case "uint16_t": type = new Type(Types.UInt16); break; case "uint32_t": type = new Type(Types.UInt32); break; case "uint64_t": type = new Type(Types.UInt64); break; case "struct": type = new Type(Types.Struct); break; case "enum": type = new Type(Types.Enum); break; case "union": type = new Type(Types.Union); break; case "char": //TODO Sort out what to do for multiple type names // case "const char*": // case "const char *": default: // System.out.println("getType() error: type not found OR ADDED!!!!"); // System.out.println("Info: 'getType()': Found Enum case or unknown type."); //Check for types defined previously for (int i = 0; i < customTypes.size(); i++) { if ( customTypes.get(i) != null && customTypes.get(i).getType() != null && customTypes.get(i).getName().matches(word) ) { switch(customTypes.get(i).getType().type()) { case Enum: type = new Type(word, word + "_E", Types.Enum); break; case Struct: type = new Type(word, word + "_C", Types.Struct); break; case Union: type = new Type(word, word + "_U", Types.Union); break; default: System.out.println("ERROR: Cannot have a Custom Primitive Type!!!"); } } } } return type; } /***********************************************************************************************************/ public static Type findType(ArrayList customTypes, String text) { //Only checks for the basic types String[] checkWith = {"uint8_t", "uint16_t", "uint32_t", "uint64_t", "int8_t", "int16_t", "int32_t", "int64_t", "int", "float", "double", "char"}; for(int i = 0; i < checkWith.length; i++) { if (text.contains(checkWith[i])) { return getType(customTypes, checkWith[i]); } } return null; } /***********************************************************************************************************/ public static String findValue(String text) { //Only checks for the basic types String[] checkWith = {"(uint8_t)", "(uint16_t)", "(uint32_t)", "(uint64_t)", "(int8_t)", "(int16_t)", "(int32_t)", "(int64_t)", "(int)", "(float)", "(double)", "(char)"}; for(int i = 0; i < checkWith.length; i++) { text = text.replace(checkWith[i], ""); } // text.replace("[a-zA-Z]", ""); return text; } } //////////////////////////////////////////////////////////////////////////////////////////////////// enum Types { Date, //NOT an actual C type but is a structure // Void, //Ignores all void items Definition, //C defines, -> let constant in swift Double, Float, Int, Int64, Int32, Int16, Int8, UInt, UInt64, UInt32, UInt16, UInt8, Char, /* String(""), */ Enum, EnumItem, Struct, //Swift Struct NOT USED!! Union, UserDefined // public final String cName; //Type name in C Lang // public final String sName; //Type name in Swift // public final Type superType;//Type of the UserDefined Type // // private Type(String cName, String sName) { // this.cName = cName; // this.sName = sName; // this.superType = null; // } // // private Type(String cName, String sName, Type superType) { // this.cName = cName; // this.sName = sName; // this.superType = superType; // } // //// public static Type custom(String cName, String sName, Type superType) { //// Type type = Type.UserDefined; //// type.cName = cName; //// type.sName = sName; //// type.superType = superType; //// return type; //// } // // public boolean isCustom( ) { // if(this == Type.UserDefined) { // return true; // } else { // return false; // } // } } class Type { private Types type; private String cName; private String sName; private Types superType; public Type(Types type, String cName, String sName) { this.cName = cName; this.sName = sName; this.superType = null; } public Type(String cName, String sName, Types superType) { this.type = Types.UserDefined; this.cName = cName; this.sName = sName; this.superType = superType; } public Types type( ) { return this.type; } public String getCName( ) { return this.cName; } public String getSName( ) { return this.sName; } public Types getSuperType( ) { return this.superType; } public boolean isCustom( ) { if(this.type == Types.UserDefined) { return true; } else { return false; } } public Type(Types type) { this.type = type; this.superType = null; switch(type) { case Char: this.cName = "char"; this.sName = "Character"; break; case Date: this.cName = "DateTime_t"; this.sName = "Date"; break; case Definition: this.cName = "#define"; this.sName = ""; break; case Double: this.cName = "double"; this.sName = "Double"; break; case Enum: this.cName = "enum"; this.sName = "Enum"; break; case EnumItem: this.cName = ""; this.sName = ""; break; case Float: this.cName = "float"; this.sName = "Float"; break; case Int: this.cName = "int"; this.sName = "Int"; break; case Int16: this.cName = "int16_t"; this.sName = "Int16"; break; case Int32: this.cName = "int32_t"; this.sName = "Int32"; break; case Int64: this.cName = "int64_t"; this.sName = "Int64"; break; case Int8: this.cName = "int8_t"; this.sName = "Int8"; break; case Struct: this.cName = "struct"; this.sName = ""; break; case UInt: this.cName = "unsigned int"; this.sName = "UInt"; break; case UInt16: this.cName = "uint16_t"; this.sName = "UInt16"; break; case UInt32: this.cName = "uint32_t"; this.sName = "UInt32"; break; case UInt64: this.cName = "uint64_t"; this.sName = "UInt64"; break; case UInt8: this.cName = "uint8_t"; this.sName = "UInt8"; break; case Union: this.cName = "uinion"; this.sName = ""; break; case UserDefined: this.cName = "unknown!!!!"; this.sName = "unknown!!!!"; break; default: break; } } // Date("DateTime_t", "Date"), //NOT an actual C type but is a structure //// Void, //Ignores all void items // Definition("#define", ""), //C defines, -> let constant in swift // Double("double", "Double"), // Float("float", "Float"), // // Int("int", "Int"), // Int64("int64_t", "Int64"), // Int32("int32_t", "Int32"), // Int16("int16_t", "Int16"), // Int8("int8_t", "Int8"), // // UInt("unsigned int", "UInt"), // UInt64("uint64_t", "UInt64"), // UInt32("uint32_t", "UInt32"), // UInt16("uint16_t", "UInt16"), // UInt8("uint8_t", "UInt8"), // // Char("char", "Character"), /* String(""), */ // // Enum("enum", "Enum"), // EnumItem("", ""), // // Struct("struct", "Struct"), //Swift Struct NOT USED!! // Union("uinion", ""), // // UserDefined("", ""); } class IntegerDefine { private Integer value; private Definition define; public IntegerDefine(int value) { this.value = value; } public IntegerDefine(Definition define) { this.define = define; } public String toString( ) { if (value != null) { return value + ""; } else { return define.getName(); } } } class CodeItem { private Type type; private String name; private long eValue; //Used for Enums only private ArrayList arrayData; private ArrayList subItems; //Stores Sub items inside //Internal ONLY private int addItemTo = -1; // -1 if do not add to any subItem, otherwise the index of the item in subItems Array List //General Constructor public CodeItem(Type type, String name) { this.type = type; this.name = name; this.eValue = -1; this.arrayData = new ArrayList(); this.subItems = new ArrayList(); } //Constructor used for enumerations public CodeItem(Type type, String name, long eValue) { this.type = type; this.name = name; this.eValue = eValue; this.arrayData = new ArrayList();; this.subItems = new ArrayList(); } //*********** Getters and Setters *************// public Type getType( ) { return this.type; } public String getName( ) { return this.name; } public void setName( String name ) { if (this.addItemTo < 0) { this.name = name; } else { this.subItems.get(addItemTo).setName(name); } } /** * Purpose: Used when checking for array dimensions * @param size: Size of the array dimension to add */ private void addArrayDimension( IntegerDefine size ) { this.arrayData.add(size); } /** * Purpose: Adds the Item to the most internal structure that is active * @param item: The CodeItem to be added * @param canHold: If the 'item' can hold other items inside it * @apiNote Is a recursive function! */ public void addSubItem( CodeItem item ) { if (this.addItemTo < 0) { this.subItems.add(item); if (item.type.type() == Types.Enum || item.type.type() == Types.Struct || item.type.type() == Types.Union ) { //tell if the item can have sub items this.addItemTo = subItems.size() - 1; //Set to the index of the new item } } else { //Passes it into the structure that 'item' is in this.subItems.get(addItemTo).addSubItem(item); } } /** * Purpose: Used when the end of a structure type thing is found at a '}' * @apiNote Is a recursive function! * @return boolean indicating if it is finished filling the sub Items */ //TODO: Check make sure working correctly public boolean setFinishedItem( ) { if (this.addItemTo < 0) { return true; } else { if (this.subItems.get(addItemTo).setFinishedItem() ) { //Will countinue until the most internal structure is set complete this.addItemTo = -1; } return false; } } public boolean hasSubItems( ) { return this.subItems != null; } //********************* Methods ***********************// public void getArrayValues(ArrayList defines, String word ) { if (this.addItemTo < 0) { word = word.replace("]","").replace(";", ""); String[] tokens = word.split("\\["); if (tokens.length > 1) { for(int i = 1; i < tokens.length; i++) { //skip the name of the variable try { this.addArrayDimension( new IntegerDefine(Integer.parseInt(tokens[i])) ); } catch (NumberFormatException e) { boolean isDefined = false; for(int j = 0; j < defines.size() && !isDefined; j++) { if (defines.get(j).getName().equals(tokens[i])) { this.addArrayDimension( new IntegerDefine(defines.get(j)) ); isDefined = true; } } if (!isDefined) { System.out.println("ERROR in 'getArrayValues()': "); System.out.println( "\t" + e.getMessage()); } } //try catch } }// if } else { this.subItems.get(addItemTo).getArrayValues(defines, word); } } /************************************************************************************************************/ private String arrayStr(String typeStr, String initStr) { String strOut = ""; for(int i = 0; i < arrayData.size(); i++) { strOut += "["; } strOut += typeStr; for(int i = 0; i < arrayData.size(); i++) { strOut += "]"; } /** Following part initializes the array */ if (arrayData.size() > 0) { strOut += " = " + strOut + "()"; } // int embededCount = arrayData.size - 1; // strOut += " = "; // for(int i = 0; i < arrayData.size(); i++) { // strOut += "Array<" + nChars(embededCount, "[") + typeStr + nChars(embededCount, "]") + ">(repeating: "; // embededCount--; // } // strOut += initStr; // for(int i = arrayData.size - 1; i >= 0; i--) { // strOut += ", count: " + arrayData[i] + ")"; // } return strOut; } /************************************************************************************************************/ private String nChars(int n, String str) { String outStr = ""; for(int i = 0; i < n; i++) { //Bad for large "n" values outStr += str; } return outStr; } } class Definition extends CodeItem { private Type actualType; private String value; public Definition(String name, Type type, String value) { super(new Type(Types.Definition), name); this.actualType = type; this.value = value; } /** * Purpose: * @param tabs * @param isInside * @param valueOffset * @return String Array index 0 If null then not used, otherwise used for Structure and Enum definition before main struct */ public String[] getSwift( String tabs, boolean isInside, int valueOffset ) { //if isInside is true then it will also output the variable definition String out = tabs + "let " + super.getName(); if (this.actualType != null) { //check if there is an actual type for it out += ": "; switch(this.actualType.type()) { case Char: out += "Character"; break; case Double: out += "Double"; break; case Float: out += "Float"; break; case Int: out += "Int"; break; case Int16: out += "Int16"; break; case Int32: out += "Int32"; break; case Int64: out += "Int64"; break; case Int8: out += "Int8"; break; case UInt: out += "UInt"; break; case UInt16: out += "UInt16"; break; case UInt32: out += "UInt32"; break; case UInt64: out += "UInt64"; break; case UInt8: out += "UInt8"; break; case Enum: // case String: case Struct: case Union: case Date: case EnumItem: case Definition: default: out = "#define ERROR! Found Type is NOT supported"; break; } } out += " = " + this.value; String[] output = {out, ""}; return output; } }