Skip to content

Commit

Permalink
[sourcegen] Implement reserved CLib methods
Browse files Browse the repository at this point in the history
  • Loading branch information
ischoegl committed Jan 1, 2025
1 parent 7350407 commit 76caedf
Show file tree
Hide file tree
Showing 10 changed files with 189 additions and 55 deletions.
12 changes: 6 additions & 6 deletions interfaces/sourcegen/sourcegen/_data/ct_auto.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,21 @@ cabinet:
prefix: ct3
base: ""
functions:
# - name: getCanteraError(int buflen, char* buf)
- name: getCanteraVersion # todo: implement C++ version
- name: getGitCommit
implements: gitCommit
- name: getCanteraError
# - name: setLogWriter(void* logger)
# - name: setLogCallback(LogCallback writer);
- name: addCanteraDirectory
implements: addDirectory
- name: getDataDirectories
- name: findInputFile
# - name: getCanteraVersion
- name: getGitCommit
implements: gitCommit
- name: suppress_deprecation_warnings
- name: make_deprecation_warnings_fatal
- name: suppress_warnings
- name: warnings_suppressed
- name: make_warnings_fatal
- name: suppress_thermo_warnings
# - name: clearStorage();
# - name: resetStorage();
- name: clearStorage
- name: resetStorage
1 change: 1 addition & 0 deletions interfaces/sourcegen/sourcegen/_data/ctfunc_auto.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,4 @@ cabinet:
- name: write
- name: del
what: destructor
- name: cabinetSize
2 changes: 2 additions & 0 deletions interfaces/sourcegen/sourcegen/_data/ctkin_auto.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ cabinet:
- name: nReactions
- name: del
what: noop
- name: cabinetSize
- name: parentHandle
1 change: 1 addition & 0 deletions interfaces/sourcegen/sourcegen/_data/ctsoln_auto.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ cabinet:
- name: nAdjacent
- name: adjacent
implements: Solution::adjacent(size_t)
- name: cabinetSize
2 changes: 2 additions & 0 deletions interfaces/sourcegen/sourcegen/_data/ctthermo_auto.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ cabinet:
uses: nSpecies
- name: del
what: noop
- name: cabinetSize
- name: parentHandle
2 changes: 2 additions & 0 deletions interfaces/sourcegen/sourcegen/_data/cttrans_auto.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ cabinet:
- name: thermalConductivity
- name: del
what: noop
- name: cabinetSize
- name: parentHandle
36 changes: 33 additions & 3 deletions interfaces/sourcegen/sourcegen/_dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class Param:
default: Any = None #: Default value (optional)

@classmethod
def from_str(cls, param: str) -> 'Param':
def from_str(cls, param: str, doc: str="") -> 'Param':
"""Generate Param from parameter string."""
param = param.strip()
default = None
Expand All @@ -32,7 +32,14 @@ def from_str(cls, param: str) -> 'Param':
param = param[:param.rfind("=")]
parts = param.strip().rsplit(" ", 1)
if len(parts) == 2 and parts[0] not in ["const", "virtual", "static"]:
return cls(*parts, "", "", default)
if "@param" not in doc:
return cls(*parts, "", "", default)
items = doc.split()
if items[1] != parts[1]:
msg = f"Documented variable {items[1]!r} does not match {parts[1]!r}"
raise ValueError(msg)
direction = items[0].split("[")[1].split("]")[0] if "[" in items[0] else ""
return cls(*parts, " ".join(items[2:]), direction, default)
return cls(param)

@classmethod
Expand Down Expand Up @@ -122,7 +129,7 @@ class Func:
@classmethod
def from_str(cls, func: str) -> 'Func':
"""Generate Func from declaration string of a function."""
func = func.strip()
func = func.rstrip(";").strip()
# match all characters before an opening parenthesis '(' or end of line
name = re.findall(r'.*?(?=\(|$)', func)[0]
arglist = ArgList.from_str(func.replace(name, "").strip())
Expand All @@ -147,6 +154,29 @@ class CFunc(Func):
base: str = "" #: Qualified scope of function/method (optional)
uses: list['CFunc'] = None #: List of auxiliary C++ methods (optional)

@classmethod
def from_str(cls, func: str) -> 'CFunc':
"""Generate CFunc from header block of a function."""
lines = func.split("\n")
func = super().from_str(lines[-1])
if len(lines) == 1:
return func
brief = ""
returns = ""
args = []
for ix, line in enumerate(lines[:-1]):
line = line.strip().lstrip("*").strip()
if ix == 1:
brief = line
elif line.startswith("@param"):
# assume that variables are documented in order
arg = func.arglist[len(args)].long_str()
args.append(Param.from_str(arg, line))
elif line.startswith("@returns"):
returns = line.lstrip("@returns").strip()
args = ArgList(args)
return cls(func.ret_type, func.name, args, brief, None, returns, "", [])

def short_declaration(self) -> str:
"""Return a short string representation."""
ret = (f"{self.name}{self.arglist.short_str()}").strip()
Expand Down
6 changes: 4 additions & 2 deletions interfaces/sourcegen/sourcegen/_orchestrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,12 @@ def generate_source(lang: str, out_dir: str=None, verbose=False):
else:
# generate CLib headers from YAML specifications
files = HeaderFileParser.headers_from_yaml(ignore_files, ignore_funcs)
clib_config = read_config(Path(__file__).parent / "clib" / "config.yaml")
clib_root = Path(__file__).parent / "clib"
clib_config = read_config(clib_root / "config.yaml")
clib_templates = read_config(clib_root / "templates.yaml")
for key in ["ignore_files", "ignore_funcs"]:
clib_config.pop(key)
clib_scaffolder = CLibSourceGenerator(None, clib_config, {})
clib_scaffolder = CLibSourceGenerator(None, clib_config, clib_templates)
clib_scaffolder.resolve_tags(files)

# find and instantiate the language-specific SourceGenerator
Expand Down
36 changes: 30 additions & 6 deletions interfaces/sourcegen/sourcegen/clib/_CLibSourceGenerator.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,28 +163,33 @@ def shared_object(cxx_type):
return cxx_type.split("<")[-1].split(">")[0]

c_args = c_func.arglist
cxx_func = (c_func.implements or
CFunc("void", "dummy", ArgList([]), "", None, "", "base"))
cxx_func = c_func.implements
if not cxx_func:
if len(c_args) and "char*" in c_args[-1].p_type:
cxx_func = CFunc("string", "dummy", ArgList([]), "", None, "", "base")
else:
cxx_func = CFunc("void", "dummy", ArgList([]), "", None, "", "base")
cxx_ix = 0
check_array = False
for c_ix, c_par in enumerate(c_func.arglist):
c_name = c_par.name
if cxx_ix >= len(cxx_func.arglist):
if c_ix == 0 and cxx_func.base:
if c_ix == 0 and cxx_func.base and "len" not in c_name.lower():
handle = c_name
c_ix += 1
if c_ix == len(c_args):
break
cxx_type = cxx_func.ret_type

# Handle output buffer
if "string" in cxx_type:
if "string" in cxx_type: # or cxx_func.name == "dummy":
buffer = ["auto out",
f"copyString(out, {c_args[c_ix+1].name}, {c_name});",
"int(out.size())"]
else:
_logger.critical(f"Scaffolding failed for {c_func.name!r}: reverse "
f"crosswalk not implemented for {cxx_type!r}.")
f"crosswalk not implemented for {cxx_type!r}:\n"
f"{c_func.declaration()}")
exit(1)
break

Expand Down Expand Up @@ -296,6 +301,11 @@ def _scaffold_body(self, c_func: CFunc, recipe: Recipe) -> tuple[str, set[str]]:
else:
template = loader.from_string(self._templates["clib-method"])

elif recipe.what == "reserved":
args["cabinets"] = list(self._config.includes.keys())
template = loader.from_string(
self._templates[f"clib-reserved-{recipe.name}-cpp"])

else:
_logger.critical(f"{recipe.what!r} not implemented: {c_func.name!r}.")
exit(1)
Expand Down Expand Up @@ -325,14 +335,27 @@ def merge_params(implements, cxx_func: CFunc) -> tuple[list[Param], int]:

return obj_handle + cxx_func.arglist.params, cxx_func

func_name = f"{recipe.prefix}_{recipe.name}"
reserved = ["cabinetSize", "parentHandle",
"getCanteraVersion", "getCanteraError",
"clearStorage", "resetStorage"]
if recipe.name in reserved:
recipe.what = "reserved"
loader = Environment(loader=BaseLoader)
if not quiet:
_logger.debug(f" generating {func_name!r} -> {recipe.what}")
header = loader.from_string(
self._templates[f"clib-reserved-{recipe.name}-h"]
).render(base=recipe.base, prefix=recipe.prefix)
return CFunc.from_str(header)

# Ensure that all functions/methods referenced in recipe are detected correctly
bases = [recipe.base] + recipe.parents + recipe.derived
if not recipe.implements:
recipe.implements = self._doxygen_tags.detect(recipe.name, bases)
recipe.uses = [self._doxygen_tags.detect(uu.split("(")[0], bases, False)
for uu in recipe.uses]

func_name = f"{recipe.prefix}_{recipe.name}"
cxx_func = None
ret_param = Param("void")
args = []
Expand Down Expand Up @@ -373,6 +396,7 @@ def merge_params(implements, cxx_func: CFunc) -> tuple[list[Param], int]:
recipe.what = "method"
else:
recipe.what = "function"

elif recipe.name == "del" and not recipe.what:
recipe.what = "destructor"

Expand Down
146 changes: 108 additions & 38 deletions interfaces/sourcegen/sourcegen/clib/templates.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,57 @@ clib-definition: |-
{{ annotations }}
{{ declaration }};
clib-reserved-parentHandle-h: |-
/**
* Return handle to parent of {{ base }} object.
*
* @param handle Handle to queried {{ base }} object.
* @returns Parent handle or -1 for exception handling.
*/
int {{ prefix }}_parentHandle(int handle);
clib-reserved-cabinetSize-h: |-
/**
* Return size of {{ base }} storage.
*
* @returns Size or -1 for exception handling.
*/
int {{ prefix }}_cabinetSize();
clib-reserved-getCanteraVersion-h: |-
/**
* Get Cantera version.
* @param[in] lenBuf Length of reserved array.
* @param[out] charBuf String representation of %Cantera version.
* @returns Actual length of string or -1 for exception handling.
*/
int {{ prefix }}_getCanteraVersion(int lenBuf, char* charBuf);
clib-reserved-getCanteraError-h: |-
/**
* Get %Cantera error.
* @param[in] lenBuf Length of reserved array.
* @param[out] charBuf String containing %Cantera error.
* @returns Actual length of string or -1 for exception handling.
*/
int {{ prefix }}_getCanteraError(int lenBuf, char* charBuf);
clib-reserved-resetStorage-h: |-
/**
* Delete all objects and erase mapping.
*
* @returns Zero if successful or -1 for exception handling.
*/
int {{ prefix }}_resetStorage();
clib-reserved-clearStorage-h: |-
/**
* Delete all objects with mapping preserved.
*
* @returns Zero if successful or -1 for exception handling.
*/
int {{ prefix }}_clearStorage();
clib-header-file: |-
/**
* {{ name.upper() }} - Experimental CLib %Cantera interface library.
Expand Down Expand Up @@ -60,20 +111,6 @@ clib-header-file: |-
{{ headers | indent(4) }}
{% if base %}
{% if no_constructor %}
/**
* Return handle to parent of {{ base }} object (if applicable).
*/
int {{ prefix }}_parentHandle(int handle);
{% endif %}
/**
* Return size of {{ base }} storage.
*/
int {{ prefix }}_cabinetSize();
{% endif %}
#ifdef __cplusplus
}
#endif
Expand Down Expand Up @@ -226,6 +263,63 @@ clib-implementation: |-
{{ body | indent(4) }}
}
clib-reserved-parentHandle-cpp: |-
// {{ cxx_base }} cabinet parent
try {
return {{ base }}Cabinet3::parent(handle);
} catch (...) {
return handleAllExceptions(-2, ERR);
}
clib-reserved-cabinetSize-cpp: |-
// {{ cxx_base }} cabinet size
try {
return {{ base }}Cabinet3::size();
} catch (...) {
return handleAllExceptions(-1, ERR);
}
clib-reserved-getCanteraVersion-cpp: |-
// get Cantera version
try {
string out = CANTERA_VERSION;
copyString(out, charBuf, lenBuf);
return int(out.size());
} catch (...) {
return handleAllExceptions(-1, ERR);
}
clib-reserved-getCanteraError-cpp: |-
try {
string err = Application::Instance()->lastErrorMessage();
copyString(err, charBuf, lenBuf);
return int(err.size());
} catch (...) {
return handleAllExceptions(-1, ERR);
}
clib-reserved-resetStorage-cpp: |-
// reset storage
try {
{% for base in cabinets %}
{{ base }}Cabinet3::reset();
{% endfor %}
return 0;
} catch (...) {
return handleAllExceptions(-1, ERR);
}
clib-reserved-clearStorage-cpp: |-
// clear storage
try {
{% for base in cabinets %}
{{ base }}Cabinet3::clear();
{% endfor %}
return 0;
} catch (...) {
return handleAllExceptions(-1, ERR);
}
clib-source-file: |-
/**
* {{ name.upper() }} - Experimental CLib %Cantera interface library.
Expand Down Expand Up @@ -269,28 +363,4 @@ clib-source-file: |-
{{ implementations | indent(4) }}
{% if base %}
{% if no_constructor %}
int {{ prefix }}_parentHandle(int handle)
{
// cabinet parent
try {
return {{ base }}Cabinet3::parent(handle);
} catch (...) {
return handleAllExceptions(-2, ERR);
}
}
{% endif %}
int {{ prefix }}_cabinetSize()
{
// cabinet size
try {
return {{ base }}Cabinet3::size();
} catch (...) {
return handleAllExceptions(-1, ERR);
}
}
{% endif %}
} // extern "C"

0 comments on commit 76caedf

Please sign in to comment.