<commit_before>from copy import deepcopy

from antlr4 import CommonTokenStream

from . import messages
from .parser.SystemRDLLexer import SystemRDLLexer
from .parser.SystemRDLParser import SystemRDLParser
from .core.ComponentVisitor import RootVisitor
from .core.properties import PropertyRuleBook
from .core.namespace import NamespaceRegistry
from .core.elaborate import ElabExpressionsListener, PrePlacementValidateListener, LateElabListener
from .core.elaborate import StructuralPlacementListener
from .core.validate import ValidateListener
from . import component as comp
from . import walker
from .node import RootNode
from .preprocessor import preprocessor

class RDLCompiler:
    
    def __init__(self, **kwargs):
        """
        RDLCompiler constructor.
        
        Parameters
        ----------
        message_printer: :class:`~systemrdl.messages.MessagePrinter`
            Override the default message printer
        """
        
        # Collect arguments
        message_printer = kwargs.pop('message_printer', messages.MessagePrinter())
        if kwargs:
            raise TypeError("got an unexpected keyword argument '%s'" % list(kwargs.keys())[0])
        
        self.env = Environment()
        
        # Set up message handling
        self.msg = messages.MessageHandler(message_printer)
        
        self.env.msg = self.msg
        
        self.namespace = NamespaceRegistry(self.env)
        self.property_rules = PropertyRuleBook(self.env)
        self.env.property_rules = self.property_rules
        
        self.visitor = RootVisitor(self)
        self.root = self.visitor.component
    
    
    def compile_file(self, path, incl_search_paths=None):
        """
        Parse & compile a single file and append it to RDLCompiler's root
        namespace.
        
        If any exceptions (:class:`~systemrdl.messages.RDLCompileError` or other)
        occur during compilation, then the RDLCompiler object should be discarded.
        
        Parameters
        ----------
        path:str
            Path to an RDL source file
        
        incl_search_paths:list
            List of additional paths to search to resolve includes.
            If unset, defaults to an empty list.
            
            Relative include paths are resolved in the following order:
            
            1. Search each path specified in ``incl_search_paths``.
            2. Path relative to the source file performing the include.
            
        Raises
        ------<commit_msg>Add ability to define UDPs from Python API<commit_after>from copy import deepcopy

from antlr4 import CommonTokenStream

from . import messages
from .parser.SystemRDLLexer import SystemRDLLexer
from .parser.SystemRDLParser import SystemRDLParser
from .core.ComponentVisitor import RootVisitor
from .core.properties import PropertyRuleBook, UserProperty
from .core.namespace import NamespaceRegistry
from .core.elaborate import ElabExpressionsListener, PrePlacementValidateListener, LateElabListener
from .core.elaborate import StructuralPlacementListener
from .core.validate import ValidateListener
from . import component as comp
from . import walker
from .node import RootNode
from .preprocessor import preprocessor

class RDLCompiler:
    
    def __init__(self, **kwargs):
        """
        RDLCompiler constructor.
        
        Parameters
        ----------
        message_printer: :class:`~systemrdl.messages.MessagePrinter`
            Override the default message printer
        """
        
        # Collect arguments
        message_printer = kwargs.pop('message_printer', messages.MessagePrinter())
        if kwargs:
            raise TypeError("got an unexpected keyword argument '%s'" % list(kwargs.keys())[0])
        
        self.env = Environment()
        
        # Set up message handling
        self.msg = messages.MessageHandler(message_printer)
        
        self.env.msg = self.msg
        
        self.namespace = NamespaceRegistry(self.env)
        self.property_rules = PropertyRuleBook(self.env)
        self.env.property_rules = self.property_rules
        
        self.visitor = RootVisitor(self)
        self.root = self.visitor.component
    
    def define_udp(self, name, valid_type, valid_components=None, default=None):
        """
        Pre-define a user-defined property.
        
        This is the equivalent to the following RDL:
        
        .. code-block:: none
            
            property <name> {
                type = <valid_type>;
                component = <valid_components>;
                default = <default>
            };
        
        Parameters:
        -----------
        name: str
            Property name
        valid_components: list
            List of :class:`~systemrdl.component.Component` types the UDP can be bound to.
            If None, then UDP can be bound to all components.
        valid_type: type
            Assignment type that this UDP will enforce
        default:
            Default if a value is not specified when the UDP is bound to a component.
            Value must be compatible with ``valid_type``
            
        """
        if valid_components is None:
            valid_components = [
                comp.Field,
                comp.Reg,
                comp.Regfile,
                comp.Addrmap,
                comp.Mem,
                comp.Signal,
                #TODO constraint,
            ]
        
        if name in self.env.property_rules.rdl_properties:
            raise ValueError("name '%s' conflicts with existing built-in RDL property")
        
        udp = UserProperty(self.env, name, valid_components, [valid_type], default)
        
        self.env.property_rules.user_properties[udp.name] = udp
        
    
    def compile_file(self, path, incl_search_paths=None):
        """
        Parse & compile a single file and append it to RDLCompiler's root
        namespace.
        
        If any exceptions (:class:`~systemrdl.messages.RDLCompileError` or other)
        occur during compilation, then the RDLCompiler object should be discarded.
        
        Parameters
        ----------
        path:str
            Path to an RDL source file
        
        incl_search_paths:list
            List of additional paths to search to resolve includes.
            If unset, defaults to an empty list.
            
            Relative include paths are resolved in the following order:
            
            1. Search each path specified in ``incl_search_paths``.
            2. Path relative to the source file performing the include.
            
        Raises
        ------
{'fraction_non_alphanumeric': 0.06992064680341369, 'fraction_numerical': 0.0011977840994160803, 'mean_word_length': 1.978154257690593, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3355826', 'n_tokens_mistral': 1642, 'n_tokens_neox': 1628, 'n_words': 518}