Skip to content

Commit

Permalink
Update zapt templates to handle typedef
Browse files Browse the repository at this point in the history
  • Loading branch information
gmarcosb committed Jan 15, 2025
1 parent c4b3825 commit d8dbcb4
Show file tree
Hide file tree
Showing 38 changed files with 509 additions and 159 deletions.
29 changes: 7 additions & 22 deletions examples/darwin-framework-tool/templates/commands.zapt
Original file line number Diff line number Diff line change
Expand Up @@ -223,29 +223,14 @@ public:
params.timedWriteTimeout = mTimedInteractionTimeoutMs.HasValue() ? [NSNumber numberWithUnsignedShort:mTimedInteractionTimeoutMs.Value()] : nil;
params.dataVersion = mDataVersion.HasValue() ? [NSNumber numberWithUnsignedInt:mDataVersion.Value()] : nil;
{{#if_chip_complex}}
{{asObjectiveCType type parent.name}} value;
{{>decodable_value target="value" source="mValue" cluster=parent.name errorCode="return err;" depth=0}}
{{asObjectiveCType type parent.name}} value;
{{>decodable_value target="value" source="mValue" cluster=parent.name errorCode="return err;" depth=0}}
{{else if isNullable}}
{{asObjectiveCType type parent.name}} value;
{{>decodable_value target="value" source="mValue" cluster=parent.name isOptional=false isArray=false errorCode="return err;" depth=0}}
{{else}}
{{#if isNullable}}
{{asObjectiveCType type parent.name}} value = nil;
if (!mValue.IsNull()) {
{{#if (isOctetString type)}}
value = [[NSData alloc] initWithBytes:mValue.Value().data() length:mValue.Value().size()];
{{else if (isString type)}}
value = [[NSString alloc] initWithBytes:mValue.Value().data() length:mValue.Value().size() encoding:NSUTF8StringEncoding];
{{else}}
value = [NSNumber numberWith{{asObjectiveCNumberType "" type false}}:mValue.Value()];
{{/if}}
}
{{else}}
{{#if (isOctetString type)}}
{{asObjectiveCType type parent.name}} value = [[NSData alloc] initWithBytes:mValue.data() length:mValue.size()];
{{else if (isString type)}}
{{asObjectiveCType type parent.name}} value = [[NSString alloc] initWithBytes:mValue.data() length:mValue.size() encoding:NSUTF8StringEncoding];
{{else}}
{{asObjectiveCType type parent.name}} value = [NSNumber numberWith{{asObjectiveCNumberType "" type false}}:mValue{{#if isNullable}}.Value(){{/if}}];
{{/if}}
{{/if}}
{{asObjectiveCType type parent.name}}
{{>decodable_value target="value" source="mValue" cluster=parent.name isOptional=false isArray=false errorCode="return err;" depth=0}}
{{/if_chip_complex}}

[cluster write{{>attribute}}WithValue:value params:params completion:^(NSError * _Nullable error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,24 @@
{{>decodable_value target=(concat ../target "." (asStructPropertyName label)) source=(concat ../source "." (asLowerCamelCase label)) cluster=../cluster depth=(incrementDepth ../depth) }}
{{/zcl_struct_items_by_struct_and_cluster_name}}
{{else}}
{{#if_is_strongly_typed_chip_enum type}}
{{target}} = [NSNumber numberWith{{asObjectiveCNumberType "" type false}}:chip::to_underlying({{source}})];
{{#if_is_typedef type}}
{{#zcl_typedef_by_typedef_and_cluster_name type cluster}}
{{>decodable_value target=../target source=../source errorCode=../errorCode cluster=../cluster depth=../depth }}
{{/zcl_typedef_by_typedef_and_cluster_name}}
{{else}}
{{#if_is_strongly_typed_bitmap type}}
{{target}} = [NSNumber numberWith{{asObjectiveCNumberType "" type false}}:{{source}}.Raw()];
{{else if (isOctetString type)}}
{{target}} = [NSData dataWithBytes:{{source}}.data() length:{{source}}.size()];
{{else if (isCharString type)}}
{{target}} = [[NSString alloc] initWithBytes:{{source}}.data() length:{{source}}.size() encoding:NSUTF8StringEncoding];
{{#if_is_strongly_typed_chip_enum type}}
{{target}} = [NSNumber numberWith{{asObjectiveCNumberType "" type false}}:chip::to_underlying({{source}})];
{{else}}
{{target}} = [NSNumber numberWith{{asObjectiveCNumberType "" type false}}:{{source}}];
{{/if_is_strongly_typed_bitmap}}
{{/if_is_strongly_typed_chip_enum}}
{{#if_is_strongly_typed_bitmap type}}
{{target}} = [NSNumber numberWith{{asObjectiveCNumberType "" type false}}:{{source}}.Raw()];
{{else if (isOctetString type)}}
{{target}} = [NSData dataWithBytes:{{source}}.data() length:{{source}}.size()];
{{else if (isCharString type)}}
{{target}} = [[NSString alloc] initWithBytes:{{source}}.data() length:{{source}}.size() encoding:NSUTF8StringEncoding];
{{else}}
{{target}} = [NSNumber numberWith{{asObjectiveCNumberType "" type false}}:{{source}}];
{{/if_is_strongly_typed_bitmap}}
{{/if_is_strongly_typed_chip_enum}}
{{/if_is_typedef}}
{{/if_is_struct}}
{{/if}}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from xml.sax.xmlreader import AttributesImpl

from matter_idl.matter_idl_types import (ApiMaturity, Attribute, AttributeQuality, Bitmap, Cluster, Command, CommandQuality,
ConstantEntry, DataType, Enum, Field, FieldQuality, Idl, Struct, StructTag)
ConstantEntry, DataType, Enum, Field, FieldQuality, Idl, Struct, StructTag, Typedef)

from .base import BaseHandler, HandledDepth
from .context import Context
Expand Down Expand Up @@ -178,6 +178,10 @@ def GetNextProcessor(self, name: str, attrs: AttributesImpl):
self._field.data_type.name = NormalizeDataType(attrs["type"])

return BaseHandler(self.context, handled=HandledDepth.SINGLE_TAG)
elif name == "typedef":
LOGGER.warning(
f"Anonymous typedef not supported when handling field {self._field.name}")
return BaseHandler(self.context, handled=HandledDepth.ENTIRE_TREE)
else:
return BaseHandler(self.context)

Expand Down Expand Up @@ -281,6 +285,24 @@ def GetNextProcessor(self, name: str, attrs: AttributesImpl):
else:
return BaseHandler(self.context)

class TypedefHandler(BaseHandler):
def __init__(self, context: Context, cluster: Cluster, attrs: AttributesImpl):
super().__init__(context, handled=HandledDepth.SINGLE_TAG)
self._cluster = cluster

# TODO: base type is GUESSED here because xml does not contain it
self._typedef = Typedef(name=NormalizeName(
attrs["name"]), base_type="UNKNOWN")

def EndProcessing(self):
self._cluster.typedefs.append(self._typedef)

def GetNextProcessor(self, name: str, attrs: AttributesImpl):
if name == "section":
# Documentation data, skipped
return BaseHandler(self.context, handled=HandledDepth.ENTIRE_TREE)
else:
return BaseHandler(self.context)

class EventsHandler(BaseHandler):
def __init__(self, context: Context, cluster: Cluster):
Expand Down Expand Up @@ -548,6 +570,8 @@ def GetNextProcessor(self, name: str, attrs: AttributesImpl):
return BitmapHandler(self.context, self._cluster, attrs)
elif name == "struct":
return StructHandler(self.context, self._cluster, attrs)
elif name == "typedef":
return TypedefHandler(self.context, self._cluster, attrs)
else:
return BaseHandler(self.context)

Expand Down
11 changes: 11 additions & 0 deletions scripts/py_matter_idl/matter_idl/generators/idl/MatterIdl.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ bitmap {{bitmap.name}} : {{ bitmap.base_type}} {

{% endfor %}

{%- for typedef in idl.global_typedefs %}
typedef {{typedef.name}} : {{ typedef.base_type}};
{% endfor %}

{%- for cluster in idl.clusters %}
{% if cluster.description %}/** {{cluster.description}} */
{% endif %}
Expand Down Expand Up @@ -83,6 +87,13 @@ bitmap {{bitmap.name}} : {{ bitmap.base_type}} {

{% endfor %}

{%- for typedef in cluster.typedefs | selectattr("is_global")%}
/* GLOBAL:
typedef {{typedef.name}} : {{ typedef.base_type}};
*/

{% endfor %}

{%- for s in cluster.structs | selectattr("is_global") %}
/* GLOBAL:
{{render_struct(s) | indent(2)}}
Expand Down
21 changes: 13 additions & 8 deletions scripts/py_matter_idl/matter_idl/generators/java/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class GlobalType:


def _UnderlyingType(field: Field, context: TypeLookupContext) -> Optional[str]:
actual = ParseDataType(field.data_type, context)
actual = ParseDataType(field.data_type, context, desugar_typedef = True)
if isinstance(actual, (IdlEnumType, IdlBitmapType)):
actual = actual.base_type

Expand Down Expand Up @@ -132,7 +132,7 @@ def _CppType(field: Field, context: TypeLookupContext) -> str:
if field.data_type.name.lower() in _KNOWN_DECODABLE_TYPES:
return _KNOWN_DECODABLE_TYPES[field.data_type.name.lower()]

actual = ParseDataType(field.data_type, context)
actual = ParseDataType(field.data_type, context, desugar_typedef = True)
if isinstance(actual, BasicString):
if actual.is_binary:
return 'chip::ByteSpan'
Expand Down Expand Up @@ -278,7 +278,7 @@ def attributesWithSupportedCallback(attrs, context: TypeLookupContext):
# Attributes will be generated for all types
# except non-list structures
if not attr.definition.is_list:
underlying = ParseDataType(attr.definition.data_type, context)
underlying = ParseDataType(attr.definition.data_type, context, desugar_typedef = True)
if isinstance(underlying, IdlType):
continue

Expand Down Expand Up @@ -420,6 +420,10 @@ def is_bitmap(self):
def is_untyped_bitmap(self):
return self.context.is_untyped_bitmap_type(self.data_type.name)

@property
def is_typedef(self):
return self.context.is_typedef_type(self.data_type.name)

def clone(self):
return EncodableValue(self.context, self.data_type, self.attrs)

Expand Down Expand Up @@ -469,7 +473,7 @@ def jni_fundamental_type(self):

@property
def boxed_java_type(self):
t = ParseDataType(self.data_type, self.context)
t = ParseDataType(self.data_type, self.context, desugar_typedef = True)

if isinstance(t, FundamentalType):
if t == FundamentalType.BOOL:
Expand Down Expand Up @@ -506,7 +510,7 @@ def boxed_java_type(self):

@property
def java_tlv_type(self):
t = ParseDataType(self.data_type, self.context)
t = ParseDataType(self.data_type, self.context, desugar_typedef = True)

if isinstance(t, FundamentalType):
if t == FundamentalType.BOOL:
Expand Down Expand Up @@ -537,7 +541,8 @@ def java_tlv_type(self):

@property
def kotlin_type(self):
t = ParseDataType(self.data_type, self.context)
# TODO: Use inline value class where possible (types not defined in java)
t = ParseDataType(self.data_type, self.context, desugar_typedef = True)

if isinstance(t, FundamentalType):
if t == FundamentalType.BOOL:
Expand Down Expand Up @@ -583,7 +588,7 @@ def unboxed_java_signature(self):
if self.is_optional or self.is_list:
raise Exception("Not a basic type: %r" % self)

t = ParseDataType(self.data_type, self.context)
t = ParseDataType(self.data_type, self.context, desugar_typedef = True)

if isinstance(t, FundamentalType):
if t == FundamentalType.BOOL:
Expand Down Expand Up @@ -611,7 +616,7 @@ def boxed_java_signature(self):
if self.is_list:
return "Ljava/util/ArrayList;"

t = ParseDataType(self.data_type, self.context)
t = ParseDataType(self.data_type, self.context, desugar_typedef = True)

if isinstance(t, FundamentalType):
if t == FundamentalType.BOOL:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ def get_underlying_enum(self):

@property
def kotlin_type(self):
t = ParseDataType(self.data_type, self.context)
t = ParseDataType(self.data_type, self.context, desugar_typedef=True)

if isinstance(t, FundamentalType):
if t == FundamentalType.BOOL:
Expand Down Expand Up @@ -507,7 +507,7 @@ def boxed_java_signature(self):
if self.is_list:
return "Ljava/util/ArrayList;"

t = ParseDataType(self.data_type, self.context)
t = ParseDataType(self.data_type, self.context, desugar_typedef=True)

if isinstance(t, FundamentalType):
if t == FundamentalType.BOOL:
Expand Down
47 changes: 45 additions & 2 deletions scripts/py_matter_idl/matter_idl/generators/type_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,15 @@ def is_struct(self) -> bool:
return self.item_type == IdlItemType.STRUCT


@dataclass
class IdlTypedefType:
"""
A typedef (concrete type that aliases another type)
"""
idl_name: str
base_type: Union[BasicInteger, BasicString, FundamentalType, IdlType, IdlEnumType, IdlBitmapType]


# Data types, held by ZAP in chip-types.xml and generally by the spec.
__CHIP_SIZED_TYPES__ = {
"bitmap16": BasicInteger(idl_name="bitmap16", byte_count=2, is_signed=False),
Expand Down Expand Up @@ -295,6 +304,13 @@ def find_bitmap(self, name) -> Optional[matter_idl_types.Bitmap]:

return None

def find_typedef(self, name) -> Optional[matter_idl_types.Typedef]:
for s in self.all_typedefs:
if s.name == name:
return s

return None

@property
def all_enums(self):
"""
Expand Down Expand Up @@ -330,6 +346,18 @@ def all_structs(self):
for e in self.cluster.structs:
yield e

@property
def all_typedefs(self):
"""
All typedefs defined within this lookup context.
typedefs are only defined at cluster level. If lookup context does not
include a typedef, the typedef list will be empty.
"""
if self.cluster:
for t in self.cluster.typedefs:
yield t

def is_enum_type(self, name: str):
"""
Determine if the given type name is an enumeration.
Expand All @@ -347,6 +375,12 @@ def is_struct_type(self, name: str):
"""
return any(map(lambda s: s.name == name, self.all_structs))

def is_typedef_type(self, name: str):
"""
Determine if the given type name is type that is known to be a typedef
"""
return any(map(lambda s: s.name == name, self.all_typedefs))

def is_untyped_bitmap_type(self, name: str):
"""Determine if the given type is a untyped bitmap (just an interger size)."""
return name.lower() in {"bitmap8", "bitmap16", "bitmap32", "bitmap64"}
Expand All @@ -364,7 +398,7 @@ def is_bitmap_type(self, name: str):
return any(map(lambda s: s.name == name, self.all_bitmaps))


def ParseDataType(data_type: DataType, lookup: TypeLookupContext) -> Union[BasicInteger, BasicString, FundamentalType, IdlType, IdlEnumType, IdlBitmapType]:
def ParseDataType(data_type: DataType, lookup: TypeLookupContext, desugar_typedef: bool = False) -> Union[BasicInteger, BasicString, FundamentalType, IdlType, IdlEnumType, IdlBitmapType, IdlTypedefType]:
"""
Given a AST data type and a lookup context, match it to a type that can be later
be used for generation.
Expand Down Expand Up @@ -404,9 +438,18 @@ def ParseDataType(data_type: DataType, lookup: TypeLookupContext) -> Union[Basic

b = lookup.find_bitmap(data_type.name)
if b:
# Valid enum found. it MUST be based on a valid data type
# Valid bitmap found. it MUST be based on a valid data type
return IdlBitmapType(idl_name=data_type.name, base_type=__CHIP_SIZED_TYPES__[b.base_type.lower()])

t = lookup.find_typedef(data_type.name)
if t:
# Valid typedef found. it MUST be based on a valid data type
typedef_base_type = ParseDataType(DataType(name=t.base_type), lookup)
if desugar_typedef:
return typedef_base_type
else:
return IdlTypedefType(idl_name=data_type.name, base_type=typedef_base_type)

result = IdlType(idl_name=data_type.name, item_type=IdlItemType.UNKNOWN)
if lookup.find_struct(data_type.name):
result.item_type = IdlItemType.STRUCT
Expand Down
5 changes: 3 additions & 2 deletions scripts/py_matter_idl/matter_idl/matter_grammar.lark
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ struct_qualities: struct_quality*

enum: "enum"i id ":" type "{" constant_entry* "}"
bitmap: "bitmap"i id ":" type "{" constant_entry* "}"
typedef: "typedef"i id ":" type ";"

?access_privilege: "view"i -> view_privilege
| "operate"i -> operate_privilege
Expand Down Expand Up @@ -68,7 +69,7 @@ cluster_revision: "revision"i positive_integer ";"
// no direct meaning currently
cluster: [maturity] ( "client" | "server" )? "cluster"i id "=" positive_integer "{" [cluster_revision] cluster_content* "}"

?cluster_content: [maturity] (enum|bitmap|event|attribute|struct|request_struct|response_struct|command)
?cluster_content: [maturity] (enum|bitmap|event|attribute|struct|request_struct|response_struct|command|typedef)

endpoint: "endpoint"i positive_integer "{" endpoint_content* "}"
?endpoint_content: endpoint_cluster_binding | endpoint_server_cluster | endpoint_device_type
Expand Down Expand Up @@ -116,7 +117,7 @@ POSITIVE_INTEGER: /\d+/
HEX_INTEGER: /0x[A-Fa-f0-9]+/
ID: /[a-zA-Z_][a-zA-Z0-9_]*/

idl: (struct|enum|bitmap|cluster|endpoint)*
idl: (struct|enum|bitmap|cluster|endpoint|typedef)*

%import common.ESCAPED_STRING
%import common.WS
Expand Down
Loading

0 comments on commit d8dbcb4

Please sign in to comment.