Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion parser/outparam.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,27 @@ def doxygen_sources(meos_root: str | Path) -> list[str]:
# here because, combined with this module's whole-doc-block `_FUNC` match, a DOTALL
# `.*?` inside the skip run backtracks catastrophically (doxygroup runs a bounded
# two-step search, so it can afford it); no vendored out-param carrier needs it.
# A guard may also sit between the return-type line and the name, choosing between two
# spellings of one signature (`basetype_in` in meos/src/temporal/type_in.c marks a
# parameter UNUSED in the build that does not read it):
# bool
# #if CBUFFER || NPOINT || POSE
# basetype_in(const char *str, MeosType type, bool end, Datum *result)
# #else
# basetype_in(const char *str, MeosType type, bool end UNUSED, Datum *result)
# #endif
# {
# `_TWIN` steps over the `#else`/`#elif` spellings and the closing `#endif`, so
# `params` is the first spelling and the match still ends at the body's `{`.
_SKIP = r"(?:[^\S\n]*(?:\#[^\n]*|//[^\n]*)?[^\S\n]*\n)*"
_TWIN = (r"(?:[^\S\n]*\n[^\S\n]*\#[^\S\n]*(?:else|elif)[^\n]*\n[^;{]*?\))*"
r"(?:[^\S\n]*\n[^\S\n]*\#[^\S\n]*endif[^\n]*)?")
_FUNC = re.compile(
r'/\*\*(?P<doc>.*?)\*/[^\S\n]*\n' + _SKIP +
r'(?:[A-Za-z_][\w\s\*]*?\n)?' # optional return-type line
+ _SKIP +
r'(?P<name>[a-z][a-z0-9_]*)\s*\('
r'(?P<params>[^;{]*?)\)\s*\{',
r'(?P<params>[^;{]*?)\)' + _TWIN + r'\s*\{',
re.S)
# One @param[out] entry: capture the (possibly comma-separated) parameter names.
_POUT = re.compile(r'@param\[out\]\s+(?P<names>\w+(?:\s*,\s*\w+)*)', re.S)
Expand Down
74 changes: 74 additions & 0 deletions tests/test_outparam.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,81 @@ def _write(root, rel, text):
"""


# A guard between the return-type line and the name choosing between two spellings
# of one signature, the shape of `basetype_in` in meos/src/temporal/type_in.c, then
# an undocumented neighbour that must not inherit the block.
TWIN_SIGNATURE = """\
/**
* @brief doc
* @param[in] str String
* @param[in] end True when the string must end where the value ends
* @param[out] result Value read
*/
bool
#if CBUFFER || NPOINT || POSE
basetype_in(const char *str, bool end, Datum *result)
#else
basetype_in(const char *str, bool end UNUSED, Datum *result)
#endif
{
return true;
}

/**
* @brief doc
*/
static Datum
parse_coord(json_object *obj)
{
return 0;
}
"""

# A guard inside the parameter list, the shape of `skiplist_splice` in
# meos/src/temporal/skiplist.c, then an undocumented neighbour.
TWIN_TAIL = """\
/**
* @brief doc
* @param[in] list Skiplist
* @param[in] sktype Type of the skiplist
*/
void
skiplist_splice(SkipList *list,
#if MEOS
SkipListType sktype)
#else
SkipListType sktype UNUSED)
#endif /* ! MEOS */
{
return;
}

/**
* @brief doc
*/
void **
skiplist_values(SkipList *list)
{
return NULL;
}
"""


class TestExtract(unittest.TestCase):
def test_guard_between_return_type_and_name(self):
with tempfile.TemporaryDirectory() as root:
_write(root, "meos/src/type_in.c", TWIN_SIGNATURE)
out = extract_outparams(Path(root) / "meos")
self.assertEqual(out.get("basetype_in"), ["result"])
self.assertNotIn("parse_coord", out)

def test_guard_inside_the_parameter_list(self):
with tempfile.TemporaryDirectory() as root:
_write(root, "meos/src/skiplist.c", TWIN_TAIL)
names = extract_param_names(Path(root) / "meos")
self.assertEqual(names.get("skiplist_splice"), {"list", "sktype"})
self.assertNotIn("skiplist_values", names)

def test_plain_meos_src(self):
with tempfile.TemporaryDirectory() as root:
_write(root, "meos/src/temporal_tile_meos.c", PLAIN)
Expand Down
Loading