Files
Autopilot-Data-ParserOLD/src/Converters/C_Swift_Converter.java
2025-08-16 12:59:01 -05:00

1084 lines
25 KiB
Java

/**
* @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<CodeItem> mainBlocks = new ArrayList<CodeItem>();
ArrayList<Definition> defines = new ArrayList<Definition>();
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<CodeItem> mainItems, ArrayList<Definition> 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<CodeItem> 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<CodeItem> mainItems, ArrayList<Definition> 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<CodeItem> mainItems, ArrayList<Definition> 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<CodeItem> 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<CodeItem> 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<IntegerDefine> arrayData;
private ArrayList<CodeItem> 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<IntegerDefine>();
this.subItems = new ArrayList<CodeItem>();
}
//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<IntegerDefine>();;
this.subItems = new ArrayList<CodeItem>();
}
//*********** 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<Definition> 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;
}
}