| 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 | | -------------------- |
|---|
| | 9 | CLY provides a convenient means to easily build command-line interfaces. This is |
|---|
| | 10 | achieved by defining a CLY grammar in either Python or XML then constructing a |
|---|
| | 11 | CLY parser object from the grammar. The parser parses user input against the |
|---|
| | 12 | grammar, executes callbacks, provides completion, and so on. |
|---|
| | 13 | |
|---|
| | 14 | A built-in terminal interface based on readline is provided. |
|---|
| | 15 | |
|---|
| | 16 | Experimenting |
|---|
| | 17 | ------------- |
|---|
| | 18 | |
|---|
| | 19 | The 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 | |
|---|
| | 28 | This will start a readline console using the given grammar. Press ``?`` at any |
|---|
| | 29 | time for contextual help, ``<Tab>`` to attempt completion, and ``<Ctrl-D>`` |
|---|
| | 30 | (EOF) on an empty line to terminate the shell. |
|---|
| | 31 | |
|---|
| | 32 | Now, on with the nitty gritty. |
|---|
| | 33 | |
|---|
| | 34 | Building a Grammar from Python |
|---|
| | 35 | ------------------------------ |
|---|
| 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``: |
|---|
| | 40 | The syntax of a shell is defined as a tree of ``Node`` objects. Each node |
|---|
| | 41 | matches 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 |
|---|
| | 43 | expression. For example, the following grammar would match the token ``one``: |
|---|
| 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 |
|---|
| | 200 | In addition to all of the attributes included with ``Node``, ``Action`` also has |
|---|
| | 201 | the ``with_user_context`` flag. This can be used to pass a user-defined context |
|---|
| | 202 | variable (as opposed to the parser context) to the callback as the first |
|---|
| | 203 | argument. Refer to `Passing User-defined Objects to Callbacks`_ for more |
|---|
| | 315 | Defining a Grammar in XML |
|---|
| | 316 | ------------------------- |
|---|
| | 317 | CLY 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 |
|---|
| | 319 | to the ``Node`` constructor. |
|---|
| | 320 | |
|---|
| | 321 | Here'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 | |
|---|
| | 334 | Parsing an XML Grammar |
|---|
| | 335 | ~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| | 336 | |
|---|
| | 337 | Calling ``Grammar.from_xml()`` with an XML string as the first argument will |
|---|
| | 338 | build 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 |
|---|
| | 340 | recognise as elements. All other keyword arguments will be treated as local |
|---|
| | 341 | symbols when evaluating attribute values in the XML grammar. |
|---|
| | 342 | |
|---|
| | 343 | Here'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 | |
|---|
| | 358 | Here'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 | |
|---|
| | 374 | The Python code is identical. |
|---|
| | 375 | |
|---|
| | 376 | XML Attribute Type Conversion |
|---|
| | 377 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| | 378 | All ``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 |
|---|
| | 380 | converted to integers, callback arguments may contain lambdas, etc. This |
|---|
| | 381 | evaluation 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 | |
|---|
| 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. |
|---|
| | 396 | A grammar is simply a data structure, to actually utilise it one needs to bind |
|---|
| | 397 | it to a ``Parser`` object. The parser takes care of creating a context for each |
|---|
| | 398 | parse run, parsing the input, and usually executing any callbacks. |
|---|
| | 399 | |
|---|
| | 400 | The context is created automatically on parser construction and contains all the |
|---|
| | 401 | information needed to parse input commands. This includes the current cursor |
|---|
| | 402 | position in the input stream, the current node in the grammar, variables |
|---|
| | 403 | collected and a history of nodes traversed. |
|---|