1
0

refactor: finish loading in ExpFctsRender

This commit is contained in:
2026-01-28 13:50:59 +08:00
parent f5645a06de
commit 69ac25a70b
6 changed files with 278 additions and 190 deletions

View File

@@ -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.
* <p>
* For end user, it is enough that knowing the last item is type itself.
*/
private Vector<String> 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<String>();
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<String> base_type, int pointer_level) {
mBaseType = (Vector<String>) 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.
* <p>
* 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<String> getBaseTypeHierarchy() {
return (Vector<String>) 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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<ExpFctParam>();
}

View File

@@ -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;
}
}

View File

@@ -9,25 +9,12 @@ 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) {

View File

@@ -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))

View File

@@ -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__":

View File

@@ -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"