def __new__(mcs, name, bases, dct): # Does this subclass come from this module? If so, it's not to be # considered as a DSLType per se. is_base = ((len(bases) == 1 and bases[0] in (object, BaseStruct)) or dct.pop('_ASTNodeList__is_astnode_list_cls', False)) # Is this the root AST node type? is_root = not is_base and mcs.root_type is None if not is_base: mcs.process_subclass(name, bases, dct, is_root) cls = type.__new__(mcs, name, bases, dct) if is_root: mcs.root_type = cls if not is_base: mcs.astnode_types.append(cls) # Create the corresponding ASTNodeType subclass if cls._base is _ASTNodeList: # Only root list types are supposed to directly subclass # _ASTNodeList. element_type = cls._element_type._resolve() assert element_type astnode_type = element_type.list else: astnode_type = ASTNodeType( cls._name, cls._location, cls._doc, base=None if is_root else cls._base._resolve(), fields=cls._fields, env_spec=cls._env_spec, annotations=cls._annotations, is_token_node=cls._is_token_node, ) astnode_type.dsl_decl = cls cls._type = astnode_type return cls
def process_subclass(mcs, name, bases, dct, is_root): from langkit.envs import EnvSpec location = extract_library_location() base = bases[0] is_list_type = issubclass(base, _ASTNodeList) is_root_list_type = base is _ASTNodeList node_ctx = Context('in {}'.format(name), location) with node_ctx: check_source_language( len(bases) == 1, 'ASTNode subclasses must have exactly one' ' base class') if mcs.root_type is not None: check_source_language( base is not ASTNode, 'Only one class can derive from ASTNode (previous was:' ' {})'.format(mcs.root_type.__name__)) env_spec = dct.pop('env_spec', None) check_source_language( env_spec is None or isinstance(env_spec, EnvSpec), 'Invalid environment specification: {}'.format(env_spec)) annotations = dct.pop('annotations', None) # If this is a list type, determine the corresponding element type if is_list_type: element_type = (dct.pop('_element_type') if is_root_list_type else base._element_type) allowed_field_types = PropertyDef else: element_type = None allowed_field_types = AbstractNodeData # Determine if this is a token node with node_ctx: is_token_node = dct.pop('token_node', None) check_source_language( is_token_node is None or isinstance(is_token_node, bool), 'The "token_node" field, when present, must contain a boolean') # If "token_node" allocation is left to None, inherit it (default # is False). if is_token_node is None: is_token_node = bool(base._is_token_node) if is_token_node: allowed_field_types = (_UserField, PropertyDef) else: # Make sure that all derivations of a token node are token # nodes themselves. check_source_language( not base._is_token_node, '"token_node" annotation inconsistent with inherited AST' ' node') # Handle enum nodes with node_ctx: # Forbid inheriting from an enum node check_source_language( not base._is_enum_node, 'Inheriting from an enum node is forbidden.') # Determine if this is an enum node is_enum_node = dct.pop('enum_node', False) check_source_language( isinstance(is_enum_node, bool), 'The "enum_node" field, when present, must contain a boolean') if is_enum_node: qualifier = dct.pop('qualifier', False) if qualifier: alternatives = ['present', 'absent'] else: alternatives = dct.pop('alternatives', None) check_source_language(alternatives is not None, 'Missing "alternatives" field') check_source_language( isinstance(alternatives, list) and all(isinstance(alt, str) for alt in alternatives), 'The "alternatives" field must contain a list of ' 'strings') alts = [ _EnumNodeAlternative(names.Name.from_lower(alt)) for alt in alternatives ] allowed_field_types = (_UserField, PropertyDef) fields = ASTNode.collect_fields(name, location, dct, allowed_field_types) DSLType._import_base_type_info(name, location, dct) if is_enum_node: mcs.import_enum_node_attributes(dct, qualifier, alts, fields) dct['_fields'] = fields dct['_base'] = base dct['_env_spec'] = env_spec dct['_is_token_node'] = is_token_node dct['_is_enum_node'] = is_enum_node # Make sure subclasses don't inherit the "list_type" cache from their # base classes. dct['_list_type'] = None dct['_element_type'] = element_type dct['_annotations'] = annotations cls = type.__new__(mcs, name, bases, dct) mcs.astnode_types.append(cls) # Create the corresponding ASTNodeType subclass if cls._base is _ASTNodeList: # Only root list types are supposed to directly subclass # _ASTNodeList. element_type = cls._element_type._resolve() assert element_type astnode_type = element_type.list else: astnode_type = ASTNodeType( cls._name, cls._location, cls._doc, base=None if is_root else cls._base._resolve(), fields=cls._fields, env_spec=cls._env_spec, annotations=cls._annotations, # Only enum nodes are abstract at this point is_abstract=cls._is_enum_node, is_enum_node=cls._is_enum_node, is_bool_node=cls._is_enum_node and cls._qualifier, is_token_node=cls._is_token_node) astnode_type.dsl_decl = cls cls._type = astnode_type if is_enum_node: mcs.create_enum_node_alternatives(cls, astnode_type) return cls