File: model_patterns.rst

package info (click to toggle)
hy 1.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,632 kB
  • sloc: python: 7,299; makefile: 38; sh: 27
file content (137 lines) | stat: -rw-r--r-- 4,007 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
==============
Model patterns
==============

The module ``hy.model-patterns`` provides a library of parser combinators for
parsing complex trees of Hy models. Model patterns exist mostly to help
implement the compiler, but they can also be useful for writing macros.

A motivating example
--------------------

The kind of problem that model patterns are suited for is the following.
Suppose you want to validate and extract the components of a form like::

    (setv form '(try
      (foo1)
      (foo2)
      (except [EType1]
        (foo3))
      (except [e EType2]
        (foo4)
        (foo5))
      (except []
        (foo6))
      (finally
        (foo7)
        (foo8))))

You could do this with loops and indexing, but it would take a lot of code and
be error-prone. Model patterns concisely express the general form of a model
tree to be matched, like what a regular expression does for text. Here's a
pattern for a ``try`` form of the above kind::

    (import
      funcparserlib.parser [maybe many]
      hy.model-patterns *)

    (setv parser (whole [
      (sym "try")
      (many (notpexpr "except" "else" "finally"))
      (many (pexpr
        (sym "except")
        (| (brackets) (brackets FORM) (brackets SYM FORM))
        (many FORM)))
      (maybe (dolike "else"))
      (maybe (dolike "finally"))]))

You can run the parser with ``(.parse parser form)``. The result is::

    #(
      ['(foo1) '(foo2)]
      [
        '([EType1] [(foo3)])
        '([e EType2] [(foo4) (foo5)])
        '([] [(foo6)])]
      None
      '((foo7) (foo8)))

which is conveniently utilized with an assignment such as ``(setv [body
except-clauses else-part finally-part] result)``. Notice that ``else-part``
will be set to ``None`` because there is no ``else`` clause in the original
form.

Usage
-----

Model patterns are implemented as funcparserlib_ parser combinators. We won't
reproduce funcparserlib's own documentation, but here are some important
built-in parsers:

- ``(+ ...)`` matches its arguments in sequence.
- ``(| ...)`` matches any one of its arguments.
- ``(>> parser function)`` matches ``parser``, then feeds the result through
  ``function`` to change the value that's produced on a successful parse.
- ``(skip parser)`` matches ``parser``, but doesn't add it to the produced
  value.
- ``(maybe parser)`` matches ``parser`` if possible. Otherwise, it produces
  the value ``None``.
- ``(some function)`` takes a predicate ``function`` and matches a form if it
  satisfies the predicate.

Some of the more important of Hy's own parsers are:

- :data:`FORM <hy.model_patterns.FORM>` matches anything.
- :data:`SYM <hy.model_patterns.SYM>` matches any symbol.
- :func:`sym <hy.model_patterns.sym>` matches and discards (per ``skip``) the
  named symbol or keyword.
- :func:`brackets <hy.model_patterns.brackets>` matches the arguments in square
  brackets.
- :func:`pexpr <hy.model_patterns.pexpr>` matches the arguments in parentheses.

Here's how you could write a simple macro using model patterns::

    (defmacro pairs [#* args]
      (import
        funcparserlib.parser [many]
        hy.model-patterns [whole SYM FORM])
      (setv [args] (.parse
        (whole [(many (+ SYM FORM))])
        args))
      `[~@(gfor  [a1 a2] args  #((str a1) a2))])

    (print (hy.repr (pairs  a 1  b 2  c 3)))
    ; => [#("a" 1) #("b" 2) #("c" 3)]

A failed parse will raise ``funcparserlib.parser.NoParseError``.

.. _funcparserlib: https://github.com/vlasovskikh/funcparserlib

Reference
---------

.. automodule:: hy.model_patterns
   :members:

   .. The below must be documented in this file rather than using
      autodoc, due to a regression in Python 3.14.

   .. data:: FORM

      Match any token.

   .. data:: SYM

      Match a :class:`Symbol <hy.models.Symbol>`.

   .. data:: KEYWORD

      Match a :class:`Keyword <hy.models.Keyword>`.

   .. data:: STR

      Match a :class:`String <hy.models.String>`.

   .. data:: LITERAL

      Match any model type denoting a literal.