Skip to content

Commit

Permalink
Parsing Julia types from c++ types in yaml description (#310)
Browse files Browse the repository at this point in the history
  • Loading branch information
soumilbaldota authored Jul 3, 2022
1 parent 9d078f8 commit e129173
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 1 deletion.
31 changes: 31 additions & 0 deletions python/generator_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,33 @@ def _prefix_name(name, prefix):
return name


def get_julia_type(cpp_type=None, is_array=False, array_type=None, array_size=None):
"""Parse the given c++ type to a Julia type"""
builtin_types_map = {"int": "Int32", "float": "Float32", "double": "Float64",
"bool": "Bool", "long": "Int32", "unsigned int": "UInt32",
"unsigned long": "UInt32", "char": "Char", "short": "Int16",
"long long": "Int64", "unsigned long long": "UInt64"}
# is a global type as described in test_MemberParser.py #L121
if cpp_type and cpp_type.startswith("::"):
cpp_type = cpp_type[2:]
if cpp_type in builtin_types_map:
return builtin_types_map[cpp_type]

if not is_array:
if cpp_type.startswith('std::'):
cpp_type = cpp_type[5:]
if cpp_type in ALLOWED_FIXED_WIDTH_TYPES:
regex_string = re.split("(u|)int(8|16|32|64)_t", cpp_type)
cpp_type = regex_string[1].upper() + "Int" + regex_string[2]
return cpp_type

else:
array_type = get_julia_type(cpp_type=array_type)
return f"MVector{{{array_size}, {array_type}}}"

return cpp_type


class DefinitionError(Exception):
"""Exception raised by the ClassDefinitionValidator for invalid definitions.
Mainly here to distinguish it from plain exceptions that are otherwise raised.
Expand Down Expand Up @@ -95,6 +122,7 @@ def __init__(self, name, **kwargs):
self.is_array = False
# ensure that this will break somewhere if requested but not set
self.namespace, self.bare_type = None, None
self.julia_type = None
self.array_namespace, self.array_bare_type = None, None

self.array_type = kwargs.pop('array_type', None)
Expand Down Expand Up @@ -148,6 +176,9 @@ def __init__(self, name, **kwargs):
else:
self.namespace, self.bare_type = _get_namespace_class(self.full_type)

self.julia_type = get_julia_type(cpp_type=self.bare_type, is_array=self.is_array,
array_type=self.array_type, array_size=self.array_size)

def __str__(self):
"""string representation"""
# Make sure to include scope-operator if necessary
Expand Down
2 changes: 1 addition & 1 deletion python/podio_class_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ def get_fn_format(tmpl):
endings = {
'Data': ('h',),
'Component': ('h',),
'PrintInfo': ('h',)
'PrintInfo': ('h',),
}.get(template_base, ('h', 'cc'))

fn_templates = []
Expand Down
20 changes: 20 additions & 0 deletions python/test_MemberParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,25 @@ def test_parse_valid(self): # pylint: disable=too-many-statements
self.assertEqual(parsed.full_type, r'float')
self.assertEqual(parsed.name, r'someFloat')
self.assertEqual(parsed.description, r'with an additional comment')
self.assertEqual(parsed.julia_type, r'Float32')

parsed = parser.parse(r'float float2 // with numbers')
self.assertEqual(parsed.full_type, r'float')
self.assertEqual(parsed.name, r'float2')
self.assertEqual(parsed.description, r'with numbers')
self.assertEqual(parsed.julia_type, r'Float32')

parsed = parser.parse(r' float spacefloat // whitespace everywhere ')
self.assertEqual(parsed.full_type, r'float')
self.assertEqual(parsed.name, r'spacefloat')
self.assertEqual(parsed.description, 'whitespace everywhere')
self.assertEqual(parsed.julia_type, r'Float32')

parsed = parser.parse(r'int snake_case // snake case')
self.assertEqual(parsed.full_type, r'int')
self.assertEqual(parsed.name, r'snake_case')
self.assertEqual(parsed.description, r'snake case')
self.assertEqual(parsed.julia_type, r'Int32')

parsed = parser.parse(r'std::string mixed_UglyCase_12 // who wants this')
self.assertEqual(parsed.full_type, r'std::string')
Expand All @@ -46,11 +50,13 @@ def test_parse_valid(self): # pylint: disable=too-many-statements
self.assertEqual(parsed.full_type, r'unsigned long long')
self.assertEqual(parsed.name, r'uVar')
self.assertEqual(parsed.description, r'an unsigned long variable')
self.assertEqual(parsed.julia_type, r'UInt64')

parsed = parser.parse(r'unsigned int uInt // an unsigned integer')
self.assertEqual(parsed.full_type, r'unsigned int')
self.assertEqual(parsed.name, r'uInt')
self.assertEqual(parsed.description, r'an unsigned integer')
self.assertEqual(parsed.julia_type, r'UInt32')

# Fixed width integers in their various forms that they can be spelled out
# and be considered valid in our case
Expand All @@ -59,24 +65,28 @@ def test_parse_valid(self): # pylint: disable=too-many-statements
self.assertEqual(parsed.name, r'qualified')
self.assertEqual(parsed.description, r'qualified fixed width ints work')
self.assertTrue(parsed.is_builtin)
self.assertEqual(parsed.julia_type, r'Int16')

parsed = parser.parse(r'std::uint64_t bits // fixed width integer types should work')
self.assertEqual(parsed.full_type, r'std::uint64_t')
self.assertEqual(parsed.name, r'bits')
self.assertEqual(parsed.description, r'fixed width integer types should work')
self.assertTrue(parsed.is_builtin)
self.assertEqual(parsed.julia_type, r'UInt64')

parsed = parser.parse(r'int32_t fixedInt // fixed width signed integer should work')
self.assertEqual(parsed.full_type, r'std::int32_t')
self.assertEqual(parsed.name, r'fixedInt')
self.assertEqual(parsed.description, r'fixed width signed integer should work')
self.assertTrue(parsed.is_builtin)
self.assertEqual(parsed.julia_type, r'Int32')

parsed = parser.parse(r'uint16_t fixedUInt // fixed width unsigned int with 16 bits')
self.assertEqual(parsed.full_type, r'std::uint16_t')
self.assertEqual(parsed.name, r'fixedUInt')
self.assertEqual(parsed.description, r'fixed width unsigned int with 16 bits')
self.assertTrue(parsed.is_builtin)
self.assertEqual(parsed.julia_type, r'UInt16')

# an array definition with space everywhere it is allowed
parsed = parser.parse(r' std::array < double , 4 > someArray // a comment ')
Expand All @@ -87,38 +97,44 @@ def test_parse_valid(self): # pylint: disable=too-many-statements
self.assertTrue(parsed.is_builtin_array)
self.assertEqual(int(parsed.array_size), 4)
self.assertEqual(parsed.array_type, r'double')
self.assertEqual(parsed.julia_type, r'MVector{4, Float64}')

# an array definition as terse as possible
parsed = parser.parse(r'std::array<int,2>anArray//with a comment')
self.assertEqual(parsed.full_type, r'std::array<int, 2>')
self.assertEqual(parsed.name, r'anArray')
self.assertEqual(parsed.description, r'with a comment')
self.assertEqual(parsed.julia_type, r'MVector{2, Int32}')

parsed = parser.parse('::TopLevelNamespaceType aValidType // hopefully')
self.assertEqual(parsed.full_type, '::TopLevelNamespaceType')
self.assertEqual(parsed.name, r'aValidType')
self.assertEqual(parsed.description, 'hopefully')
self.assertEqual(parsed.julia_type, r'TopLevelNamespaceType')

parsed = parser.parse(r'std::array<::GlobalType, 1> anArray // with a top level type')
self.assertEqual(parsed.full_type, r'std::array<::GlobalType, 1>')
self.assertEqual(parsed.name, r'anArray')
self.assertEqual(parsed.description, r'with a top level type')
self.assertTrue(not parsed.is_builtin_array)
self.assertEqual(parsed.array_type, r'::GlobalType')
self.assertEqual(parsed.julia_type, r'MVector{1, GlobalType}')

parsed = parser.parse(r'std::array<std::int16_t, 42> fixedWidthArray // a fixed width type array')
self.assertEqual(parsed.full_type, r'std::array<std::int16_t, 42>')
self.assertEqual(parsed.name, r'fixedWidthArray')
self.assertEqual(parsed.description, r'a fixed width type array')
self.assertTrue(parsed.is_builtin_array)
self.assertEqual(parsed.array_type, r'std::int16_t')
self.assertEqual(parsed.julia_type, r'MVector{42, Int16}')

parsed = parser.parse(r'std::array<uint32_t, 42> fixedWidthArray // a fixed width type array without namespace')
self.assertEqual(parsed.full_type, r'std::array<std::uint32_t, 42>')
self.assertEqual(parsed.name, r'fixedWidthArray')
self.assertEqual(parsed.description, r'a fixed width type array without namespace')
self.assertTrue(parsed.is_builtin_array)
self.assertEqual(parsed.array_type, r'std::uint32_t')
self.assertEqual(parsed.julia_type, r'MVector{42, UInt32}')

def test_parse_invalid(self):
"""Test that invalid member variable definitions indeed fail during parsing"""
Expand Down Expand Up @@ -161,22 +177,26 @@ def test_parse_valid_no_description(self):
parsed = parser.parse('unsigned long long aLongWithoutDescription', False)
self.assertEqual(parsed.full_type, 'unsigned long long')
self.assertEqual(parsed.name, 'aLongWithoutDescription')
self.assertEqual(parsed.julia_type, r'UInt64')

parsed = parser.parse('std::array<unsigned long, 123> unDescribedArray', False)
self.assertEqual(parsed.full_type, 'std::array<unsigned long, 123>')
self.assertEqual(parsed.name, 'unDescribedArray')
self.assertEqual(parsed.array_type, 'unsigned long')
self.assertTrue(parsed.is_builtin_array)
self.assertEqual(parsed.julia_type, r'MVector{123, UInt32}')

parsed = parser.parse('unsigned long longWithReallyStupidName', False)
self.assertEqual(parsed.full_type, 'unsigned long')
self.assertEqual(parsed.name, 'longWithReallyStupidName')
self.assertEqual(parsed.julia_type, r'UInt32')

parsed = parser.parse('NonBuiltIn aType // descriptions are not ignored even though they are not required', False)
self.assertEqual(parsed.full_type, 'NonBuiltIn')
self.assertEqual(parsed.name, 'aType')
self.assertEqual(parsed.description, 'descriptions are not ignored even though they are not required')
self.assertTrue(not parsed.is_builtin)
self.assertEqual(parsed.julia_type, r'NonBuiltIn')

def test_string_representation(self):
"""Test that the string representation that is used in the jinja2 templates
Expand Down

0 comments on commit e129173

Please sign in to comment.