def field_type(type): """Extract the outermost type from a field""" if not isinstance(type, dict): if is_primitive(type): return type raise CmfParseError(type.parseinfo, f"Invalid field type: {type}") # The semantics class puts enum and msg into the same form as a compound for compound in [ "list", "fixedlist", "kvpair", "map", "optional", "oneof", "enum", "msg" ]: if compound in type: return compound raise CmfParseError(type.parseinfo, f"Invalid field type: {type}")
def enumname(self, ast): """ Check that enum names are unique """ if ast.name in self.symbol_table.enum_names: raise CmfParseError( ast.parseinfo, 'Enum: "{}" already defined on line {}'.format( ast.name, self.symbol_table.enum_names[ast.name])) if ast.name in self.symbol_table.msg_names: raise CmfParseError( ast.parseinfo, 'Enum: "{}" cannot have the same name as the Msg defined on line {}' .format(ast.name, self.symbol_table.msg_names[ast.name])) # parseinfo.line is zero-based self.symbol_table.enum_names[ast.name] = ast.parseinfo.line + 1 return ast.name
def msgid(self, ast): """ Check that each message id is unique and fits in a 32 bit integer """ id = int(ast.id) if id < 0 or id > pow(2, 32): raise CmfParseError( ast.parseinfo, 'Message ID: "{}" must fit in a uint32'.format(id)) if id in self.symbol_table.msg_ids: raise CmfParseError( ast.parseinfo, 'Message ID: "{}" already defined on line {}'.format( id, self.symbol_table.msg_ids[id])) # parseinfo.line is zero-based self.symbol_table.msg_ids[id] = ast.parseinfo.line + 1 return id
def msgname_ref(self, ast): if ast.name not in self.symbol_table.msg_names.keys(): raise CmfParseError( ast.parseinfo, "Messages must be defined before they are referenced: {}". format(ast.name)) return ast.name
def enum_def(self, ast): """ Ensure that an enum fits in a uint8 and that it has no duplicate fields """ if (len(ast.tags) > 256): raise CmfParseError( ast.parseinfo, 'Enum: "{}" contains more than 256 entries. It must fit in a uint8_t' .format(ast.name)) tags = set() for tag in ast.tags: if tag in tags: raise CmfParseError( ast.parseinfo, 'Enum: "{}" contains duplicate tag: "{}"'.format( ast.name, tag)) tags.add(tag) return ast
def walk_type(self, type): if not isinstance(type, dict): if is_primitive(type): getattr(self.visitor, type)() else: self.visitor.msgname_ref(type) elif "list" in type: self.visitor.list_start() self.walk_type(type.list.type) self.visitor.list_end() elif "kvpair" in type: self.visitor.kvpair_start() self.walk_type(type.kvpair.key) self.visitor.kvpair_key_end() self.walk_type(type.kvpair.value) self.visitor.kvpair_end() elif "map" in type: self.visitor.map_start() self.walk_type(type.map.key) self.visitor.map_key_end() self.walk_type(type.map.value) self.visitor.map_end() elif "optional" in type: self.visitor.optional_start() self.walk_type(type.optional.type) self.visitor.optional_end() elif "oneof" in type: self.visitor.oneof( dict([(n, self.msgs[n]) for n in type.oneof.msg_names])) else: raise CmfParseError(type.parseinfo, "Invalid field type")
def walk_type(self, type): if not isinstance(type, dict): if is_primitive(type): getattr(self.visitor, type)() elif "list" in type: self.visitor.list_start() self.walk_type(type.list.type) self.visitor.list_end() elif "fixedlist" in type: self.visitor.fixedlist_start() self.walk_type(type.fixedlist.type) self.visitor.fixedlist_type_end() self.visitor.fixedlist_end(type.fixedlist.size) elif "kvpair" in type: self.visitor.kvpair_start() self.walk_type(type.kvpair.key) self.visitor.kvpair_key_end() self.walk_type(type.kvpair.value) self.visitor.kvpair_end() elif "map" in type: self.visitor.map_start() self.walk_type(type.map.key) self.visitor.map_key_end() self.walk_type(type.map.value) self.visitor.map_end() elif "optional" in type: self.visitor.optional_start() self.walk_type(type.optional.type) self.visitor.optional_end() elif "oneof" in type: msgs = dict() for d in type.oneof.msg_names: if not 'msg' in d: # This is needed to prevent oneofs of Enums now that both messages and enums # are top level types raise CmfParseError( type.parseinfo, "A oneof can only contain names of messages") name = d['msg'] msgs[name] = self.msgs[name] self.visitor.oneof(msgs) elif "msg" in type: self.visitor.msgname_ref(type['msg']) elif "enum" in type: self.visitor.enum(type['enum']) else: raise CmfParseError(type.parseinfo, "Invalid field type")
def msgname(self, ast): """ Check that message names are unique """ if ast.name in self.symbol_table.msg_names: raise CmfParseError( ast.parseinfo, 'Message: "{}" already defined on line {}'.format( ast.name, self.symbol_table.msg_names[ast.name])) # parseinfo.line is zero-based self.symbol_table.msg_names[ast.name] = ast.parseinfo.line + 1 return ast.name
def toplevel_ref(self, ast): """ Determine if the name in the ast is a Msg or Enum and tag it. """ if ast.name in self.symbol_table.msg_names.keys(): return {'msg': ast.name} if ast.name in self.symbol_table.enum_names.keys(): return {'enum': ast.name} raise CmfParseError( ast.parseinfo, "Messages or Enums must be defined before they are referenced: {}". format(ast.name))
def field_type(type): """Extract the outermost type from a field""" if not isinstance(type, dict): if is_primitive(type): return type return "msg" for compound in ["list", "kvpair", "map", "optional", "oneof"]: if compound in type: return compound raise CmfParseError(type.parseinfo, f"Invalid field type: {type}")
def msg(self, ast): """ Check that each field in a message has a unique name """ field_names = set() for field in ast.fields: if field.name in field_names: raise CmfParseError( ast.parseinfo, 'Message: "{}" contains duplicate field: "{}"'.format( ast.name, field.name)) field_names.add(field.name) return ast