Changeset 425

Show
Ignore:
Timestamp:
05/18/07 11:47:29 (2 years ago)
Author:
athomas
Message:

cly:

  • Renamed cly.variables to cly.types.
  • Node now has some dictionary-like operators allowing manipulation of the
    Grammar after construction.
  • Made Interact somewhat extensible via subclassing.
  • Interact messages are now more contextual, displaying at the cursor
    location.
  • Added example code from video.
  • Added cly.Context.parsed which contains the text successfully parsed in
    the context.
  • Console error/fatal/warning/info functions no longer prefix their output.
Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • cly/trunk/cly/console.py

    r423 r425  
    4545 
    4646 
    47 _decode_re = re.compile(r'''[^^]+|\^([N0-7BU])''') 
     47_decode_re = re.compile(r'''\^([N0-7BU])|.''') 
    4848_encode_re = re.compile(r'''\033(?:[^[]|$)|\033\[(.*?)m''') 
    4949_cprint_strip = re.compile(r'''\^([N0-7BU])''') 
    50 _cwrap_re = re.compile(r'''(\n)|(\s+)|((?:\^[N0-7BU]|\S)+\b[^\n^\w]*)''') 
     50_cwrap_re = re.compile(r'''(\n)|(\s+)|((?:\^[N0-7BU]|\S)+\b[^\n^\w]*)|(.)''') 
    5151_colour_terminal = 0 
    5252 
     
    240240 
    241241def error(*args): 
    242     """Print a red error message prefixed by ERR.""" 
    243     cprint(sys.stderr, "^1^BERR " + ' '.join(map(str, args)) + '^N') 
     242    """Print a message in red to stderr.""" 
     243    cprint(sys.stderr, "^1^B" + ' '.join(map(str, args)) + '^N') 
    244244 
    245245 
    246246def fatal(*args): 
    247     """Print a red error message prefixed by FTL, then exit with status -1""" 
    248     cprint(sys.stderr, "^1^BFTL " + ' '.join(map(str, args)) + '^N') 
     247    """Print a message in red to stderr then exit with status -1.""" 
     248    cprint(sys.stderr, "^1^B" + ' '.join(map(str, args)) + '^N') 
    249249    sys.exit(-1) 
    250250 
    251251 
    252252def warning(*args): 
    253     """Print a yellow warning message prefixed by WRN""" 
    254     cprint(sys.stderr, "^3^BWRN " + ' '.join(map(str, args)) + '^N') 
     253    """Print a yellow warning message to stderr.""" 
     254    cprint(sys.stderr, "^3^B" + ' '.join(map(str, args)) + '^N') 
    255255 
    256256 
    257257def info(*args): 
    258     """Print a green notice prefixed by INF.""" 
    259     cprint("^2^BINF ^B" + ' '.join(map(str, args)) + '^N') 
     258    """Print a green notice.""" 
     259    cprint("^2" + ' '.join(map(str, args)) + '^N') 
    260260 
    261261 
  • cly/trunk/cly/__init__.py

    r418 r425  
    247247        """Iterate over child nodes, ignoring context. 
    248248 
    249         >>> for child in Grammar(one=Node('One'), two=Node('Two')): print child 
    250         <Node:/one> 
     249        >>> tree = Node('One')(two=Node('Two'), three=Node('Three')) 
     250        >>> for child in tree: print child 
     251        <Node:/three> 
    251252        <Node:/two> 
    252253        """ 
     
    256257            yield child 
    257258 
     259    def __setitem__(self, key, child): 
     260        """Emulate dictionary set. 
     261 
     262        >>> node = Node('One') 
     263        >>> node['two'] = Node('Two') 
     264        >>> for i in node.walk(): print i 
     265        <Node:/> 
     266        <Node:/two> 
     267        """ 
     268        self(**{key: child}) 
     269 
     270    def __getitem__(self, key): 
     271        """Emulate dictionary get. 
     272 
     273        >>> node = Node('One')(two=Node('Two')) 
     274        >>> node['two'] 
     275        <Node:/two> 
     276        """ 
     277        return self._children[key] 
     278 
     279    def __delitem__(self, key): 
     280        """Emulate dictionary delete. 
     281 
     282        >>> node = Node('One')(two=Node('Two'), three=Node('Three')) 
     283        >>> for i in node.walk(): print i 
     284        <Node:/> 
     285        <Node:/three> 
     286        <Node:/two> 
     287        >>> del node['two'] 
     288        >>> for i in node.walk(): print i 
     289        <Node:/> 
     290        <Node:/three> 
     291        """ 
     292        child = self._children.pop(key) 
     293        child.parent = None 
     294 
     295    def __contains__(self, key): 
     296        """Emulate dictionary key existence test. 
     297 
     298        >>> node = Node('One')(two=Node('Two'), three=Node('Three')) 
     299        >>> 'two' in node 
     300        True 
     301        """ 
     302        return key in self._children 
     303 
    258304    def walk(self): 
    259305        """Perform a recursive walk of the grammar tree. 
    260306 
    261         >>> parser = Parser(Grammar(one=Node('One'), two=Node('Two', three=Node('Three')))) 
    262         >>> for node in parser: print node 
    263         <Grammar:/> 
     307        >>> tree = Node('One')(two=Node('Two', three=Node('Three'), 
     308        ...                             four=Node('Four'))) 
     309        >>> for i in tree.walk(): print i 
     310        <Node:/> 
    264311        <Node:/two> 
     312        <Node:/two/four> 
    265313        <Node:/two/three> 
    266         <Node:/one> 
    267314        """ 
    268315        def walk(root): 
     
    468515        return alias.valid(context) 
    469516 
     517    def visible(self, context): 
     518        alias = self.parser.find(self.alias) 
     519        return alias.visible(context) 
     520 
    470521    def selected(self, context, match): 
    471522        """This node was selected by the parser.""" 
     
    523574 
    524575    def help(self, context): 
    525         if not self.visible
     576        if not self.visible(context)
    526577            return 
    527578        if isinstance(self.doc, basestring): 
     
    698749 
    699750        for child in node.children(context, follow=True): 
    700             add_help(child) 
     751            if child.visible(context): 
     752                add_help(child) 
    701753 
    702754        self.help.sort() 
     
    762814    remaining = property(_get_remaining_input) 
    763815 
     816    def _get_parsed(self): 
     817        """Return command text that has been successfully parsed.""" 
     818        return self.command[:self.cursor] 
     819    parsed = property(_get_parsed) 
     820 
    764821    def _last_node(self): 
    765822        """Return the last node parsed.""" 
  • cly/trunk/cly/interactive.py

    r423 r425  
    2323import cly 
    2424import cly.rlext 
    25 from cly.console import error as print_error 
     25import cly.console as console 
    2626 
    2727 
     
    7878        Interact.history_length = history_length 
    7979        Interact.completion_delimiters = completion_delimiters 
     80        Interact.completion_key = completion_key 
    8081 
    8182        try: 
     
    115116                context.execute() 
    116117            except cly.ParseError, e: 
    117                 print_error(e) 
     118                self.print_error(context, e) 
    118119            return context 
    119120 
     121    def print_error(self, context, e): 
     122        """Called by `interact_once()` to print a ParseError.""" 
     123        candidates = [help[1] for help in context.help()] 
     124        if len(candidates) > 1: 
     125            message = '%s (candidates are %s)' 
     126        else: 
     127            message = '%s (expected %s)' 
     128        message = message % (str(e), ', '.join(candidates)) 
     129        self.error_at_cursor(context, message) 
     130 
     131    def error_at_cursor(self, context, text): 
     132        """Attempt to intelligently print an error at the current cursor 
     133        offset.""" 
     134        text = str(text) 
     135        term_width = console.termwidth() 
     136        indent = ' ' * (context.cursor % term_width 
     137                        + len(Interact.prompt)) 
     138        if len(indent + text) > term_width: 
     139            console.error(indent + '^') 
     140            console.error(text) 
     141        else: 
     142            console.error(indent + '^ ' + text) 
    120143 
    121144    def interact_loop(self): 
     
    180203                       (' ' * (context.cursor + len(Interact.prompt)), 
    181204                        ', '.join(candidates)) 
    182                 print_error(text) 
     205                console.error(text) 
    183206                cly.rlext.force_redisplay() 
    184207                return 
  • cly/trunk/doc/tutorial.py

    r423 r425  
    1313 
    1414grammar = Grammar( 
    15     quit=Node('Quit')( 
     15    # Quit is distinct from normal commands, so lets reflect that with a visual 
     16    # cue. 
     17    # Those are just a few of the features of CLY. Check the tutorial for more 
     18    # and the API documentation for detail. Enjoy :) 
     19    quit=Node('Quit', group=9999)( 
    1620        Action('Quit', do_quit), 
    1721    ), 
    1822    cat=Node('Concatenate files')( 
    19         files=File('File to concatenate', traversals=0)( 
     23        # This matches a file any number of times... 
     24        # Note how the example matched any file. If we add the following... 
     25        # Next, we'll restrict the number of files to 2... 
     26        files=File('File to concatenate', traversals=2, includes=['*.py'])( 
    2027            Action('Concatenate files', do_cat), 
     28            # This jumps back to the parent (files) node 
    2129            Alias('..'), 
     30            # Now let's see it in action... 
    2231        ), 
    2332    ), 
  • cly/trunk/.todo

    r413 r425  
    1 <todo version="0.1.19"> 
     1<todo version="0.1.20"> 
    22    <title> 
    33        CLY 
     
    99        Add XPath support: eg. grammar.find_all('//action') 
    1010    </note> 
     11    <note priority="medium" time="1179508609"> 
     12        console.textwrap() has issues 
     13    </note> 
     14    <note priority="medium" time="1179509575"> 
     15        Allow with_context to be specified per-parser. 
     16    </note> 
    1117</todo>