diff --git a/Assets/CodeGen/BMapBinder/ExpFctsAnalyzer/ExpFctsHelper.java b/Assets/CodeGen/BMapBinder/ExpFctsAnalyzer/ExpFctsHelper.java index 40b7c7c..bcbc787 100644 --- a/Assets/CodeGen/BMapBinder/ExpFctsAnalyzer/ExpFctsHelper.java +++ b/Assets/CodeGen/BMapBinder/ExpFctsAnalyzer/ExpFctsHelper.java @@ -1,184 +1,30 @@ -import java.util.Collections; import java.util.Vector; -import java.util.stream.Collectors; public class ExpFctsHelper { /** - * The class represent the type of each parameters and function return value. - */ - public static class VariableType { - /** - * The base type of this variable removing all ending stars (remove all pointer levels). - * Each item in this Vector is a part of namespace and the last one must be the type itself - * (without any namespace constraint). - * If no namespace constraint for this type, this Vector will only have one item. - *

- * For end user, it is enough that knowing the last item is type itself. - */ - private Vector mBaseType; - /** - * The pointer level of this type. It is equal to the count of trailing star of - * this field in C style representation. - */ - private int mPointerLevel; - - /** - * Construct an empty varible type. This is commonly used constructor. - */ - public VariableType() { - mBaseType = new Vector(); - mPointerLevel = 0; - } - - /** - * The constructor used for cloning self. This constructor is only can be used - * by self. - * - * @param base_type The hierarchy of the variable type. - * @param pointer_level The pointer level of new created variable type. - */ - private VariableType(Vector base_type, int pointer_level) { - mBaseType = (Vector) base_type.clone(); - mPointerLevel = pointer_level; - } - - /** - * Set this variable type with a type string in C/C++ style. For example - * "NSTest::NSTest2::MyType**". - * - * @param ctype The type string in C/C++ style. - */ - public void fromCType(String ctype) { - if (ctype.isEmpty()) - throw new IllegalArgumentException("empty string can not be parsed."); - - // get pointer part and name part - int len = ctype.length(); - int star_pos = ctype.indexOf('*'); - String namepart; - if (star_pos == -1) { - // no star - namepart = ctype; - mPointerLevel = 0; - } else { - // has star - if (star_pos == 0) - throw new IllegalArgumentException("base type not found."); - namepart = ctype.substring(0, star_pos); - mPointerLevel = len - star_pos; - } - - // resolve name part - mBaseType.clear(); - for (String item : namepart.split("::")) { - mBaseType.add(item); - } - } - - /** - * Build a type string represented by this variable type in C/C++ style. - * - * @return The type string in C/C++ style. - */ - public String toCType() { - return mBaseType.stream().collect(Collectors.joining("::")) - + String.join("", Collections.nCopies(mPointerLevel, "*")); - } - - /** - * Get the base type of this variable type without any namespace. It just simply - * get the last entry in type hierarchy. - * - * @return The base type string without namespace prefix. - */ - public String getBaseType() { - return mBaseType.lastElement(); - } - - /** - * Check whether this variable type is a pointer. This function just check - * whether the pointer level of this variavle type is zero. - * - * @return True if it is pointer, otherwise false. - */ - public boolean isPointer() { - return mPointerLevel != 0; - } - - /** - * Return the pointer level of this variable type. You can simply assume the - * pointer level is equal to the count of trailing star. - * - * @return The pointer level integer. Zero means that this type is not a - * pointer. - */ - public int getPointerLevel() { - return mPointerLevel; - } - - /** - * Return the clone of the type hierarchy of this variable type. - *

- * It is rarely used. This only should be used when you need the namespace - * hierarchy of this variable type. - * - * @return The clone of current variable type hierarchy. - */ - public Vector getBaseTypeHierarchy() { - return (Vector) mBaseType.clone(); - } - - /** - * Check whether this type is a valid one. It actually check whether type - * hierarchy include at least one entry. - * - * @return True if no problem of this type, otherwise false. - */ - public boolean isValid() { - return mBaseType.size() != 0; - } - - /** - * Return a new created variable type which is the pointer of this variable - * type. - *

- * In internal implementation, it just create a clone of current variable type - * with the increase of pointer level by 1. - * - * @return The new created pointer type of this variable type. - */ - public VariableType getPointerOfThis() { - return new VariableType(mBaseType, mPointerLevel + 1); - } - - } - - - /** - * The class represent a single parameter (argument) of function. This class - * usually is the member of {@linkplain ExpFct}. + * The class represent a single parameter (argument) of function. */ public static class ExpFctParam { /** * The type of this parameter. */ - public VariableType mVarType; + public String mVarType; /** * The name of this parameter. */ public String mVarName; /** - * True if this paramter is marked as input parameter, otherwise false. + * True if this parameter is marked as input parameter, otherwise false. *

- * Input parameter and output paramter is commonly used in C/C++ code. By using + * Input parameter and output parameter is commonly used in C/C++ code. By using * this feature, each function can receive multiple arguments and return * multiple arguments without defining a struct to hold it. *

* The type of input parameter is itself. However, the type of output parameter * is the pointer of itself. So you may need get its pointer type when - * processing output paramter, especially for the scenario that the target + * processing output parameter, especially for the scenario that the target * language do not support explicit output parameter keyword. */ public boolean mIsInput; @@ -186,7 +32,7 @@ public class ExpFctsHelper { * The description of this parameter. *

* This description is generated by this program. It will indicate the - * underlying C++ type to tell end user how to treat this paramter because some + * underlying C++ type to tell end user how to treat this parameter because some * target languages' native calling style can not represent these detail. *

* In this program, this field must be written as a annotation of corresponding @@ -195,7 +41,7 @@ public class ExpFctsHelper { public String mVarDesc; public ExpFctParam() { - mVarType = new VariableType(); + mVarType = ""; mVarName = ""; mVarDesc = ""; mIsInput = true; @@ -215,9 +61,9 @@ public class ExpFctsHelper { /** * The return value type of this function. */ - public VariableType mFctRetType; + public String mFctRvType; /** - * The parameters (arguments) list of this function. Each items are + * The parameters (arguments) list of this function. Each item are * {@linkplain ExpFctParam} and represent parameter one by one from left to * right. */ @@ -225,7 +71,7 @@ public class ExpFctsHelper { public ExpFct() { mFctName = ""; - mFctRetType = new VariableType(); + mFctRvType = ""; mFctParams = new Vector(); } diff --git a/Assets/CodeGen/BMapBinder/ExpFctsAnalyzer/ExpFctsWalker.java b/Assets/CodeGen/BMapBinder/ExpFctsAnalyzer/ExpFctsWalker.java index bbfc09d..48d5c6f 100644 --- a/Assets/CodeGen/BMapBinder/ExpFctsAnalyzer/ExpFctsWalker.java +++ b/Assets/CodeGen/BMapBinder/ExpFctsAnalyzer/ExpFctsWalker.java @@ -1,4 +1,5 @@ import java.util.Collections; +import java.util.Objects; import java.util.stream.Collectors; public class ExpFctsWalker extends ExpFctsParserBaseListener { @@ -32,8 +33,7 @@ public class ExpFctsWalker extends ExpFctsParserBaseListener { // set name mCurrentFct.mFctName = ctx.EXPFCTS_IDENTIFIER().getText(); // check return type - if (!mCurrentFct.mFctRetType.isValid() || mCurrentFct.mFctRetType.isPointer() - || !mCurrentFct.mFctRetType.getBaseType().equals("bool")) + if (!Objects.equals(mCurrentFct.mFctRvType, "bool")) throw new IllegalArgumentException("invalid interface function return type. must be bool."); // add into list @@ -47,7 +47,7 @@ public class ExpFctsWalker extends ExpFctsParserBaseListener { param.mVarName = ctx.EXPFCTS_IDENTIFIER().getText(); param.mVarDesc = "The pointer to corresponding BMFile."; param.mIsInput = true; - param.mVarType.fromCType("BMap::BMFile*"); + param.mVarType = "BMap::BMFile*"; mCurrentFct.mFctParams.add(param); } @@ -57,7 +57,7 @@ public class ExpFctsWalker extends ExpFctsParserBaseListener { param.mVarName = ctx.EXPFCTS_IDENTIFIER().getText(); param.mVarDesc = "The pointer to corresponding BMMeshTransition."; param.mIsInput = true; - param.mVarType.fromCType("BMap::BMMeshTransition*"); + param.mVarType = "BMap::BMMeshTransition*"; mCurrentFct.mFctParams.add(param); } @@ -67,14 +67,14 @@ public class ExpFctsWalker extends ExpFctsParserBaseListener { firstParam.mVarName = ctx.EXPFCTS_IDENTIFIER(0).getText(); firstParam.mVarDesc = "The pointer to corresponding BMFile."; firstParam.mIsInput = true; - firstParam.mVarType.fromCType("BMap::BMFile*"); + firstParam.mVarType = "BMap::BMFile*"; mCurrentFct.mFctParams.add(firstParam); ExpFctsHelper.ExpFctParam secondParam = new ExpFctsHelper.ExpFctParam(); secondParam.mVarName = ctx.EXPFCTS_IDENTIFIER(1).getText(); secondParam.mVarDesc = "The CKID of object you accessing."; secondParam.mIsInput = true; - secondParam.mVarType.fromCType("LibCmo::CK2::CK_ID"); + secondParam.mVarType = "LibCmo::CK2::CK_ID"; mCurrentFct.mFctParams.add(secondParam); } @@ -118,12 +118,15 @@ public class ExpFctsWalker extends ExpFctsParserBaseListener { ctype += String.join("", Collections.nCopies(ctx.EXPFCTS_STAR().size(), "*")); } - if (!mCurrentFct.mFctRetType.isValid()) { + // if there is function return value is not filled, + // we fill it first because return value type is the first captured type in function statement. + // otherwise we fill parameter type. + if (mCurrentFct.mFctRvType.isEmpty()) { // fill function ret type first - mCurrentFct.mFctRetType.fromCType(ctype); + mCurrentFct.mFctRvType = ctype; } else { // otherwise, fill param data - mCurrentParam.mVarType.fromCType(ctype); + mCurrentParam.mVarType = ctype; } } diff --git a/Assets/CodeGen/BMapBinder/ExpFctsAnalyzer/JsonWriter.java b/Assets/CodeGen/BMapBinder/ExpFctsAnalyzer/JsonWriter.java index d5a87b7..578761a 100644 --- a/Assets/CodeGen/BMapBinder/ExpFctsAnalyzer/JsonWriter.java +++ b/Assets/CodeGen/BMapBinder/ExpFctsAnalyzer/JsonWriter.java @@ -8,26 +8,13 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; public class JsonWriter { - - private static JsonObject writeVariableType(ExpFctsHelper.VariableType vt) { - JsonObject data = new JsonObject(); - - JsonArray hierarchy = new JsonArray(); - for (String item : vt.getBaseTypeHierarchy()) { - hierarchy.add(item); - } - data.add("hierarchy", hierarchy); - data.addProperty("pointer_level", vt.getPointerLevel()); - - return data; - } - + private static JsonObject writeExpFctParam(ExpFctsHelper.ExpFctParam param) { JsonObject data = new JsonObject(); + data.addProperty("type", param.mVarType); data.addProperty("name", param.mVarName); data.addProperty("is_input", param.mIsInput); data.addProperty("desc", param.mVarDesc); - data.add("type", writeVariableType(param.mVarType)); return data; } @@ -35,7 +22,7 @@ public class JsonWriter { private static JsonObject writeExpFct(ExpFctsHelper.ExpFct fct) { JsonObject data = new JsonObject(); data.addProperty("name", fct.mFctName); - data.add("return", writeVariableType(fct.mFctRetType)); + data.addProperty("return", fct.mFctRvType); JsonArray paramList = new JsonArray(); for (ExpFctsHelper.ExpFctParam param : fct.mFctParams) { diff --git a/Assets/CodeGen/BMapBinder/ExpFctsRender/json_loader.py b/Assets/CodeGen/BMapBinder/ExpFctsRender/json_loader.py new file mode 100644 index 0000000..2b318b9 --- /dev/null +++ b/Assets/CodeGen/BMapBinder/ExpFctsRender/json_loader.py @@ -0,0 +1,225 @@ +import json +import typing +import utils +from dataclasses import dataclass + + +class VariableType: + """The class represent the type of each parameters and function return value.""" + + __base_type_hierarchy: list[str] + """ + The base type of this variable removing all ending stars (remove all pointer levels). + Each item in this list is a part of namespace and the last one must be the type itself + (without any namespace constraint). + If no namespace constraint for this type, this list will only have one item. + + For end user, it is enough that knowing the last item is type itself. + """ + __pointer_level: int + """ + The pointer level of this type. + It is equal to the count of trailing star of this field in C style representation. + """ + + def __init__( + self, base_type_hierarchy: list[str] = [], pointer_level: int = 0 + ) -> None: + """Construct a new varible type.""" + self.__base_type_hierarchy = base_type_hierarchy + self.__pointer_level = pointer_level + + def clone(self) -> "VariableType": + """CLone self into a new instance.""" + return VariableType(list(self.__base_type_hierarchy), self.__pointer_level) + + @staticmethod + def from_c_type(ctype: str) -> "VariableType": + """ + Set this variable type with a type string in C/C++ style. + + For example, "NSTest::NSTest2::MyType**" will produce 2 pointer level + with ('NSTest', 'NSTest2', 'MyType') as its base type hierarchy. + + :param ctype: The type string in C/C++ style. + :return: The parsed VariableType instance. + """ + if len(ctype) == 0: + raise RuntimeError("empty string can not be parsed") + + # get pointer part and name part + length = len(ctype) + star_index = ctype.find("*") + name_part: str + pointer_level: int + if star_index == -1: + # No star, no pointer level + name_part = ctype + pointer_level = 0 + else: + # Has star + if star_index == 0: + raise RuntimeError("base type not found") + name_part = ctype[0:star_index] + pointer_level = length - star_index + + # resolve name part + base_type_hierarchy = list(name_part.split("::")) + + # return value + return VariableType(base_type_hierarchy, pointer_level) + + def to_c_type(self) -> str: + """ + Build a type string represented by this variable type in C/C++ style. + + :return: The type string in C/C++ style. + """ + return "::".join(self.__base_type_hierarchy) + ("*" * self.__pointer_level) + + def get_base_type(self) -> str: + """ + Get the base type of this variable type without any namespace. + + It just simply get the last entry in type hierarchy. + + :return: The base type string without namespace prefix. + """ + return self.__base_type_hierarchy[-1] + + def is_pointer(self) -> bool: + """ + Check whether this variable type is a pointer. + This function just check whether the pointer level of this variavle type is zero. + + :return: True if it is pointer, otherwise false. + """ + return self.__pointer_level != 0 + + def get_pointer_level(self) -> int: + """ + Return the pointer level of this variable type. + + You can simply assume the pointer level is equal to the count of trailing star. + + :return: The pointer level integer. Zero means that this type is not a pointer. + """ + return self.__pointer_level + + def iter_base_type_hierarchy(self) -> typing.Iterator[str]: + """ + Return the clone of the type hierarchy of this variable type. + + It is rarely used. + This only should be used when you need the namespace hierarchy of this variable type. + + :return: The clone of current variable type hierarchy. + """ + return iter(self.__base_type_hierarchy) + + # def is_valid(self) -> bool: + # """ + # Check whether this type is a valid one. + + # It actually check whether type hierarchy include at least one entry. + + # :return: True if no problem of this type, otherwise false. + # """ + # return len(self.__base_type_hierarchy) != 0 + + def get_pointer_of_this(self) -> "VariableType": + """ + Return a new created variable type which is the pointer of this variable type. + + In internal implementation, it just create a clone of current variable type + with the increase of pointer level by 1. + + :return: The new created pointer type of this variable type. + """ + return VariableType(list(self.__base_type_hierarchy), self.__pointer_level + 1) + + @staticmethod + def from_json(data: str) -> "VariableType": + return VariableType.from_c_type(data) + + +@dataclass(frozen=True) +class ExpFctParam: + """The class represent a single parameter (argument) of function.""" + + var_type: VariableType + """The type of this parameter.""" + var_name: str + """The name of this parameter.""" + is_input: bool + """ + True if this parameter is marked as input parameter, otherwise false. + + Input parameter and output parameter is commonly used in C/C++ code. + By using this feature, each function can receive multiple arguments + and return multiple arguments without defining a struct to hold it. + + The type of input parameter is itself. + However, the type of output parameter is the pointer of itself. + So you may need get its pointer type when processing output parameter, + especially for the scenario that the target language do not support explicit output parameter keyword. + """ + var_desc: str + """ + The description of this parameter. + + This description is generated by this program. + It will indicate the underlying C++ type to tell end user how to treat this parameter + because some target languages' native calling style can not represent these detail. + + In this program, this field must be written as a docstring of corresponding function. + """ + + @staticmethod + def from_json(data: dict[str, typing.Any]) -> "ExpFctParam": + return ExpFctParam( + VariableType.from_c_type(data["type"]), + data["name"], + data["is_input"], + data["desc"], + ) + + +@dataclass(frozen=True) +class ExpFct: + """The class represent an export BMap function.""" + + fct_name: str + """The name of this function.""" + fct_rv_type: VariableType + """The return value type of this function.""" + fct_params: list[ExpFctParam] + """ + The parameters (arguments) list of this function. + Each item represent parameter accepted by this function one by one from left to right. + """ + + @staticmethod + def from_json(data: dict[str, typing.Any]) -> "ExpFct": + return ExpFct( + data["name"], + VariableType.from_json(data["return"]), + list(map(lambda i: ExpFctParam.from_json(i), data["params"])), + ) + + +@dataclass(frozen=True) +class ExpFctCollection: + """The class represent a collection of export BMap functions.""" + + fcts: list[ExpFct] + """The collection of exported BMap functions.""" + + @staticmethod + def from_json(data: list[typing.Any]) -> "ExpFctCollection": + return ExpFctCollection(list(map(lambda i: ExpFct.from_json(i), data))) + + +def load_fcts(filename: str) -> ExpFctCollection: + with open(utils.get_input_file_path(filename), "r", encoding="utf-8") as f: + return ExpFctCollection.from_json(json.load(f)) diff --git a/Assets/CodeGen/BMapBinder/ExpFctsRender/main.py b/Assets/CodeGen/BMapBinder/ExpFctsRender/main.py index 4cd3820..a3c4580 100644 --- a/Assets/CodeGen/BMapBinder/ExpFctsRender/main.py +++ b/Assets/CodeGen/BMapBinder/ExpFctsRender/main.py @@ -1,5 +1,10 @@ +import json_loader +import utils + def main(): - print("Hello from exp-fcts-render!") + fcts = json_loader.load_fcts("BMExports.json") + + print("Done") if __name__ == "__main__": diff --git a/Assets/CodeGen/BMapBinder/ExpFctsRender/utils.py b/Assets/CodeGen/BMapBinder/ExpFctsRender/utils.py new file mode 100644 index 0000000..d1e1ba8 --- /dev/null +++ b/Assets/CodeGen/BMapBinder/ExpFctsRender/utils.py @@ -0,0 +1,22 @@ +import os +from pathlib import Path + + +def _get_root_directory() -> Path: + bmap_binder_root = os.environ.get("BMAP_BINDER_ROOT", None) + if bmap_binder_root is None: + return Path(__file__).resolve().parent.parent + else: + return Path(bmap_binder_root).resolve() + + +def get_input_file_path(filename: str) -> Path: + return _get_root_directory() / "Analyzed" / filename + + +def get_output_file_path(filename: str) -> Path: + return _get_root_directory() / "Output" / filename + + +def get_template_directory() -> Path: + return Path(__file__).resolve().parent / "templates"