Changeset 482

Show
Ignore:
Timestamp:
12/09/07 05:21:56 (1 year ago)
Author:
athomas
Message:

cly: More developers guide documentation, specifically about the XML grammar.
Extending the nodes supported by the XML parser is now as simple as passing
them as a list to Grammar.from_xml().

Files:

Legend:

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

    r481 r482  
    668668            raise XMLParseError(str(e)) 
    669669 
    670         extra_nodes = extra_nodes or {} 
     670        extra_nodes = extra_nodes or [] 
    671671 
    672672        def boolean(v): 
     
    690690 
    691691        node_types = dict([(v.__name__.lower(), v) 
    692                           for v in globals().values(
     692                          for v in (globals().values() + extra_nodes
    693693                          if isclass(v) and issubclass(v, Node)]) 
    694  
    695         node_types.update(extra_nodes) 
    696694 
    697695        def parse(parent, xnode): 
  • cly/trunk/doc/developers-guide.rst

    r481 r482  
    77-------- 
    88 
    9 CLY's general use-case can be broken down into several phases: 
    10  
    11   1. `Defining the grammar`_. 
    12   2. `Parsing`_ input text. 
    13  
    14 Defining the Grammar 
    15 -------------------- 
     9CLY provides a convenient means to easily build command-line interfaces. This is 
     10achieved by defining a CLY grammar in either Python or XML then constructing a 
     11CLY parser object from the grammar. The parser parses user input against the 
     12grammar, executes callbacks, provides completion, and so on. 
     13 
     14A built-in terminal interface based on readline is provided. 
     15 
     16Experimenting 
     17------------- 
     18 
     19The quickest way to experiment with CLY is by passing a ``Grammar`` or 
     20``Parser`` object to the ``interact`` function: 
     21 
     22.. code-block:: python 
     23 
     24  from cly import interact 
     25 
     26  interact(grammar_or_parser) 
     27 
     28This will start a readline console using the given grammar. Press ``?`` at any 
     29time for contextual help, ``<Tab>`` to attempt completion, and ``<Ctrl-D>`` 
     30(EOF) on an empty line to terminate the shell. 
     31 
     32Now, on with the nitty gritty. 
     33 
     34Building a Grammar from Python 
     35------------------------------ 
    1636 
    1737Matching Input 
    1838~~~~~~~~~~~~~~ 
    1939 
    20 The syntax of a shell is defined as a tree of ``Node`` objects. 
    21  
    22 A node matches a token of user input defined by the ``pattern`` 
    23 attribute of the ``Node``, which defaults to the ``Node`` name. For 
    24 example, the following grammar would match the token ``one``: 
     40The syntax of a shell is defined as a tree of ``Node`` objects.  Each node 
     41matches a token of user input defined by the ``pattern`` attribute of the 
     42``Node`` and defaults to the ``Node`` name. ``pattern`` is treated a regular 
     43expression. For example, the following grammar would match the token ``one``: 
    2544 
    2645.. code-block:: python 
     
    179198  ) 
    180199 
    181 In addition to all of the attributes included with ``Node``, ``Action`` 
    182 also has the ``with_user_context`` flag. This can be used to pass a 
    183 user-defined context variable (as opposed to the parser context) to the 
    184 callback as the first argument. Refer to `Creating a Parser`_ for more 
     200In addition to all of the attributes included with ``Node``, ``Action`` also has 
     201the ``with_user_context`` flag. This can be used to pass a user-defined context 
     202variable (as opposed to the parser context) to the callback as the first 
     203argument. Refer to `Passing User-defined Objects to Callbacks`_ for more 
    185204information about user contexts. 
    186205 
     
    294313already been collected. 
    295314 
     315Defining a Grammar in XML 
     316------------------------- 
     317CLY XML grammars are simply a one-to-one mapping of XML elements to Python 
     318``cly.builder.Node`` objects. Attributes of each element are passed as arguments 
     319to the ``Node`` constructor. 
     320 
     321Here's a simple example of a grammar defining a single "echo" command: 
     322 
     323.. code-block:: xml 
     324 
     325  <?xml version="1.0"?> 
     326  <grammar xmlns="http://swapoff.org/cly/xml"> 
     327    <node name="echo"> 
     328      <variable name="text"> 
     329        <action callback="echo" pattern=".+"/> 
     330      </variable> 
     331    </node> 
     332  </grammar> 
     333 
     334Parsing an XML Grammar 
     335~~~~~~~~~~~~~~~~~~~~~~ 
     336 
     337Calling ``Grammar.from_xml()`` with an XML string as the first argument will 
     338build a new ``Grammar`` object from that XML. An optional second argument 
     339``extra_nodes`` can be used to pass a list of extra ``Node`` sub-classes to 
     340recognise as elements. All other keyword arguments will be treated as local 
     341symbols when evaluating attribute values in the XML grammar. 
     342 
     343Here's an example that uses the previously defined XML grammar to implement an 
     344"echo" command: 
     345 
     346.. code-block:: python 
     347 
     348  from cly import Grammar, interact 
     349 
     350  def echo(text): 
     351      print text 
     352 
     353  xml_grammar = open('example.xml').read() 
     354  grammar = Grammar.from_xml(xml_grammar, echo=echo) 
     355 
     356  interact(grammar) 
     357 
     358Here's a slightly more complex example with looping and grouping: 
     359 
     360.. code-block:: xml 
     361 
     362  <?xml version="1.0"?> 
     363  <grammar xmlns="http://swapoff.org/cly/xml"> 
     364    <node name="echo"> 
     365      <group traversals="0"> 
     366        <variable name="text"> 
     367          <alias target="/echo/*"/> 
     368          <action callback="echo"/> 
     369        </variable> 
     370      </group> 
     371    </node> 
     372  </grammar> 
     373 
     374The Python code is identical. 
     375 
     376XML Attribute Type Conversion 
     377~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     378All ``Node`` constructor arguments that are not strings but are known to the 
     379``Parser`` will be evaluated as Python code, meaning integer arguments will be 
     380converted to integers, callback arguments may contain lambdas, etc. This 
     381evaluation can be forced by using the ``eval`` XML namespace: 
     382 
     383.. code-block:: xml 
     384 
     385  <?xml version="1.0"?> 
     386  <grammar xmlns="http://swapoff.org/cly/xml" xmlns:eval="http://swapoff.org/cly/xml-eval"> 
     387    <node eval:help="'HELLO WORLD'.title()"/> 
     388      ... 
     389    </node> 
     390  </grammar> 
     391 
     392 
    296393Parsing 
    297394------- 
    298395 
    299 To actually utilise a grammar you need to bind it to a ``Parser`` 
    300 object. The parser takes care of creating a context for each parse run, 
    301 parsing the input, and usually executing any callbacks. 
     396A grammar is simply a data structure, to actually utilise it one needs to bind 
     397it to a ``Parser`` object. The parser takes care of creating a context for each 
     398parse run, parsing the input, and usually executing any callbacks. 
     399 
     400The context is created automatically on parser construction and contains all the 
     401information needed to parse input commands. This includes the current cursor 
     402position in the input stream, the current node in the grammar, variables 
     403collected and a history of nodes traversed. 
    302404 
    303405Basic usage is: