diff --git a/pyproject.toml b/pyproject.toml index 8ae7807..e7cb86a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,6 +10,7 @@ dependencies = [ "antlr4-python3-runtime >= 4.11, < 4.14", "colorama", "markdown", + "typing_extensions", ] authors = [ {name="Alex Mykyta"}, diff --git a/src/systemrdl/ast/cast.py b/src/systemrdl/ast/cast.py index 135d2b0..1a3693c 100644 --- a/src/systemrdl/ast/cast.py +++ b/src/systemrdl/ast/cast.py @@ -9,7 +9,7 @@ if TYPE_CHECKING: from ..compiler import RDLEnvironment from ..source_ref import SourceRefBase - from rdltypes.typing import PreElabRDLType + from ..rdltypes.typing import PreElabRDLType OptionalSourceRef = Optional[SourceRefBase] @@ -22,6 +22,7 @@ class WidthCast(ASTNode): def __init__(self, env: 'RDLEnvironment', src_ref: 'OptionalSourceRef', v: ASTNode, w_expr: Optional[ASTNode]=None, w_int: int=64): super().__init__(env, src_ref) + self.w_expr: Optional[ASTNode] if w_expr is not None: self.v = v self.w_expr = w_expr @@ -33,6 +34,7 @@ def __init__(self, env: 'RDLEnvironment', src_ref: 'OptionalSourceRef', v: ASTNo def predict_type(self) -> Type[int]: if self.cast_width is None: + assert self.w_expr is not None # mutually exclusive if not is_castable(self.w_expr.predict_type(), int): self.msg.fatal( "Width operand of cast expression is not a compatible numeric type", @@ -48,6 +50,7 @@ def predict_type(self) -> Type[int]: def get_min_eval_width(self) -> int: if self.cast_width is None: + assert self.w_expr is not None # mutually exclusive self.cast_width = int(self.w_expr.get_value()) return self.cast_width @@ -55,6 +58,7 @@ def get_min_eval_width(self) -> int: def get_value(self, eval_width: Optional[int]=None) -> int: # Truncate to cast width instead of eval width if self.cast_width is None: + assert self.w_expr is not None # mutually exclusive self.cast_width = int(self.w_expr.get_value()) if self.cast_width == 0: self.msg.fatal( diff --git a/src/systemrdl/ast/conditional.py b/src/systemrdl/ast/conditional.py index aab4cad..c9852d8 100644 --- a/src/systemrdl/ast/conditional.py +++ b/src/systemrdl/ast/conditional.py @@ -7,7 +7,7 @@ if TYPE_CHECKING: from ..compiler import RDLEnvironment from ..source_ref import SourceRefBase - from rdltypes.typing import PreElabRDLType + from ..rdltypes.typing import PreElabRDLType OptionalSourceRef = Optional[SourceRefBase] @@ -21,7 +21,7 @@ def __init__(self, env: 'RDLEnvironment', src_ref: 'OptionalSourceRef', i: ASTNo self.i = i self.j = j self.k = k - self.is_numeric = None # type: bool + self.is_numeric: bool = False def predict_type(self) -> 'PreElabRDLType': t_i = self.i.predict_type() @@ -35,7 +35,7 @@ def predict_type(self) -> 'PreElabRDLType': t_j = self.j.predict_type() t_k = self.k.predict_type() - typ = None # type: Any + typ: Any = None if is_castable(t_j, int) and is_castable(t_k, int): self.is_numeric = True typ = int diff --git a/src/systemrdl/ast/literals.py b/src/systemrdl/ast/literals.py index f1fc66a..405e438 100644 --- a/src/systemrdl/ast/literals.py +++ b/src/systemrdl/ast/literals.py @@ -10,7 +10,7 @@ if TYPE_CHECKING: from ..compiler import RDLEnvironment from ..source_ref import SourceRefBase - from rdltypes.typing import PreElabRDLType, RDLValue + from ..rdltypes.typing import PreElabRDLType, RDLValue OptionalSourceRef = Optional[SourceRefBase] diff --git a/src/systemrdl/ast/references.py b/src/systemrdl/ast/references.py index 3f78e0f..b237d3c 100644 --- a/src/systemrdl/ast/references.py +++ b/src/systemrdl/ast/references.py @@ -11,7 +11,7 @@ from ..compiler import RDLEnvironment from ..core.parameter import Parameter from ..source_ref import SourceRefBase - from rdltypes.typing import PreElabRDLType, RDLValue + from ..rdltypes.typing import PreElabRDLType OptionalSourceRef = Optional[SourceRefBase] @@ -59,7 +59,12 @@ def predict_type(self) -> 'PreElabRDLType': "Cannot index non-array type", self.array.src_ref ) - assert isinstance(array_type, rdltypes.ArrayedType) + if array_type.element_type is None: + # Array type is not known, therefore it must be an emptyy array. + self.msg.fatal( + "Array is empty. Cannot index", + self.array.src_ref + ) return array_type.element_type @@ -162,7 +167,8 @@ def predict_type(self) -> Type[comp.Component]: referenced. Also do some checks on the array indexes """ - current_comp = self.ref_root + current_comp: Optional[comp.Component] = self.ref_root + assert current_comp is not None for name, array_suffixes, name_src_ref in self.ref_elements: # find instance @@ -179,8 +185,7 @@ def predict_type(self) -> Type[comp.Component]: array_suffix.predict_type() # Check array suffixes - if (isinstance(current_comp, comp.AddressableComponent)) and current_comp.is_array: - assert isinstance(current_comp.array_dimensions, list) + if (isinstance(current_comp, comp.AddressableComponent)) and current_comp.array_dimensions: # is an array if len(array_suffixes) != len(current_comp.array_dimensions): self.msg.fatal( diff --git a/src/systemrdl/ast/relational.py b/src/systemrdl/ast/relational.py index f99d74b..5823f37 100644 --- a/src/systemrdl/ast/relational.py +++ b/src/systemrdl/ast/relational.py @@ -21,7 +21,7 @@ def __init__(self, env: 'RDLEnvironment', src_ref: 'OptionalSourceRef', l: ASTNo super().__init__(env, src_ref) self.l = l self.r = r - self.is_numeric = None # type: bool + self.is_numeric: bool = False def predict_type(self) -> Type[bool]: l_type = self.l.predict_type() diff --git a/src/systemrdl/ast/sequence.py b/src/systemrdl/ast/sequence.py index 34667e6..9e0cb38 100644 --- a/src/systemrdl/ast/sequence.py +++ b/src/systemrdl/ast/sequence.py @@ -13,7 +13,7 @@ class Concatenate(ASTNode): def __init__(self, env: 'RDLEnvironment', src_ref: 'OptionalSourceRef', elements: List[ASTNode]): super().__init__(env, src_ref) self.elements = elements - self.type = None # type: Union[Type[int], Type[str]] + self.type: Union[Type[int], Type[str]] = int def predict_type(self) -> Union[Type[int], Type[str]]: element_iter = iter(self.elements) @@ -73,11 +73,10 @@ def __init__(self, env: 'RDLEnvironment', src_ref: 'OptionalSourceRef', reps: AS super().__init__(env, src_ref) self.reps = reps self.concat = concat - self.type = None # type: Union[Type[int], Type[str]] - self.reps_value = None # type: int + self.type: Union[Type[int], Type[str]] = int + self.reps_value: Optional[int] = None - def predict_type(self): - # type: () -> Union[Type[int], Type[str]] + def predict_type(self) -> Union[Type[int], Type[str]]: if not is_castable(self.reps.predict_type(), int): self.msg.fatal( diff --git a/src/systemrdl/compiler.py b/src/systemrdl/compiler.py index 7f6ab25..b07a625 100644 --- a/src/systemrdl/compiler.py +++ b/src/systemrdl/compiler.py @@ -46,7 +46,6 @@ def included_files(self) -> Iterable[str]: class RDLCompiler: - def __init__(self, **kwargs: Any): """ RDLCompiler constructor. @@ -110,14 +109,14 @@ def __init__(self, **kwargs: Any): #: This will be deprecated in a future release. See this page for more details: https://github.com/SystemRDL/systemrdl-compiler/issues/168 self.msg = self.env.msg - self.namespace = NamespaceRegistry(self.env) # type: NamespaceRegistry - self.visitor = RootVisitor(self) - self.root = self.visitor.component # type: comp.Root # type: ignore + self.namespace: NamespaceRegistry = NamespaceRegistry(self.env) + self.visitor: RootVisitor = RootVisitor(self) + self.root = self.visitor.component def define_udp( self, name: str, valid_type: Any, - valid_components: 'Optional[Set[Type[comp.Component]]]'=None, + valid_components: Optional[Set[Type[comp.Component]]]=None, default: Any=None ) -> None: @@ -130,6 +129,15 @@ def define_udp( raise ValueError(f"UDP definition's name '{name}' conflicts with existing built-in RDL property") if name in self.env.property_rules.user_properties: raise ValueError(f"UDP '{name}' has already been defined") + if valid_components is None: + valid_components = { + comp.Field, + comp.Reg, + comp.Regfile, + comp.Addrmap, + comp.Mem, + comp.Signal, + } udp = LegacyExternalUserProperty( self.env, @@ -386,8 +394,10 @@ def elaborate(self, top_def_name: Optional[str]=None, inst_name: Optional[str]=N literal_expr = ast.ExternalLiteral(self.env, value) assign_expr = ast.AssignmentCast(self.env, None, literal_expr, parameter.param_type) - assign_type = assign_expr.predict_type() - if assign_type is None: + try: + # Try to predict type to test if ExternalLiteral maps to a valid RDL type + assign_expr.predict_type() + except ValueError: self.msg.fatal(f"Incorrect type for top-level parameter override of '{param_name}'") parameter.expr = assign_expr diff --git a/src/systemrdl/component.py b/src/systemrdl/component.py index 4c9da3c..8bfca1e 100644 --- a/src/systemrdl/component.py +++ b/src/systemrdl/component.py @@ -2,12 +2,13 @@ import functools from copy import deepcopy from collections import OrderedDict -from typing import Optional, List, Dict, TYPE_CHECKING, Any +from typing import Optional, List, Dict, TYPE_CHECKING, Any, Union if TYPE_CHECKING: from typing import TypeVar from .core.parameter import Parameter from .source_ref import SourceRefBase + from .ast import ASTNode ComponentClass = TypeVar('ComponentClass', bound='Component') @@ -32,14 +33,14 @@ def __init__(self) -> None: #: .. note:: #: This represents the parent *lexical* scope! This does *not* #: refer to the hierarchical parent of this component. - self.parent_scope = None # type: Component + self.parent_scope: Optional[Component] = None # Name of this component's declaration scope # This field is only valid in non-instantiated components (referenced - # via an instance's original_def) + # via an instance's original_def, or a component's parent_scope) # If declaration was anonymous, inherits the first instance's name, # otherwise it contains the original type name. - self._scope_name = None # type: str + self._scope_name: Optional[str] = None #: Named definition identifier. #: If declaration was anonymous, instantiation type names inherit @@ -53,7 +54,7 @@ def __init__(self) -> None: #: #: Importers may leave this as ``None`` if an appropriate type name #: cannot be imported. - self.type_name = None # type: Optional[str] + self.type_name: Optional[str] = None #: List of :class:`~systemrdl.component.Component` instances that are #: direct descendants of this component. @@ -64,50 +65,50 @@ def __init__(self) -> None: #: - All other components follow. #: - AddressableComponents are sorted by ascending base_addr #: - Fields are sorted by ascending low bit - self.children = [] # type: List[Component] + self.children: List[Component] = [] # Parameters of this component definition. # These are listed in the order that they were defined - self.parameters = [] # type: List[Parameter] + self.parameters: List[Parameter] = [] # Properties applied to this component - self.properties = {} # type: Dict[str, Any] + self.properties: Dict[str, Any] = {} #: :ref:`api_src_ref` for each explicit property assignment (if available) - self.property_src_ref = {} # type: Dict[str, SourceRefBase] + self.property_src_ref: Dict[str, SourceRefBase] = {} #: :ref:`api_src_ref` for the component definition - self.def_src_ref = None # type: Optional[SourceRefBase] + self.def_src_ref: Optional[SourceRefBase] = None #------------------------------ # Component instantiation #------------------------------ #: If instantiated, set to True - self.is_instance = False # type: bool + self.is_instance: bool = False #: Name of instantiated element - self.inst_name = None # type: Optional[str] + self.inst_name: Optional[str] = None #: Reference to original :class:`~systemrdl.component.Component` #: definition this instance is derived from. #: #: Importers may leave this as ``None`` if appropriate. - self.original_def = None # type: Optional[Component] + self.original_def: Optional[Component] = None #: True if instance type is external. False if internal. - self.external = None # type: bool + self.external: Optional[bool] = None #: :ref:`api_src_ref` for the component instantiation. - self.inst_src_ref = None # type: Optional[SourceRefBase] + self.inst_src_ref: Optional[SourceRefBase] = None #------------------------------ # List of property names that were assigned via a dynamic property # assignment. - self._dyn_assigned_props = [] # type: List[str] + self._dyn_assigned_props: List[str] = [] # List of child instances that were assigned "through" this component, # from outside this component's scope. - self._dyn_assigned_children = [] # type: List[str] + self._dyn_assigned_children: List[str] = [] def _copy_for_inst(self: 'ComponentClass', memo: Dict[int, Any]) -> 'ComponentClass': @@ -147,7 +148,7 @@ def __repr__(self) -> str: if self.is_instance: name_str = f"{self.inst_name} ({self.type_name})" else: - name_str = self.type_name + name_str = str(self.type_name) return f"<{self.__class__.__qualname__} {name_str} at {id(self):#x}>" @@ -212,6 +213,10 @@ def get_scope_path(self, scope_separator: str="::") -> Optional[str]: # Extend it with its scope name if parent_path: + # If parent scope exists, then its scope name is also guaranteed to + # exist + assert self.parent_scope._scope_name is not None + return( parent_path + scope_separator @@ -232,25 +237,26 @@ def __init__(self) -> None: # Component instantiation #------------------------------ #: Address offset from the parent component. - #: If left as None, compiler will resolve with inferred value. - self.addr_offset = None # type: int + #: If left as None, compiler will resolve with inferred value during + #: elaboration + self.addr_offset: Optional[int] = None #: Address alignment if explicitly assigned by user. - self.addr_align = None # type: Optional[int] + self.addr_align: Optional[int] = None #------------------------------ # Array Properties #------------------------------ #: If true, then ``array_dimensions`` and ``array_stride`` are valid. - self.is_array = False # type: bool + self.is_array: bool = False #: List of sizes for each array dimension. #: Last item in the list iterates the most frequently. - self.array_dimensions = None # type: Optional[List[int]] + self.array_dimensions: Optional[List[int]] = None #: Address offset between array elements. #: If left as None, compiler will resolve with inferred value. - self.array_stride = None # type: Optional[int] + self.array_stride: Optional[int] = None @property @@ -261,11 +267,21 @@ def n_elements(self) -> int: Returns 1 if not an array. """ if self.is_array: - assert isinstance(self.array_dimensions, list) + assert self.array_dimensions is not None return functools.reduce(operator.mul, self.array_dimensions) else: return 1 +class AddressableComponent_PreExprElab(AddressableComponent): + """ + Alternately typed representation for type hinting prior to expression + elaboration + """ + addr_offset: Optional[Union[int, 'ASTNode']] # type: ignore + addr_align: Optional[Union[int, 'ASTNode']] # type: ignore + array_stride: Optional[Union[int, 'ASTNode']] # type: ignore + + class VectorComponent(Component): """ Base class for all components that are vector-like @@ -276,18 +292,31 @@ def __init__(self) -> None: #------------------------------ # Component instantiation #------------------------------ + # Note: All of these are guaranteed to be resolved after elaboration to + # not be None + #: Width of vector in bits - self.width = None # type: int + self.width: Optional[int] = None #: Bit position of most significant bit - self.msb = None # type: int + self.msb: Optional[int] = None #: Bit position of least significant bit - self.lsb = None # type: int + self.lsb: Optional[int] = None #: High index of bit range - self.high = None # type: int + self.high: Optional[int] = None #: Low index of bit range - self.low = None # type: int + self.low: Optional[int] = None + + +class VectorComponent_PreExprElab(VectorComponent): + """ + Alternately typed representation for type hinting prior to expression + elaboration + """ + width: Optional[Union[int, 'ASTNode']] # type: ignore + msb: Optional[Union[int, 'ASTNode']] # type: ignore + lsb: Optional[Union[int, 'ASTNode']] # type: ignore #=============================================================================== class Root(Component): @@ -298,7 +327,7 @@ def __init__(self) -> None: super().__init__() #: Dictionary of :class:`~systemrdl.component.Component` definitions in #: the global root scope. - self.comp_defs = OrderedDict() # type: Dict[str, Component] + self.comp_defs: Dict[str, Component] = OrderedDict() class Signal(VectorComponent): pass @@ -313,22 +342,22 @@ def __init__(self) -> None: #: If true, fields are to be arranged in msb0 order #: #: .. versionadded:: 1.7 - self.is_msb0_order = False # type: bool + self.is_msb0_order: bool = False # List of register inst names that are aliases of this primary # Due to limitations in RDL grammar, these can only be siblings in the hierarchy # so names are unambiguous - self._alias_names = [] # type: List[str] + self._alias_names: List[str] = [] #------------------------------ # Alias Register #------------------------------ #: If true, then ``alias_primary_inst`` is valid - self.is_alias = False # type: bool + self.is_alias: bool = False #: Reference to primary register :class:`~systemrdl.component.Component` #: instance - self.alias_primary_inst = None # type: Optional[Reg] + self.alias_primary_inst: Optional[Reg] = None class Regfile(AddressableComponent): pass diff --git a/src/systemrdl/core/BaseVisitor.py b/src/systemrdl/core/BaseVisitor.py index 64caa92..f274278 100644 --- a/src/systemrdl/core/BaseVisitor.py +++ b/src/systemrdl/core/BaseVisitor.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Type, Union from .helpers import get_ID_text @@ -13,7 +13,6 @@ from ..compiler import RDLCompiler from antlr4.Token import CommonToken from antlr4 import ParserRuleContext - from typing import Union, Type class BaseVisitor(SystemRDLVisitor): @@ -42,8 +41,7 @@ def __init__(self, compiler: 'RDLCompiler') -> None: SystemRDLParser.FIELD_kw : comp.Field, SystemRDLParser.SIGNAL_kw : comp.Signal, } - def datatype_from_token(self, token): - # type: ('CommonToken') -> Type[Union[int, str, bool, rdltypes.BuiltinEnum, rdltypes.UserEnum, rdltypes.UserStruct, rdltypes.references.RefType, comp.Component]] + def datatype_from_token(self, token: 'CommonToken') -> Type[Union[int, str, bool, rdltypes.BuiltinEnum, rdltypes.UserEnum, rdltypes.UserStruct, rdltypes.references.RefType, comp.Component]]: """ Given a SystemRDLParser token, lookup the type This only includes types under the "data_type" grammar rule @@ -60,7 +58,7 @@ def datatype_from_token(self, token): ) if rdltypes.is_user_enum(typ) or rdltypes.is_user_struct(typ): - return typ # type: ignore + return typ else: self.msg.fatal( "Type '%s' is not a struct or enum" % get_ID_text(token), diff --git a/src/systemrdl/core/ComponentVisitor.py b/src/systemrdl/core/ComponentVisitor.py index c35641b..6d6cf3d 100644 --- a/src/systemrdl/core/ComponentVisitor.py +++ b/src/systemrdl/core/ComponentVisitor.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Dict, Tuple, List, Optional, Any +from typing import TYPE_CHECKING, Dict, Tuple, List, Optional, Any, cast from collections import OrderedDict from ..parser.SystemRDLParser import SystemRDLParser @@ -27,8 +27,9 @@ #=============================================================================== class ComponentVisitor(BaseVisitor): comp_type = comp.Component + component: comp.Component - def __init__(self, compiler: 'RDLCompiler', def_name: str=None, param_defs: List[Parameter]=None): + def __init__(self, compiler: 'RDLCompiler', def_name: Optional[str]=None, param_defs: Optional[List[Parameter]]=None): super().__init__(compiler) self.component = self.comp_type() #pylint: disable=not-callable @@ -43,7 +44,7 @@ def __init__(self, compiler: 'RDLCompiler', def_name: str=None, param_defs: List # { # prop_name : (prop_src_ref, prop_rhs) # } - self.property_dict = OrderedDict() # type: Dict[str, Tuple[SourceRefBase, Any]] + self.property_dict: Dict[str, Tuple[SourceRefBase, Any]] = OrderedDict() # Scratchpad of dynamic property assignments encountered in body of component # format is: @@ -52,13 +53,13 @@ def __init__(self, compiler: 'RDLCompiler', def_name: str=None, param_defs: List # prop_name : (prop_src_ref, prop_rhs) # } # } - self.dynamic_property_dict = OrderedDict() # type: Dict[comp.Component, Dict[str, Tuple[SourceRefBase, Any]]] + self.dynamic_property_dict: Dict[comp.Component, Dict[str, Tuple[SourceRefBase, Any]]] = OrderedDict() # Scratchpad to pass stuff down between visitor functions - self._tmp_comp_def = None # type: comp.Component - self._tmp_comp_inst = None # type: comp.Component - self._tmp_inst_type = None # type: CommonToken - self._tmp_alias_primary_inst = None # type: Optional[comp.Reg] + self._tmp_comp_def: comp.Component + self._tmp_comp_inst: comp.Component + self._tmp_inst_type: CommonToken + self._tmp_alias_primary_inst: Optional[comp.Reg] #--------------------------------------------------------------------------- # Component Definitions @@ -162,7 +163,7 @@ def check_comp_def_allowed(self, type_token: 'CommonToken') -> None: SystemRDLParser.SIGNAL_kw : comp.Signal, SystemRDLParser.MEM_kw : comp.Mem } - def define_component(self, body: SystemRDLParser.Component_bodyContext, type_token: 'CommonToken', def_name: str, param_defs: List[Parameter]) -> comp.Component: + def define_component(self, body: SystemRDLParser.Component_bodyContext, type_token: 'CommonToken', def_name: Optional[str], param_defs: List[Parameter]) -> comp.Component: """ Given component definition, recurse to another ComponentVisitor to define a new component @@ -337,7 +338,7 @@ def resolve_inst_external(self, ctx: SystemRDLParser.Component_instsContext, com comp_inst.external = None - def get_instance_assignment(self, ctx: 'ParserRuleContext') -> ast.ASTNode: + def get_instance_assignment(self, ctx: Optional['ParserRuleContext']) -> Optional[ast.ASTNode]: """ Gets the integer expression in any of the four instance assignment operators ('=' '@' '+=' '%=') @@ -462,12 +463,15 @@ def visitComponent_inst(self, ctx: SystemRDLParser.Component_instContext) -> Non comp_inst.properties['reset'] = field_inst_reset elif isinstance(comp_inst, comp.AddressableComponent): - comp_inst.addr_offset = inst_addr_fixed # type: ignore - comp_inst.addr_align = inst_addr_align # type: ignore + # Cast to pre-elaborated variant to satisfy type hinting + comp_inst = cast(comp.AddressableComponent_PreExprElab, comp_inst) + + comp_inst.addr_offset = inst_addr_fixed + comp_inst.addr_align = inst_addr_align if array_suffixes: comp_inst.is_array = True comp_inst.array_dimensions = array_suffixes - comp_inst.array_stride = inst_addr_stride # type: ignore + comp_inst.array_stride = inst_addr_stride else: raise RuntimeError @@ -510,9 +514,10 @@ def visitParam_def_elem(self, ctx: SystemRDLParser.Param_def_elemContext) -> Par # Construct parameter type data_type_token = self.visit(ctx.data_type()) param_data_type = self.datatype_from_token(data_type_token) + param_type: Union[Type[Union[int, str, bool, rdltypes.BuiltinEnum, rdltypes.UserEnum, rdltypes.UserStruct, rdltypes.references.RefType, comp.Component]], rdltypes.ArrayedType] if ctx.array_type_suffix() is None: # Non-array type - param_type = param_data_type # type: Union[Type[Union[int, str, bool, rdltypes.BuiltinEnum, rdltypes.UserEnum, rdltypes.UserStruct, rdltypes.references.RefType, comp.Component]], rdltypes.ArrayedType] + param_type = param_data_type else: # Array-like type param_type = rdltypes.ArrayedType(param_data_type) @@ -542,7 +547,7 @@ def visitParam_def_elem(self, ctx: SystemRDLParser.Param_def_elemContext) -> Par return param - def visitParam_inst(self, ctx: SystemRDLParser.Param_instContext) -> Dict[str, Tuple[ast.ASTNode, SourceRefBase]]: + def visitParam_inst(self, ctx: SystemRDLParser.Param_instContext) -> Dict[str, Tuple[ast.ASTNode, Optional[SourceRefBase]]]: param_assigns = {} for assignment in ctx.getTypedRuleContexts(SystemRDLParser.Param_assignmentContext): param_name, assign_expr = self.visit(assignment) @@ -641,13 +646,15 @@ def visitDynamic_property_assignment(self, ctx: SystemRDLParser.Dynamic_property target_inst._dyn_assigned_children.append(inst_name) # Traverse to next child in token list - target_inst = target_inst.get_child_by_name(inst_name) - if target_inst is None: + next_target_inst = target_inst.get_child_by_name(inst_name) + if next_target_inst is None: # Not found! self.msg.fatal( "Could not resolve hierarchical reference to '%s'" % inst_name, src_ref_from_antlr(name_token) ) + else: + target_inst = next_target_inst # Add assignment to dynamic_property_dict target_inst_dict = self.dynamic_property_dict.get(target_inst, OrderedDict()) @@ -684,7 +691,7 @@ def visitInstance_ref_element(self, ctx: SystemRDLParser.Instance_ref_elementCon return name_token - def visitNormal_prop_assign(self, ctx: SystemRDLParser.Normal_prop_assignContext) -> Tuple[SourceRefBase, str, Optional[ast.ASTNode]]: + def visitNormal_prop_assign(self, ctx: SystemRDLParser.Normal_prop_assignContext) -> Tuple[Optional[SourceRefBase], str, Optional[ast.ASTNode]]: # Get property string if ctx.prop_keyword() is not None: @@ -726,7 +733,7 @@ def visitEncode_prop_assign(self, ctx: SystemRDLParser.Encode_prop_assignContext return src_ref_from_antlr(prop_token), prop_name, rhs # type: ignore - def visitProp_mod_assign(self, ctx: SystemRDLParser.Prop_mod_assignContext) -> Tuple[SourceRefBase, str]: + def visitProp_mod_assign(self, ctx: SystemRDLParser.Prop_mod_assignContext) -> Tuple[Optional[SourceRefBase], str]: prop_mod_token = self.visit(ctx.prop_mod()) prop_mod = prop_mod_token.text @@ -766,7 +773,7 @@ def apply_local_properties(self) -> None: rule.assign_value(self.component, prop_rhs, prop_src_ref) # Apply locally-assigned properties - mutex_bins = {} # type: Dict[str, str] + mutex_bins: Dict[str, str] = {} for prop_name, (prop_src_ref, prop_rhs) in self.property_dict.items(): rule = self.compiler.env.property_rules.lookup_property(prop_name) if rule is None: @@ -797,7 +804,7 @@ def apply_local_properties(self) -> None: def apply_dynamic_properties(self) -> None: for target_inst, target_inst_dict in self.dynamic_property_dict.items(): - mutex_bins = {} # type: Dict[str, str] + mutex_bins: Dict[str, str] = {} for prop_name, (prop_src_ref, prop_rhs) in target_inst_dict.items(): rule = self.compiler.env.property_rules.lookup_property(prop_name) if rule is None: @@ -902,6 +909,7 @@ def visitConstraint_def(self, ctx: SystemRDLParser.Constraint_defContext) -> Non #=============================================================================== class RootVisitor(ComponentVisitor): comp_type = comp.Root + component: comp.Root def visitRoot(self, ctx: SystemRDLParser.RootContext) -> None: self.visitChildren(ctx) @@ -917,11 +925,11 @@ def visitLocal_property_assignment(self, ctx: SystemRDLParser.Local_property_ass super().visitLocal_property_assignment(ctx) - def define_component(self, body: SystemRDLParser.Component_bodyContext, type_token: 'CommonToken', def_name: str, param_defs: List[Parameter]) -> comp.Component: + def define_component(self, body: SystemRDLParser.Component_bodyContext, type_token: 'CommonToken', def_name: Optional[str], param_defs: List[Parameter]) -> comp.Component: comp_def = super().define_component(body, type_token, def_name, param_defs) if def_name is not None: - self.component.comp_defs[def_name] = comp_def # type: ignore + self.component.comp_defs[def_name] = comp_def return comp_def @@ -959,6 +967,7 @@ def visitUdp_def(self, ctx: SystemRDLParser.Udp_defContext) -> None: #=============================================================================== class FieldComponentVisitor(ComponentVisitor): comp_type = comp.Field + component: comp.Field def visitComponent_insts(self, ctx: SystemRDLParser.Component_instsContext) -> None: # Unpack instance def info from parent @@ -992,6 +1001,7 @@ def check_comp_def_allowed(self, type_token: 'CommonToken') -> None: #=============================================================================== class RegComponentVisitor(ComponentVisitor): comp_type = comp.Reg + component: comp.Reg def visitComponent_insts(self, ctx: SystemRDLParser.Component_instsContext) -> None: # Unpack instance def info from parent @@ -1022,6 +1032,7 @@ def check_comp_def_allowed(self, type_token: 'CommonToken') -> None: #=============================================================================== class RegfileComponentVisitor(ComponentVisitor): comp_type = comp.Regfile + component: comp.Regfile def visitComponent_insts(self, ctx: SystemRDLParser.Component_instsContext) -> None: # Unpack instance def info from parent @@ -1051,6 +1062,7 @@ def check_comp_def_allowed(self, type_token: 'CommonToken') -> None: #=============================================================================== class AddrmapComponentVisitor(ComponentVisitor): comp_type = comp.Addrmap + component: comp.Addrmap def visitComponent_insts(self, ctx: SystemRDLParser.Component_instsContext) -> None: # Unpack instance def info from parent @@ -1071,6 +1083,7 @@ def visitComponent_insts(self, ctx: SystemRDLParser.Component_instsContext) -> N #=============================================================================== class MemComponentVisitor(ComponentVisitor): comp_type = comp.Mem + component: comp.Mem def visitComponent_insts(self, ctx: SystemRDLParser.Component_instsContext) -> None: # Unpack instance def info from parent @@ -1100,6 +1113,7 @@ def check_comp_def_allowed(self, type_token: 'CommonToken') -> None: #=============================================================================== class SignalComponentVisitor(ComponentVisitor): comp_type = comp.Signal + component: comp.Signal def visitComponent_insts(self, ctx: SystemRDLParser.Component_instsContext) -> None: self.msg.fatal( diff --git a/src/systemrdl/core/EnumVisitor.py b/src/systemrdl/core/EnumVisitor.py index b34dc2e..91743c1 100644 --- a/src/systemrdl/core/EnumVisitor.py +++ b/src/systemrdl/core/EnumVisitor.py @@ -1,4 +1,4 @@ -from typing import List, TYPE_CHECKING, Tuple, Any, Optional +from typing import List, TYPE_CHECKING, Tuple, Any, Optional, Type from ..parser.SystemRDLParser import SystemRDLParser @@ -16,7 +16,6 @@ from ..source_ref import SourceRefBase from ..compiler import RDLCompiler from .. import component as comp - from typing import Type class EnumVisitor(BaseVisitor): @@ -24,8 +23,7 @@ def __init__(self, compiler: 'RDLCompiler', parent_component: 'comp.Component') super().__init__(compiler) self.parent_component = parent_component - def visitEnum_def(self, ctx): - # type: (SystemRDLParser.Enum_defContext) -> Tuple[Type[rdltypes.UserEnum], str, SourceRefBase] + def visitEnum_def(self, ctx: SystemRDLParser.Enum_defContext) -> Tuple[Type[rdltypes.UserEnum], str, Optional['SourceRefBase']]: self.compiler.namespace.enter_scope() # Allowing enum definitions to access Parameters from the parent scope @@ -37,9 +35,9 @@ def visitEnum_def(self, ctx): enum_name = get_ID_text(ctx.ID()) # Collect members - entry_values = [] # type: List[int] - entry_names = [] # type: List[str] - members = [] # type: List[rdltypes.UserEnumMemberContainer] + entry_values: List[int] = [] + entry_names: List[str] = [] + members: List[rdltypes.UserEnumMemberContainer] = [] for enum_entry_ctx in ctx.getTypedRuleContexts(SystemRDLParser.Enum_entryContext): name_token, value_expr_ctx, rdl_name, rdl_desc = self.visit(enum_entry_ctx) @@ -96,8 +94,8 @@ def visitEnum_entry(self, ctx: SystemRDLParser.Enum_entryContext) -> Tuple['Comm name_token = ctx.ID() value_expr_ctx = ctx.expr() - rdl_name = None # type: Optional[str] - rdl_desc = None # type: Optional[str] + rdl_name: Optional[str] = None + rdl_desc: Optional[str] = None for pa_ctx in ctx.getTypedRuleContexts(SystemRDLParser.Enum_prop_assignContext): prop_token, prop_value = self.visit(pa_ctx) diff --git a/src/systemrdl/core/ExprVisitor.py b/src/systemrdl/core/ExprVisitor.py index c8d3c79..7b7c042 100644 --- a/src/systemrdl/core/ExprVisitor.py +++ b/src/systemrdl/core/ExprVisitor.py @@ -374,7 +374,7 @@ def visitInstance_ref(self, ctx: SystemRDLParser.Instance_refContext) -> ast.AST if isinstance(first_elem, Parameter): # Reference is to a local parameter - ref_expr = ast.ParameterRef(self.compiler.env, first_name_src_ref, first_elem) # type: ast.ASTNode + ref_expr: ast.ASTNode = ast.ParameterRef(self.compiler.env, first_name_src_ref, first_elem) # Wrap ref_expr with array/struct dereferencers as appropriate for array_suffix in first_array_suffixes: diff --git a/src/systemrdl/core/UDPVisitor.py b/src/systemrdl/core/UDPVisitor.py index ae780ae..ad16a39 100644 --- a/src/systemrdl/core/UDPVisitor.py +++ b/src/systemrdl/core/UDPVisitor.py @@ -22,10 +22,10 @@ class UDPVisitor(BaseVisitor): def __init__(self, compiler: 'RDLCompiler') -> None: super().__init__(compiler) - self._constr_componentwidth = None # type: Optional[bool] - self._default_assignment = None # type: Any - self._valid_type = None # type: Optional[Type[Union[int, str, bool, rdltypes.BuiltinEnum, rdltypes.UserEnum, rdltypes.UserStruct, comp.Component, rdltypes.references.RefType]]] - self._bindable_to = None # type: Optional[Set[Type[comp.Component]]] + self._constr_componentwidth: Optional[bool] = None + self._default_assignment: Any = None + self._valid_type: Optional[Type[Union[int, str, bool, rdltypes.BuiltinEnum, rdltypes.UserEnum, rdltypes.UserStruct, comp.Component, rdltypes.references.RefType]]] = None + self._bindable_to: Optional[Set[Type[comp.Component]]] = None def visitUdp_def(self, ctx: SystemRDLParser.Udp_defContext) -> None: udp_name = get_ID_text(ctx.ID()) @@ -146,7 +146,7 @@ def visitUdp_usage(self, ctx: SystemRDLParser.Udp_usageContext) -> None: src_ref_from_antlr(ctx.COMPONENT_kw()) ) - comp_types = [] # type: List[Type[comp.Component]] + comp_types: List[Type[comp.Component]] = [] for token_ctx in ctx.getTypedRuleContexts(SystemRDLParser.Udp_comp_typeContext): token = self.visit(token_ctx) if token.type == SystemRDLParser.ALL_kw: diff --git a/src/systemrdl/core/elaborate.py b/src/systemrdl/core/elaborate.py index 9724cee..25327ce 100644 --- a/src/systemrdl/core/elaborate.py +++ b/src/systemrdl/core/elaborate.py @@ -1,5 +1,5 @@ import hashlib -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING, List, Optional, cast from ..ast import ASTNode from .helpers import is_pow2, roundup_pow2, roundup_to @@ -43,7 +43,9 @@ def enter_Component(self, node: Node) -> None: def enter_AddressableComponent(self, node: AddressableNode) -> None: - assert isinstance(node.inst, comp.AddressableComponent) + # Cast to pre-elaborated variant to satisfy type hinting + node.inst = cast(comp.AddressableComponent_PreExprElab, node.inst) + # Evaluate instance object expressions if isinstance(node.inst.addr_offset, ASTNode): node.inst.addr_offset = node.inst.addr_offset.get_value() @@ -76,7 +78,9 @@ def enter_AddressableComponent(self, node: AddressableNode) -> None: def enter_VectorComponent(self, node: VectorNode) -> None: - assert isinstance(node.inst, comp.VectorComponent) + # Cast to pre-elaborated variant to satisfy type hinting + node.inst = cast(comp.VectorComponent_PreExprElab, node.inst) + # Evaluate instance object expressions if isinstance(node.inst.width, ASTNode): node.inst.width = node.inst.width.get_value() @@ -208,9 +212,9 @@ class StructuralPlacementListener(walker.RDLListener): def __init__(self, msg_handler: 'MessageHandler'): self.msg = msg_handler - self.msb0_mode_stack = [] # type: List[bool] - self.addressing_mode_stack = [] # type: List[rdltypes.AddressingType] - self.alignment_stack = [] # type: List[Optional[int]] + self.msb0_mode_stack: List[bool] = [] + self.addressing_mode_stack: List[rdltypes.AddressingType] = [] + self.alignment_stack: List[Optional[int]] = [] self.max_vreg_width = 0 @@ -250,10 +254,9 @@ def exit_Mem(self, node: MemNode) -> None: def exit_Field(self, node: FieldNode) -> None: - assert isinstance(node.inst, comp.Field) # Resolve field width if node.inst.width is None: - fieldwidth = node.get_property('fieldwidth') + fieldwidth = node.get_property('fieldwidth', default=None) if (node.inst.lsb is not None) and (node.inst.msb is not None): width = abs(node.inst.msb - node.inst.lsb) + 1 @@ -275,11 +278,9 @@ def exit_Field(self, node: FieldNode) -> None: def exit_Signal(self, node: SignalNode) -> None: - assert isinstance(node.inst, comp.Signal) - # Resolve signal width if node.inst.width is None: - signalwidth = node.get_property('signalwidth') + signalwidth = node.get_property('signalwidth', default=None) if signalwidth is not None: node.inst.width = signalwidth @@ -303,8 +304,6 @@ def exit_Signal(self, node: SignalNode) -> None: def exit_Reg(self, node: RegNode) -> None: - assert isinstance(node.inst, comp.Reg) - regwidth = node.get_property('regwidth') self.max_vreg_width = max(regwidth, self.max_vreg_width) @@ -347,11 +346,13 @@ def exit_Reg(self, node: RegNode) -> None: # Assign field positions # Children are iterated in order of declaration - prev_inst = None # type: Optional[comp.Field] + prev_inst: Optional[comp.Field] = None for inst in node.inst.children: if not isinstance(inst, comp.Field): continue + assert inst.width is not None # Width was resolved in field visit + if (inst.lsb is None) or (inst.msb is None): # Offset is not known @@ -369,9 +370,12 @@ def exit_Reg(self, node: RegNode) -> None: if prev_inst is None: inst.lsb = regwidth - 1 else: + assert prev_inst.msb is not None inst.lsb = prev_inst.msb - 1 + assert inst.lsb is not None inst.msb = inst.lsb - inst.width + 1 + assert inst.msb is not None if inst.msb < 0: node.env.msg.fatal( @@ -386,9 +390,12 @@ def exit_Reg(self, node: RegNode) -> None: if prev_inst is None: inst.lsb = 0 else: + assert prev_inst.msb is not None inst.lsb = prev_inst.msb + 1 + assert inst.lsb is not None inst.msb = inst.lsb + inst.width - 1 + assert inst.msb is not None inst.high = max(inst.msb, inst.lsb) inst.low = min(inst.msb, inst.lsb) prev_inst = inst @@ -399,6 +406,7 @@ def get_field_sort_key(inst: comp.Component) -> int: if not isinstance(inst, comp.Field): return -1 else: + assert inst.low is not None return inst.low node.inst.children.sort(key=get_field_sort_key) @@ -419,7 +427,6 @@ def exit_Addrmap(self, node: AddrmapNode) -> None: def exit_AddressableComponent(self, node: AddressableNode) -> None: - assert isinstance(node.inst, comp.AddressableComponent) # Resolve array stride if needed if node.inst.is_array and (node.inst.array_stride is None): node.inst.array_stride = node.size @@ -448,7 +455,6 @@ def resolve_addresses(self, node: AddressableNode, is_bridge: bool = False) -> N for child_node in node.children(skip_not_present=False): if not isinstance(child_node, AddressableNode): continue - assert isinstance(child_node.inst, comp.AddressableComponent) if child_node.inst.addr_offset is not None: # Address is already known. Do not need to infer @@ -519,6 +525,7 @@ def get_child_sort_key(inst: comp.Component) -> int: if not isinstance(inst, comp.AddressableComponent): return -1 else: + assert inst.addr_offset is not None return inst.addr_offset node.inst.children.sort(key=get_child_sort_key) @@ -530,10 +537,10 @@ class LateElabListener(walker.RDLListener): def __init__(self, msg_handler: 'MessageHandler', env: 'RDLEnvironment'): self.msg = msg_handler self.env = env - self.coerce_external_to = None # type: Optional[bool] - self.coerce_end_regfile = None # type: Optional[Node] + self.coerce_external_to: Optional[bool] = None + self.coerce_end_regfile: Optional[Node] = None - self.node_needs_revisit = [] # type: List[Node] + self.node_needs_revisit: List[Node] = [] def enter_Component(self, node: Node) -> None: @@ -565,15 +572,13 @@ def enter_Regfile(self, node: RegfileNode) -> None: def enter_Reg(self, node: RegNode) -> None: - assert isinstance(node.inst, comp.Reg) - if self.coerce_external_to is not None: # Is nested inside regfile that is coercing to a particular inst type node.inst.external = self.coerce_external_to elif node.inst.external is None: if node.inst.is_alias: # inherit internal/external instance type from alias primary - assert isinstance(node.inst.alias_primary_inst, comp.Reg) + assert node.inst.alias_primary_inst is not None if node.inst.alias_primary_inst.external is not None: node.inst.external = node.inst.alias_primary_inst.external else: @@ -585,7 +590,7 @@ def enter_Reg(self, node: RegNode) -> None: # Register aliases with their primary register if node.inst.is_alias: - assert isinstance(node.inst.alias_primary_inst, comp.Reg) + assert node.inst.alias_primary_inst is not None node.inst.alias_primary_inst._alias_names.append(node.inst_name) @@ -628,6 +633,7 @@ def exit_Component(self, node: Node) -> None: for child_name in node.inst._dyn_assigned_children: child = node.inst.get_child_by_name(child_name) assert child is not None + assert child.inst_name is not None # _ if child.type_name is not None: @@ -670,9 +676,8 @@ def revisit_reg(self, node: RegNode) -> None: # Resolve alias register's instance type if it was not possible to do so earlier. # By now, its primary would have been resolved. if node.inst.external is None: - assert isinstance(node.inst, comp.Reg) if node.inst.is_alias: - assert isinstance(node.inst.alias_primary_inst, comp.Reg) + assert node.inst.alias_primary_inst is not None if node.inst.alias_primary_inst.external is not None: node.inst.external = node.inst.alias_primary_inst.external else: diff --git a/src/systemrdl/core/namespace.py b/src/systemrdl/core/namespace.py index 434f6f0..401e228 100644 --- a/src/systemrdl/core/namespace.py +++ b/src/systemrdl/core/namespace.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Dict, List, Union, Type, Tuple +from typing import TYPE_CHECKING, Dict, List, Union, Type, Tuple, Optional from .parameter import Parameter @@ -15,7 +15,7 @@ TypeNSScope = Dict[str, TypeNSEntry] ElementNSRef = Union[comp.Component, Parameter] -ElementNSEntry = Tuple[ElementNSRef, comp.Component] +ElementNSEntry = Tuple[ElementNSRef, Optional[comp.Component]] ElementNSScope = Dict[str, ElementNSEntry] DefaultNSRef = Union['ASTNode', bool, 'rdltypes.InterruptType'] @@ -28,14 +28,14 @@ def __init__(self, env: 'RDLEnvironment'): self.env = env self.msg = env.msg - self.type_ns_stack = [{}] # type: List[TypeNSScope] - self.element_ns_stack = [{}] # type: List[ElementNSScope] - self.default_property_ns_stack = [{}] # type: List[DefaultNSScope] + self.type_ns_stack: List[TypeNSScope] = [{}] + self.element_ns_stack: List[ElementNSScope] = [{}] + self.default_property_ns_stack: List[DefaultNSScope] = [{}] # Control if Parameter objects are visible from parent scopes self.parent_parameters_visible = True - def register_type(self, name: str, ref: TypeNSRef, src_ref: 'SourceRefBase') -> None: + def register_type(self, name: str, ref: TypeNSRef, src_ref: Optional['SourceRefBase']) -> None: if name in self.type_ns_stack[-1]: self.msg.fatal( "Multiple declarations of type '%s'" % name, @@ -43,7 +43,7 @@ def register_type(self, name: str, ref: TypeNSRef, src_ref: 'SourceRefBase') -> ) self.type_ns_stack[-1][name] = ref - def register_element(self, name: str, ref: ElementNSRef, parent_comp_def: comp.Component, src_ref: 'SourceRefBase') -> None: + def register_element(self, name: str, ref: ElementNSRef, parent_comp_def: Optional[comp.Component], src_ref: Optional['SourceRefBase']) -> None: if name in self.element_ns_stack[-1]: self.msg.fatal( "Multiple declarations of instance '%s'" % name, @@ -61,13 +61,13 @@ def register_default_property(self, name: str, ref: DefaultNSRef, src_ref: 'Sour self.default_property_ns_stack[-1][name] = (src_ref, ref) - def lookup_type(self, name: str) -> TypeNSEntry: + def lookup_type(self, name: str) -> Optional[TypeNSEntry]: for scope in reversed(self.type_ns_stack): if name in scope: return scope[name] return None - def lookup_element(self, name: str) -> ElementNSEntry: + def lookup_element(self, name: str) -> Union[ElementNSEntry, Tuple[None, None]]: """ Look up the element (component instance, or parameter) visible in the current scope. @@ -93,8 +93,7 @@ def lookup_element(self, name: str) -> ElementNSEntry: return (el, parent_def) return (None, None) - def get_default_properties(self, comp_type): - # type: (Type[comp.Component]) -> DefaultNSScope + def get_default_properties(self, comp_type: Type[comp.Component]) -> DefaultNSScope: """ Returns a flattened dictionary of all default property assignments visible in the current scope that apply to the current component type. diff --git a/src/systemrdl/core/parameter.py b/src/systemrdl/core/parameter.py index 05a6a01..4632ded 100644 --- a/src/systemrdl/core/parameter.py +++ b/src/systemrdl/core/parameter.py @@ -6,6 +6,7 @@ if TYPE_CHECKING: from .. import rdltypes from ..ast import ASTNode + from ..node import Node class Parameter: def __init__(self, param_type: 'rdltypes.PreElabRDLType', name: str, default_expr: Optional['ASTNode']=None): @@ -16,7 +17,7 @@ def __init__(self, param_type: 'rdltypes.PreElabRDLType', name: str, default_exp # Stores the evaluated result of self.expr so that subsequent queries do # not need to repeatedly re-evaluate it - self._value = None # type: Any + self._value: Any = None def __deepcopy__(self, memo: Dict[int, Any]) -> 'Parameter': diff --git a/src/systemrdl/core/rdlformatcode.py b/src/systemrdl/core/rdlformatcode.py index a6916c4..49438f2 100755 --- a/src/systemrdl/core/rdlformatcode.py +++ b/src/systemrdl/core/rdlformatcode.py @@ -88,35 +88,43 @@ def rdlfc_to_html(text: str, node: Optional[Node]=None, md: Optional['Markdown'] text_segs.append("") elif m.lastgroup == 'color': m2 = re.match(r'\[color=([^\]]+)\]', m.group(0)) + assert m2 is not None text_segs.append('' % m2.group(1)) elif m.lastgroup == 'xcolor': text_segs.append('') elif m.lastgroup == 'size': m2 = re.match(r'\[size=([^\]]+)\]', m.group(0)) + assert m2 is not None text_segs.append('' % m2.group(1)) elif m.lastgroup == 'xsize': text_segs.append('') elif m.lastgroup == 'plainurl': m2 = re.match(r'\[url\](.*?)\[/url\]', m.group(0), re.DOTALL) + assert m2 is not None text_segs.append('%s' % (m2.group(1).strip(), m2.group(1).strip())) elif m.lastgroup == 'url': m2 = re.match(r'\[url=([^\]]+)\]', m.group(0)) + assert m2 is not None text_segs.append('' % m2.group(1).strip()) elif m.lastgroup == 'xurl': text_segs.append('') elif m.lastgroup == 'email': m2 = re.match(r'\[email\](.*?)\[/email\]', m.group(0), re.DOTALL) + assert m2 is not None text_segs.append('%s' % (m2.group(1).strip(), m2.group(1).strip())) elif m.lastgroup == 'img': m2 = re.match(r'\[img\](.*?)\[/img\]', m.group(0), re.DOTALL) + assert m2 is not None text_segs.append('' % m2.group(1)) elif m.lastgroup == 'code': m2 = re.match(r'\[code\](.*?)\s*\[/code\]', m.group(0), re.DOTALL) + assert m2 is not None text_segs.append('%s' % m2.group(1)) elif m.lastgroup == 'list': # List start tag m2 = re.match(r'\[list(?:=([^\]]+))?\]', m.group(0)) + assert m2 is not None ltype = m2.group(1) if ltype is None: text_segs.append('