Browse Source

Merge branch 'master' of https://github.com/lark-parser/lark into better-terminals

 Conflicts:
	lark/common.py
	lark/exceptions.py
	lark/lexer.py
	lark/load_grammar.py
	lark/parser_frontends.py
tags/gm/2021-09-23T00Z/github.com--lark-parser-lark/0.11.2
MegaIng1 5 years ago
parent
commit
17d4da7924
44 changed files with 3884 additions and 544 deletions
  1. +2
    -2
      README.md
  2. +212
    -0
      docs/_static/sppf/sppf.html
  3. +765
    -0
      docs/_static/sppf/sppf_111.svg
  4. +584
    -0
      docs/_static/sppf/sppf_abcd.svg
  5. +522
    -0
      docs/_static/sppf/sppf_abcd_noint.svg
  6. +682
    -0
      docs/_static/sppf/sppf_cycle.svg
  7. +1
    -1
      docs/features.md
  8. +7
    -3
      docs/grammar.md
  9. +14
    -3
      docs/parsers.md
  10. +1
    -1
      docs/philosophy.md
  11. +11
    -4
      docs/visitors.rst
  12. +79
    -0
      examples/advanced/error_reporting_earley.py
  13. +1
    -1
      examples/advanced/error_reporting_lalr.py
  14. +6
    -14
      examples/advanced/python3.lark
  15. +7
    -3
      examples/lark_grammar.py
  16. +4
    -0
      lark-stubs/exceptions.pyi
  17. +24
    -4
      lark-stubs/lark.pyi
  18. +21
    -8
      lark-stubs/lexer.pyi
  19. +2
    -2
      lark/__init__.py
  20. +9
    -3
      lark/common.py
  21. +37
    -10
      lark/exceptions.py
  22. +0
    -3
      lark/grammar.py
  23. +10
    -1
      lark/grammars/common.lark
  24. +1
    -1
      lark/grammars/lark.lark
  25. +19
    -0
      lark/grammars/python.lark
  26. +7
    -0
      lark/grammars/unicode.lark
  27. +83
    -33
      lark/lark.py
  28. +23
    -16
      lark/lexer.py
  29. +164
    -64
      lark/load_grammar.py
  30. +19
    -8
      lark/parse_tree_builder.py
  31. +155
    -189
      lark/parser_frontends.py
  32. +16
    -12
      lark/parsers/earley.py
  33. +41
    -20
      lark/parsers/earley_forest.py
  34. +11
    -4
      lark/parsers/lalr_parser.py
  35. +20
    -4
      lark/parsers/lalr_puppet.py
  36. +6
    -4
      lark/parsers/xearley.py
  37. +4
    -2
      lark/tools/nearley.py
  38. +10
    -6
      lark/tree.py
  39. +9
    -1
      lark/tree_matcher.py
  40. +10
    -39
      lark/utils.py
  41. +52
    -38
      lark/visitors.py
  42. +2
    -2
      setup.py
  43. +2
    -14
      tests/__main__.py
  44. +229
    -24
      tests/test_parser.py

+ 2
- 2
README.md View File

@@ -106,7 +106,7 @@ Lark is great at handling ambiguity. Here is the result of parsing the phrase "f
- MyPy support using type stubs - MyPy support using type stubs
- And much more! - And much more!


See the full list of [features here](https://lark-parser.readthedocs.io/en/latest/features/)
See the full list of [features here](https://lark-parser.readthedocs.io/en/latest/features.html)




### Comparison to other libraries ### Comparison to other libraries
@@ -132,7 +132,7 @@ Check out the [JSON tutorial](/docs/json_tutorial.md#conclusion) for more detail
|:--------|:----------|:----|:--------|:------------|:------------|:----------|:---------- |:--------|:----------|:----|:--------|:------------|:------------|:----------|:----------
| **Lark** | Earley/LALR(1) | EBNF | Yes! | Yes! | Yes! | Yes! | Yes! (LALR only) | | **Lark** | Earley/LALR(1) | EBNF | Yes! | Yes! | Yes! | Yes! | Yes! (LALR only) |
| [PLY](http://www.dabeaz.com/ply/) | LALR(1) | BNF | No | No | No | No | No | | [PLY](http://www.dabeaz.com/ply/) | LALR(1) | BNF | No | No | No | No | No |
| [PyParsing](http://pyparsing.wikispaces.com/) | PEG | Combinators | No | No | No\* | No | No |
| [PyParsing](https://github.com/pyparsing/pyparsing) | PEG | Combinators | No | No | No\* | No | No |
| [Parsley](https://pypi.python.org/pypi/Parsley) | PEG | EBNF | No | No | No\* | No | No | | [Parsley](https://pypi.python.org/pypi/Parsley) | PEG | EBNF | No | No | No\* | No | No |
| [Parsimonious](https://github.com/erikrose/parsimonious) | PEG | EBNF | Yes | No | No\* | No | No | | [Parsimonious](https://github.com/erikrose/parsimonious) | PEG | EBNF | Yes | No | No\* | No | No |
| [ANTLR](https://github.com/antlr/antlr4) | LL(*) | EBNF | Yes | No | Yes? | Yes | No | | [ANTLR](https://github.com/antlr/antlr4) | LL(*) | EBNF | Yes | No | Yes? | Yes | No |


+ 212
- 0
docs/_static/sppf/sppf.html View File

@@ -0,0 +1,212 @@
<!DOCTYPE html>
<!-- saved from url=(0115)https://web.archive.org/web/20200128172318/http://www.bramvandersanden.com/post/2014/06/shared-packed-parse-forest/ -->
<html lang="en-us">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">

<body>

<div class="container">

<article class="article" itemscope itemtype="http://schema.org/Article">



<h1 itemprop="name">Shared Packed Parse Forest (SPPF)</h1>



<div class="article-metadata" style="font-size: 80%">

<span class="article-date">
<time datetime="2014-06-27 12:00:00 +0000 UTC" itemprop="datePublished">
Fri, Jun 27, 2014
</time>
</span>




<span class="article-tags">
<i class="fa fa-tags"></i>

<a class="article-tag-link" href="https://web.archive.org/web/20200128172318/http://www.bramvandersanden.com/tags/parsing">parsing</a>,

<a class="article-tag-link" href="https://web.archive.org/web/20200128172318/http://www.bramvandersanden.com/tags/sppf">sppf</a>

</span>






</div>


<div class="article-style" itemprop="articleBody">

<p></p>

<p>In the last decade there has been a lot of interest in generalized parsing techniques. These techniques can be used to generate a working parser for any context-free grammar. This means that we no longer have to massage our grammar to fit into restricted classes such as <em>LL(k)</em> or <em>LR(k)</em>. Supporting all context-free grammars means that grammars can be written in a natural way, and grammars can be combined, since the class of context-free grammars is closed under composition.</p>

<p>One of the consequences of supporting the whole class of context-free grammars is that also ambiguous grammars are supported. In an ambiguous grammar there are sentences in the language that can be derived in multiple ways. Each derivation results in a distinct parse tree. For each additional ambiguity in the input sentence, the number of derivations might grow exponentially. Therefore generalized parsers output a parse forest, rather than a set of the parse trees. In this parse forest, often sharing is used used to reduce the total space required to represent all derivation trees. Nodes which have the same subtree are shared, and nodes are combined which correspond to different derivations of the same substring. A parse forest where sharing is employed is called a shared packed parse forest (SPPF).</p>

<p>This article will describe the SPPF data structure in more detail. More information about the generation of the SPPF using the GLL algorithm can be found in the paper <a href="https://web.archive.org/web/20200128172318/http://dx.doi.org/10.1016/j.scico.2012.03.005">GLL parse-tree generation</a> by E. Scott and A. Johnstone. Right Nulled GLR parsers can also be used to generate an SPPF, which is described in the paper <a href="https://web.archive.org/web/20200128172318/http://doi.acm.org/10.1145/1146809.1146810">Right Nulled GLR Parsers</a> by E. Scott and A. Johnstone.</p>

<p>There are three types of nodes in an SPPF associated with a GLL parser: <em>symbol nodes</em>, <em>packed nodes</em>, and <em>intermediate nodes</em>. In the visualizations symbol nodes are shown as rectangles with rounded corners, packed nodes are shown as circles, or ovals when the label is visualized, and intermediate nodes are shown as rectangles.</p>

<p>Symbol nodes have labels of the form $(x,j,i)$ where $x$ is a terminal, nonterminal, or $\varepsilon$ (i.e. $x \in T \cup N \cup \lbrace \varepsilon \rbrace$), and $0 \leq j \leq i \leq m$ with $m$ being the length of the input sentence. The tuple $(j,i)$ is called the <em>extent</em>, and denotes that the symbol $x$ has been matched on the substring from position $j$ up to position $i$. Here $j$ is called the <em>left extent</em>, and $i$ is called the <em>right extent</em>.</p>

<p>Packed nodes have labels of the form $(t,k)$, where $0 \leq k \leq m$. Here $k$ is called the <em>pivot</em>, and $t$ is of the form $X ::= \alpha \cdot \beta$. The value of $k$ represents that the last symbol of $\alpha$ ends at position $k$ of the input string. Packed nodes are used to represent multiple derivation trees. When multiple derivations are possible with the same extent, starting from the same nonterminal symbol node, a separate packed node is added to the symbol node for each derivation.</p>

<p>Intermediate nodes are used to binarize the SPPF. They are introduced from the left, and group the children of packed nodes in pairs from the left. The binarization ensures that the size of the SPPF is worst-case cubic in the size of the input sentence. The fact that the SPPF is binarized does not mean that each node in the SPPF has at most two children. A symbol node or intermediate node can still have as many packed node children as there are ambiguities starting from it. Intermediate nodes have labels of the form $(t,j,i)$ where $t$ is a grammar slot, and $(j,i)$ is the extent. There are no intermediate nodes of the shape $(A ::= \alpha \cdot, j,i)$, where the grammar pointer of the grammar slot is at the end of the alternate. These grammar slots are present in the form of symbol nodes.</p>

<p>Consider the following grammar:</p>

<p>$\quad S ::= ABCD \quad A ::= a \quad B ::= b \quad C ::= c \quad D ::= d. $</p>

<p>Then given input sentence $abcd$, the the following SPPF will be the result:</p>


<figure>

<img src="sppf_abcd.svg"/>


<figcaption>
<h4>SPPF with intermediate nodes</h4>

</figcaption>

</figure>


<p>Suppose that the intermediate nodes had not been added to the SPPF. Then the nonterminal symbol nodes for $A$, $B$, $C$, and $D$ would have been attached to the nonterminal symbol node $S$:</p>


<figure>

<img src="sppf_abcd_noint.svg"/>


<figcaption>
<h4>SPPF without intermediate nodes</h4>

</figcaption>

</figure>


<p>This example shows how intermediate nodes ensure that the tree is binarized.</p>

<h2 id="adding-cycles">Adding cycles</h2>

<p>Grammars that contain cycles can generate sentences which have infinitely many derivation trees. A context-free grammar is cyclic if there exists a nonterminal $A \in N$ and a derivation $A \overset{+}\Rightarrow A$. Note that a cyclic context-free grammar implies that the context-free grammar is left-recursive, but the converse does not hold. The derivation trees for a cyclic grammar are represented in the finite SPPF by introducing cycles in the graph.</p>

<p>Consider the following cyclic grammar:
$S ::= SS \mid a \mid \varepsilon$.</p>

<p>Given input sentence $a$, there are infinitely many derivations. All these derivations are present in the following SPPF:</p>


<figure>

<img src="sppf_cycle.svg"/>


<figcaption>
<h4>SPPF containing an infinite number of derivations</h4>

</figcaption>

</figure>


<h2 id="ambiguities">Ambiguities</h2>

<p>A parse forest is <em>ambiguous</em> if and only if it contains at least one ambiguity. An ambiguity arises when a symbol node or intermediate node has at least two packed nodes as its children. Such nodes are called <em>ambiguous</em>. Consider for instance the following grammar with input sentence $1+1+1$:
$ E ::= E + E \mid 1 $.</p>

<p>This gives the following SPPF:</p>


<figure>

<img src="sppf_111.svg"/>


<figcaption>
<h4>SPPF containing an ambiguous root node</h4>

</figcaption>

</figure>


<p>In this SPPF, symbol node $(E,0,5)$ has two packed nodes as children. This means that there are at least two different parse trees starting at this node, the parse trees representing derivations $(E+(E+E))$ and $((E+E)+E)$ respectively.</p>

<p>The set of all parse trees present in the SPPF is defined in the following way:</p>

<p>Start at the root node of the SPPF, and walk the tree by choosing one packed node below each visited node, and choosing all the children of a visited packed node in a recursive manner.</p>

<h2 id="structural-properties">Structural Properties</h2>

<p>There are various structural properties that are useful when reasoning about SPPFs in general. At first note that each symbol node $(E,j,i)$ with $E \in T \cup N \cup \lbrace \varepsilon \rbrace$ is unique, so an SPPF does not contain two symbol nodes $(A,k,l)$ and $(B,m,n)$ with $A = B, k = m$, and $l=n$.</p>

<p>Terminal symbol nodes have no children. These nodes represent the leaves of the parse forest. Nonterminal symbol nodes $(A,j,i)$ have packed node children of the form $(A ::= \gamma \cdot, k)$ with $j \leq k \leq i$, and the number of children is not limited to two.</p>

<p>Intermediate nodes $(t,j,i)$ have packed node children with labels of the form $(t,k)$, where $j \leq k \leq i$.</p>

<p>Packed nodes $(t,k)$ have one or two children. The right child is a symbol node $(x,k,i)$ and the left child (if it exists) is a symbol or intermediate node with label $(s,j,k)$, where $j \leq k \leq i$. Packed nodes have always exactly one parent which is a symbol node or intermediate node.</p>

<p>It is useful to observe that the SPPF is a bipartite graph, with on the one hand the set of intermediate and symbol nodes and on the other hand the set of packed nodes. Therefore edges always go from a node of the first type to a node of the second type, or the other way round. As a consequence, cyles in the SPPF are always of even length.</p>

<h2 id="transformation-to-an-abstract-syntax-tree">Transformation to an abstract syntax tree</h2>

<p>In the end, we often want a single abstract syntax tree (AST) when parsing an input sentence. In order to arrive at this AST, we need disambiguation techniques to remove undesired parse trees from the SPPF or avoid the generation of undesired parse trees in the first place. {% cite sanden2014thesis %} describes several SPPF disambiguation filters that remove ambiguities arising in expression grammars. Furthermore a method is described to integrate parse-time filtering in GLL that tries to avoid embedding undesired parse trees in the SPPF.</p>

<p>Of course, other transformation might be needed such as the removal of whitespace and comments from the parse forest.</p>

</div>

</article>

<nav>
<ul class="pager">



<li class="next"><a href="https://web.archive.org/web/20200128172318/http://www.bramvandersanden.com/post/2016/10/2016-10-31-fdl-presentation/">Compositional Specification of Functionality and Timing of Manufacturing Systems <span aria-hidden="true">&rarr;</span></a></li>

</ul>
</nav>


<footer class="site-footer">
<div class="container">
<p class="powered-by">

&copy; 2016 Bram van der Sanden &middot;

Powered by the <a href="https://web.archive.org/web/20200128172318/https://github.com/gcushen/hugo-academic" target="_blank">Academic
theme</a> for <a href="https://web.archive.org/web/20200128172318/http://gohugo.io/" target="_blank">Hugo</a>.

<span class="pull-right" aria-hidden="true">
<a href="#" id="back_to_top">
<span class="button_icon">
<i class="fa fa-chevron-up fa-2x"></i>
</span>
</a>
</span>

<p>Source: <a href="https://web.archive.org/web/20200128172318/http://www.bramvandersanden.com/post/2014/06/shared-packed-parse-forest/">Wayback Machine</a>
copy of <code>http://www.bramvandersanden.com/post/2014/06/shared-packed-parse-forest/</code> used to be.
</p>

</p>
</div>

</footer>
</body>

+ 765
- 0
docs/_static/sppf/sppf_111.svg View File

@@ -0,0 +1,765 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="538.75"
height="490"
id="svg2"
xml:space="preserve"><metadata
id="metadata8"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs6"><clipPath
id="clipPath16"><path
d="M 0,0 509,0 509,472 0,472 0,0 z"
inkscape:connector-curvature="0"
id="path18" /></clipPath><clipPath
id="clipPath20"><path
d="m 36,436 438,0 0,-401 -438,0 0,401 z"
inkscape:connector-curvature="0"
id="path22" /></clipPath></defs><g
transform="matrix(1.25,0,0,-1.25,0,490)"
id="g10"><g
transform="translate(-39,-40)"
id="g12"><g
id="g14" /><g
id="g24"><g
clip-path="url(#clipPath16)"
id="g26"><g
id="g28"><path
d="m 36,436 438,0 0,-401 -438,0 0,401 z"
inkscape:connector-curvature="0"
id="path30"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /></g><g
id="g32"><g
clip-path="url(#clipPath20)"
id="g34"><path
d="m 36,436 438,0 0,-401 -438,0 0,401 z"
inkscape:connector-curvature="0"
id="path36"
style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 278,431.5 -40,0"
inkscape:connector-curvature="0"
id="path38"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 238,431.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path40"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 231,424.5 0,-7"
inkscape:connector-curvature="0"
id="path42"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 231,417.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path44"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 238,410.5 40,0"
inkscape:connector-curvature="0"
id="path46"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 278,410.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path48"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 285,417.5 0,7"
inkscape:connector-curvature="0"
id="path50"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 285,424.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path52"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,239,418.5)"
id="text54"><tspan
x="0 3.7499084 9.7397623 12.72969 15.719617 21.719471 24.709396 27.699324 33.699177"
y="0"
id="tspan56"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(E, 0, 5)</tspan></text>
<path
d="m 210,384 c 0,-4.668 -6,-4.668 -6,0 0,4.668 6,4.668 6,0 z"
inkscape:connector-curvature="0"
id="path58"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 243.523,410.496 c -9.019,-6.543 -20.316,-14.738 -27.937,-20.269"
inkscape:connector-curvature="0"
id="path60"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 216.891,388.148 -7.102,-2.125 4.227,6.094 2.875,-3.969 z"
inkscape:connector-curvature="0"
id="path62"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 312,384 c 0,-4.668 -6,-4.668 -6,0 0,4.668 6,4.668 6,0 z"
inkscape:connector-curvature="0"
id="path64"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 272.477,410.496 c 9.019,-6.543 20.316,-14.738 27.937,-20.269"
inkscape:connector-curvature="0"
id="path66"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 301.984,392.117 4.227,-6.094 -7.102,2.125 2.875,3.969 z"
inkscape:connector-curvature="0"
id="path68"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 78,209.5 106,0 0,-21 -106,0 0,21 z"
inkscape:connector-curvature="0"
id="path70"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,85.5,196.5)"
id="text72"><tspan
x="0 3.7499084 9.7397623 12.72969 15.719617 18.709543 26.959343 29.949268 35.939121 38.92905 47.16885 50.158775 56.148628 59.148556 65.138412 68.128334 71.118263 77.118118 80.108047 86.107903"
y="0"
id="tspan74"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(E ::= E + • E ,0,2)</tspan></text>
<path
d="M 204.113,382.902 C 190.23,377.426 131,351.621 131,310 c 0,0 0,0 0,-37 0,-19.254 0,-41.328 0,-56.32"
inkscape:connector-curvature="0"
id="path76"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 133.449,216.539 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path78"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 227,357.5 -40,0"
inkscape:connector-curvature="0"
id="path80"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 187,357.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path82"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 180,350.5 0,-7"
inkscape:connector-curvature="0"
id="path84"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 180,343.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path86"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 187,336.5 40,0"
inkscape:connector-curvature="0"
id="path88"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 227,336.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path90"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 234,343.5 0,7"
inkscape:connector-curvature="0"
id="path92"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 234,350.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path94"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,188,344.5)"
id="text96"><tspan
x="0 3.7499084 9.7397623 12.72969 15.719617 21.719471 24.709396 27.699324 33.699177"
y="0"
id="tspan98"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(E, 2, 5)</tspan></text>
<path
d="m 207,380.316 c 0,-3.699 0,-9.687 0,-15.57"
inkscape:connector-curvature="0"
id="path100"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 209.449,364.527 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path102"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 462,283.5 -40,0"
inkscape:connector-curvature="0"
id="path104"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 422,283.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path106"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 415,276.5 0,-7"
inkscape:connector-curvature="0"
id="path108"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 415,269.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path110"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 422,262.5 40,0"
inkscape:connector-curvature="0"
id="path112"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 462,262.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path114"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 469,269.5 0,7"
inkscape:connector-curvature="0"
id="path116"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 469,276.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path118"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,423,270.5)"
id="text120"><tspan
x="0 3.7499084 9.7397623 12.72969 15.719617 21.719471 24.709396 27.699324 33.699177"
y="0"
id="tspan122"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(E, 4, 5)</tspan></text>
<path
d="m 312.211,383.301 c 9.875,-2.27 40.355,-10.172 60.789,-25.301 25.441,-18.84 46.984,-49.141 58.934,-68.02"
inkscape:connector-curvature="0"
id="path124"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 434.289,290.828 1.602,-7.242 -5.77,4.664 4.168,2.578 z"
inkscape:connector-curvature="0"
id="path126"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 256,357.5 106,0 0,-21 -106,0 0,21 z"
inkscape:connector-curvature="0"
id="path128"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,263.5,344.5)"
id="text130"><tspan
x="0 3.7499084 9.7397623 12.72969 15.719617 18.709543 26.959343 29.949268 35.939121 38.92905 47.16885 50.158775 56.148628 59.148556 65.138412 68.128334 71.118263 77.118118 80.108047 86.107903"
y="0"
id="tspan132"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(E ::= E + • E ,0,4)</tspan></text>
<path
d="m 309,380.316 c 0,-3.699 0,-9.687 0,-15.57"
inkscape:connector-curvature="0"
id="path134"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 311.449,364.527 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path136"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 134,162 c 0,-4.668 -6,-4.668 -6,0 0,4.668 6,4.668 6,0 z"
inkscape:connector-curvature="0"
id="path138"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 131,188.496 c 0,-4.926 0,-10.793 0,-15.75"
inkscape:connector-curvature="0"
id="path140"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 133.449,172.586 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path142"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 343,310 c 0,-4.668 -6,-4.668 -6,0 0,4.668 6,4.668 6,0 z"
inkscape:connector-curvature="0"
id="path144"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 234.383,339.02 c 3.57,-1.024 7.179,-2.051 10.617,-3.02 30.836,-8.684 67.418,-18.582 84.82,-23.266"
inkscape:connector-curvature="0"
id="path146"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 330.918,314.977 6.121,-4.184 -7.394,-0.551 1.273,4.735 z"
inkscape:connector-curvature="0"
id="path148"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 87,135.5 -40,0"
inkscape:connector-curvature="0"
id="path150"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 47,135.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path152"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 40,128.5 0,-7"
inkscape:connector-curvature="0"
id="path154"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 40,121.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path156"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 47,114.5 40,0"
inkscape:connector-curvature="0"
id="path158"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 87,114.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path160"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 94,121.5 0,7"
inkscape:connector-curvature="0"
id="path162"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 94,128.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path164"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,48,122.5)"
id="text166"><tspan
x="0 3.7499084 9.7397623 12.72969 15.719617 21.719471 24.709396 27.699324 33.699177"
y="0"
id="tspan168"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(E, 0, 1)</tspan></text>
<path
d="m 128.25,160.41 c -6.348,-3.672 -22.57,-13.047 -36.723,-21.23"
inkscape:connector-curvature="0"
id="path170"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 92.496,136.91 -7.285,-1.383 4.836,5.625 2.449,-4.242 z"
inkscape:connector-curvature="0"
id="path172"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 165,135.5 -42,0"
inkscape:connector-curvature="0"
id="path174"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 123,135.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path176"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 116,128.5 0,-7"
inkscape:connector-curvature="0"
id="path178"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 116,121.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path180"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 123,114.5 42,0"
inkscape:connector-curvature="0"
id="path182"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 165,114.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path184"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 172,121.5 0,7"
inkscape:connector-curvature="0"
id="path186"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 172,128.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path188"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,124,122.5)"
id="text190"><tspan
x="0 3.7499084 11.989707 14.979634 17.969561 23.969416 26.959343 29.949268 35.949123"
y="0"
id="tspan192"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(+, 1, 2)</tspan></text>
<path
d="m 132.199,158.586 c 1.301,-3.703 3.481,-9.902 5.613,-15.973"
inkscape:connector-curvature="0"
id="path194"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 140.23,143.117 0.008,-7.414 -4.629,5.789 4.621,1.625 z"
inkscape:connector-curvature="0"
id="path196"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 70,88 c 0,-4.668 -6,-4.668 -6,0 0,4.668 6,4.668 6,0 z"
inkscape:connector-curvature="0"
id="path198"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 67,114.496 c 0,-4.926 0,-10.793 0,-15.75"
inkscape:connector-curvature="0"
id="path200"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 69.449,98.586 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path202"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 87,61.5 -40,0"
inkscape:connector-curvature="0"
id="path204"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 47,61.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path206"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 40,54.5 0,-7"
inkscape:connector-curvature="0"
id="path208"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 40,47.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path210"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 47,40.5 40,0"
inkscape:connector-curvature="0"
id="path212"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 87,40.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path214"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 94,47.5 0,7"
inkscape:connector-curvature="0"
id="path216"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 94,54.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path218"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,48,48.5)"
id="text220"><tspan
x="0 3.7499084 9.7497625 12.739689 15.729616 21.729469 24.719397 27.709324 33.709179"
y="0"
id="tspan222"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(1, 0, 1)</tspan></text>
<path
d="m 67,84.316 c 0,-3.699 0,-9.687 0,-15.57"
inkscape:connector-curvature="0"
id="path224"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 69.449,68.527 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path226"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 287,283.5 106,0 0,-21 -106,0 0,21 z"
inkscape:connector-curvature="0"
id="path228"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,294.5,270.5)"
id="text230"><tspan
x="0 3.7499084 9.7397623 12.72969 15.719617 18.709543 26.959343 29.949268 35.939121 38.92905 47.16885 50.158775 56.148628 59.148556 65.138412 68.128334 71.118263 77.118118 80.108047 86.107903"
y="0"
id="tspan232"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(E ::= E + • E ,2,4)</tspan></text>
<path
d="m 340,306.316 c 0,-3.699 0,-9.687 0,-15.57"
inkscape:connector-curvature="0"
id="path234"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 342.449,290.527 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path236"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 342.941,308.934 c 9.497,-3.446 40.137,-14.559 64.852,-23.528"
inkscape:connector-curvature="0"
id="path238"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 408.84,287.633 5.746,-4.688 -7.414,0.082 1.668,4.606 z"
inkscape:connector-curvature="0"
id="path240"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 313,236 c 0,-4.668 -6,-4.668 -6,0 0,4.668 6,4.668 6,0 z"
inkscape:connector-curvature="0"
id="path242"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 331.484,262.496 c -4.679,-5.769 -10.398,-12.82 -14.757,-18.199"
inkscape:connector-curvature="0"
id="path244"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 318.422,242.496 -6.313,-3.894 2.504,6.98 3.809,-3.086 z"
inkscape:connector-curvature="0"
id="path246"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 445,236 c 0,-4.668 -6,-4.668 -6,0 0,4.668 6,4.668 6,0 z"
inkscape:connector-curvature="0"
id="path248"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 442,262.496 c 0,-4.926 0,-10.793 0,-15.75"
inkscape:connector-curvature="0"
id="path250"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 444.449,246.586 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path252"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 253,209.5 -40,0"
inkscape:connector-curvature="0"
id="path254"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 213,209.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path256"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 206,202.5 0,-7"
inkscape:connector-curvature="0"
id="path258"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 206,195.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path260"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 213,188.5 40,0"
inkscape:connector-curvature="0"
id="path262"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 253,188.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path264"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 260,195.5 0,7"
inkscape:connector-curvature="0"
id="path266"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 260,202.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path268"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,214,196.5)"
id="text270"><tspan
x="0 3.7499084 9.7397623 12.72969 15.719617 21.719471 24.709396 27.699324 33.699177"
y="0"
id="tspan272"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(E, 2, 3)</tspan></text>
<path
d="m 307.074,234.594 c -7.535,-3.621 -28.258,-13.578 -45.875,-22.043"
inkscape:connector-curvature="0"
id="path274"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 262.25,210.336 -7.371,-0.824 5.246,5.242 2.125,-4.418 z"
inkscape:connector-curvature="0"
id="path276"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 331,209.5 -42,0"
inkscape:connector-curvature="0"
id="path278"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 289,209.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path280"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 282,202.5 0,-7"
inkscape:connector-curvature="0"
id="path282"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 282,195.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path284"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 289,188.5 42,0"
inkscape:connector-curvature="0"
id="path286"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 331,188.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path288"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 338,195.5 0,7"
inkscape:connector-curvature="0"
id="path290"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 338,202.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path292"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,290,196.5)"
id="text294"><tspan
x="0 3.7499084 11.989707 14.979634 17.969561 23.969416 26.959343 29.949268 35.949123"
y="0"
id="tspan296"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(+, 3, 4)</tspan></text>
<path
d="m 310,232.316 c 0,-3.699 0,-9.687 0,-15.57"
inkscape:connector-curvature="0"
id="path298"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 312.449,216.527 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path300"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 236,162 c 0,-4.668 -6,-4.668 -6,0 0,4.668 6,4.668 6,0 z"
inkscape:connector-curvature="0"
id="path302"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 233,188.496 c 0,-4.926 0,-10.793 0,-15.75"
inkscape:connector-curvature="0"
id="path304"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 235.449,172.586 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path306"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 253,135.5 -40,0"
inkscape:connector-curvature="0"
id="path308"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 213,135.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path310"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 206,128.5 0,-7"
inkscape:connector-curvature="0"
id="path312"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 206,121.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path314"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 213,114.5 40,0"
inkscape:connector-curvature="0"
id="path316"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 253,114.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path318"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 260,121.5 0,7"
inkscape:connector-curvature="0"
id="path320"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 260,128.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path322"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,214,122.5)"
id="text324"><tspan
x="0 3.7499084 9.7497625 12.739689 15.729616 21.729469 24.719397 27.709324 33.709179"
y="0"
id="tspan326"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(1, 2, 3)</tspan></text>
<path
d="m 233,158.316 c 0,-3.699 0,-9.687 0,-15.57"
inkscape:connector-curvature="0"
id="path328"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 235.449,142.527 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path330"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 462,209.5 -40,0"
inkscape:connector-curvature="0"
id="path332"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 422,209.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path334"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 415,202.5 0,-7"
inkscape:connector-curvature="0"
id="path336"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 415,195.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path338"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 422,188.5 40,0"
inkscape:connector-curvature="0"
id="path340"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 462,188.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path342"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 469,195.5 0,7"
inkscape:connector-curvature="0"
id="path344"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 469,202.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path346"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,423,196.5)"
id="text348"><tspan
x="0 3.7499084 9.7497625 12.739689 15.729616 21.729469 24.719397 27.709324 33.709179"
y="0"
id="tspan350"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(1, 4, 5)</tspan></text>
<path
d="m 442,232.316 c 0,-3.699 0,-9.687 0,-15.57"
inkscape:connector-curvature="0"
id="path352"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 444.449,216.527 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path354"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 256,310 c 0,-4.668 -6,-4.668 -6,0 0,4.668 6,4.668 6,0 z"
inkscape:connector-curvature="0"
id="path356"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 293.102,336.496 c -10.204,-6.742 -23.063,-15.238 -31.43,-20.766"
inkscape:connector-curvature="0"
id="path358"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 262.848,313.57 -7.188,-1.812 4.488,5.902 2.7,-4.09 z"
inkscape:connector-curvature="0"
id="path360"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 254.617,306.48 c 3.676,-7.976 13,-28.011 21.383,-44.48 7.992,-15.699 17.559,-33.328 24.504,-45.938"
inkscape:connector-curvature="0"
id="path362"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 302.824,216.93 1.242,-7.313 -5.531,4.942 4.289,2.371 z"
inkscape:connector-curvature="0"
id="path364"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 212,283.5 -40,0"
inkscape:connector-curvature="0"
id="path366"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 172,283.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path368"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 165,276.5 0,-7"
inkscape:connector-curvature="0"
id="path370"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 165,269.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path372"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 172,262.5 40,0"
inkscape:connector-curvature="0"
id="path374"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 212,262.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path376"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 219,269.5 0,7"
inkscape:connector-curvature="0"
id="path378"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 219,276.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path380"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,173,270.5)"
id="text382"><tspan
x="0 3.7499084 9.7397623 12.72969 15.719617 21.719471 24.709396 27.699324 33.699177"
y="0"
id="tspan384"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(E, 0, 3)</tspan></text>
<path
d="m 250.059,308.215 c -6.219,-3.77 -21.207,-12.863 -34.395,-20.863"
inkscape:connector-curvature="0"
id="path386"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 216.633,285.078 -7.254,-1.539 4.715,5.727 2.539,-4.188 z"
inkscape:connector-curvature="0"
id="path388"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 195,236 c 0,-4.668 -6,-4.668 -6,0 0,4.668 6,4.668 6,0 z"
inkscape:connector-curvature="0"
id="path390"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 192,262.496 c 0,-4.926 0,-10.793 0,-15.75"
inkscape:connector-curvature="0"
id="path392"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 194.449,246.586 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path394"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 189.059,234.215 c -6.219,-3.77 -21.207,-12.863 -34.395,-20.863"
inkscape:connector-curvature="0"
id="path396"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 155.633,211.078 -7.254,-1.539 4.715,5.727 2.539,-4.188 z"
inkscape:connector-curvature="0"
id="path398"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 194.691,233.574 c 4.286,-3.867 12.997,-11.73 21.032,-18.98"
inkscape:connector-curvature="0"
id="path400"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 217.559,216.234 3.554,-6.507 -6.84,2.871 3.286,3.636 z"
inkscape:connector-curvature="0"
id="path402"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /></g></g></g></g></g></g></svg>

+ 584
- 0
docs/_static/sppf/sppf_abcd.svg View File

@@ -0,0 +1,584 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="395"
height="398.75"
id="svg2"
xml:space="preserve"><metadata
id="metadata8"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs6"><clipPath
id="clipPath16"><path
d="M 0,0 394,0 394,398 0,398 0,0 z"
inkscape:connector-curvature="0"
id="path18" /></clipPath><clipPath
id="clipPath20"><path
d="m 36,362 323,0 0,-327 -323,0 0,327 z"
inkscape:connector-curvature="0"
id="path22" /></clipPath></defs><g
transform="matrix(1.25,0,0,-1.25,0,398.75)"
id="g10"><g
transform="translate(-39,-40)"
id="g12"><g
id="g14" /><g
id="g24"><g
clip-path="url(#clipPath16)"
id="g26"><g
id="g28"><path
d="m 36,362 323,0 0,-327 -323,0 0,327 z"
inkscape:connector-curvature="0"
id="path30"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /></g><g
id="g32"><g
clip-path="url(#clipPath20)"
id="g34"><path
d="m 36,362 323,0 0,-327 -323,0 0,327 z"
inkscape:connector-curvature="0"
id="path36"
style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 291,357.5 -40,0"
inkscape:connector-curvature="0"
id="path38"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 251,357.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path40"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 244,350.5 0,-7"
inkscape:connector-curvature="0"
id="path42"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 244,343.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path44"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 251,336.5 40,0"
inkscape:connector-curvature="0"
id="path46"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 291,336.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path48"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 298,343.5 0,7"
inkscape:connector-curvature="0"
id="path50"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 298,350.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path52"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,251.5,344.5)"
id="text54"><tspan
x="0 3.7499084 10.489744 13.479671 16.469599 22.469452 25.459379 28.449306 34.449158"
y="0"
id="tspan56"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(S, 0, 4)</tspan></text>
<path
d="m 274,310 c 0,-4.668 -6,-4.668 -6,0 0,4.668 6,4.668 6,0 z"
inkscape:connector-curvature="0"
id="path58"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 271,336.496 c 0,-4.926 0,-10.793 0,-15.75"
inkscape:connector-curvature="0"
id="path60"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 273.449,320.586 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path62"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 158,283.5 118,0 0,-21 -118,0 0,21 z"
inkscape:connector-curvature="0"
id="path64"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,166,270.5)"
id="text66"><tspan
x="0 3.7499084 10.489744 13.479671 16.469599 19.459526 27.709324 30.699251 37.449085 40.439014 47.18885 50.178776 56.928612 59.918537 65.908394 68.898315 76.398132 79.388062 82.377991 88.377846 91.367767 97.367622"
y="0"
id="tspan68"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(S ::= A B C • D ,0,3)</tspan></text>
<path
d="M 268.395,308.215 C 262.941,304.477 249.863,295.52 238.266,287.57"
inkscape:connector-curvature="0"
id="path70"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 239.543,285.477 -7.16,-1.938 4.39,5.981 2.77,-4.043 z"
inkscape:connector-curvature="0"
id="path72"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 347,283.5 -42,0"
inkscape:connector-curvature="0"
id="path74"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 305,283.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path76"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 298,276.5 0,-7"
inkscape:connector-curvature="0"
id="path78"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 298,269.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path80"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 305,262.5 42,0"
inkscape:connector-curvature="0"
id="path82"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 347,262.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path84"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 354,269.5 0,7"
inkscape:connector-curvature="0"
id="path86"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 354,276.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path88"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,306,270.5)"
id="text90"><tspan
x="0 3.7499084 11.249725 14.239653 17.22958 23.229433 26.21936 29.209288 35.209141"
y="0"
id="tspan92"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(D, 3, 4)</tspan></text>
<path
d="m 273.652,308.215 c 5.555,-3.738 18.875,-12.695 30.688,-20.645"
inkscape:connector-curvature="0"
id="path94"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 305.891,289.48 4.441,-5.941 -7.176,1.875 2.735,4.066 z"
inkscape:connector-curvature="0"
id="path96"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 220,236 c 0,-4.668 -6,-4.668 -6,0 0,4.668 6,4.668 6,0 z"
inkscape:connector-curvature="0"
id="path98"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 217,262.496 c 0,-4.926 0,-10.793 0,-15.75"
inkscape:connector-curvature="0"
id="path100"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 219.449,246.586 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path102"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 329,236 c 0,-4.668 -6,-4.668 -6,0 0,4.668 6,4.668 6,0 z"
inkscape:connector-curvature="0"
id="path104"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 326,262.496 c 0,-4.926 0,-10.793 0,-15.75"
inkscape:connector-curvature="0"
id="path106"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 328.449,246.586 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path108"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 67,209.5 118,0 0,-21 -118,0 0,21 z"
inkscape:connector-curvature="0"
id="path110"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,75,196.5)"
id="text112"><tspan
x="0 3.7499084 10.489744 13.479671 16.469599 19.459526 27.709324 30.699251 37.449085 40.439014 47.18885 50.178776 56.168629 59.158558 65.908394 68.898315 76.398132 79.388062 82.377991 88.377846 91.367767 97.367622"
y="0"
id="tspan114"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(S ::= A B • C D ,0,2)</tspan></text>
<path
d="m 213.973,234.77 c -8.571,-3.485 -33.758,-13.727 -54.996,-22.364"
inkscape:connector-curvature="0"
id="path116"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 159.652,210.039 -7.406,-0.367 5.559,4.906 1.847,-4.539 z"
inkscape:connector-curvature="0"
id="path118"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 254,209.5 -40,0"
inkscape:connector-curvature="0"
id="path120"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 214,209.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path122"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 207,202.5 0,-7"
inkscape:connector-curvature="0"
id="path124"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 207,195.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path126"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 214,188.5 40,0"
inkscape:connector-curvature="0"
id="path128"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 254,188.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path130"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 261,195.5 0,7"
inkscape:connector-curvature="0"
id="path132"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 261,202.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path134"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,214.5,196.5)"
id="text136"><tspan
x="0 3.7499084 10.499743 13.489671 16.479597 22.479452 25.469379 28.459305 34.45916"
y="0"
id="tspan138"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(C, 2, 3)</tspan></text>
<path
d="m 218.57,232.586 c 1.739,-3.785 4.672,-10.172 7.52,-16.367"
inkscape:connector-curvature="0"
id="path140"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 228.387,217.086 0.695,-7.383 -5.148,5.336 4.453,2.047 z"
inkscape:connector-curvature="0"
id="path142"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 129,162 c 0,-4.668 -6,-4.668 -6,0 0,4.668 6,4.668 6,0 z"
inkscape:connector-curvature="0"
id="path144"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 126,188.496 c 0,-4.926 0,-10.793 0,-15.75"
inkscape:connector-curvature="0"
id="path146"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 128.449,172.586 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path148"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 237,162 c 0,-4.668 -6,-4.668 -6,0 0,4.668 6,4.668 6,0 z"
inkscape:connector-curvature="0"
id="path150"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 234,188.496 c 0,-4.926 0,-10.793 0,-15.75"
inkscape:connector-curvature="0"
id="path152"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 236.449,172.586 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path154"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 87,135.5 -40,0"
inkscape:connector-curvature="0"
id="path156"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 47,135.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path158"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 40,128.5 0,-7"
inkscape:connector-curvature="0"
id="path160"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 40,121.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path162"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 47,114.5 40,0"
inkscape:connector-curvature="0"
id="path164"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 87,114.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path166"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 94,121.5 0,7"
inkscape:connector-curvature="0"
id="path168"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 94,128.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path170"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,47.5,122.5)"
id="text172"><tspan
x="0 3.7499084 10.499743 13.489671 16.479597 22.479452 25.469379 28.459305 34.45916"
y="0"
id="tspan174"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(A, 0, 1)</tspan></text>
<path
d="m 123.156,160.215 c -6.015,-3.77 -20.511,-12.863 -33.269,-20.863"
inkscape:connector-curvature="0"
id="path176"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 91.039,137.184 -7.23,-1.645 4.629,5.797 2.601,-4.152 z"
inkscape:connector-curvature="0"
id="path178"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 163,135.5 -40,0"
inkscape:connector-curvature="0"
id="path180"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 123,135.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path182"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 116,128.5 0,-7"
inkscape:connector-curvature="0"
id="path184"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 116,121.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path186"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 123,114.5 40,0"
inkscape:connector-curvature="0"
id="path188"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 163,114.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path190"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 170,121.5 0,7"
inkscape:connector-curvature="0"
id="path192"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 170,128.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path194"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,123.5,122.5)"
id="text196"><tspan
x="0 3.7499084 10.499743 13.489671 16.479597 22.479452 25.469379 28.459305 34.45916"
y="0"
id="tspan198"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(B, 1, 2)</tspan></text>
<path
d="m 127.57,158.586 c 1.739,-3.785 4.672,-10.172 7.52,-16.367"
inkscape:connector-curvature="0"
id="path200"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 137.387,143.086 0.695,-7.383 -5.148,5.336 4.453,2.047 z"
inkscape:connector-curvature="0"
id="path202"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 70,88 c 0,-4.668 -6,-4.668 -6,0 0,4.668 6,4.668 6,0 z"
inkscape:connector-curvature="0"
id="path204"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 67,114.496 c 0,-4.926 0,-10.793 0,-15.75"
inkscape:connector-curvature="0"
id="path206"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 69.449,98.586 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path208"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 146,88 c 0,-4.668 -6,-4.668 -6,0 0,4.668 6,4.668 6,0 z"
inkscape:connector-curvature="0"
id="path210"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 143,114.496 c 0,-4.926 0,-10.793 0,-15.75"
inkscape:connector-curvature="0"
id="path212"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 145.449,98.586 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path214"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 87,61.5 -40,0"
inkscape:connector-curvature="0"
id="path216"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 47,61.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path218"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 40,54.5 0,-7"
inkscape:connector-curvature="0"
id="path220"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 40,47.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path222"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 47,40.5 40,0"
inkscape:connector-curvature="0"
id="path224"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 87,40.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path226"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 94,47.5 0,7"
inkscape:connector-curvature="0"
id="path228"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 94,54.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path230"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,48,48.5)"
id="text232"><tspan
x="0 3.7499084 9.7397623 12.72969 15.719617 21.719471 24.709396 27.699324 33.699177"
y="0"
id="tspan234"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(a, 0, 1)</tspan></text>
<path
d="m 67,84.316 c 0,-3.699 0,-9.687 0,-15.57"
inkscape:connector-curvature="0"
id="path236"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 69.449,68.527 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path238"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 163,61.5 -40,0"
inkscape:connector-curvature="0"
id="path240"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 123,61.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path242"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 116,54.5 0,-7"
inkscape:connector-curvature="0"
id="path244"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 116,47.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path246"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 123,40.5 40,0"
inkscape:connector-curvature="0"
id="path248"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 163,40.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path250"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 170,47.5 0,7"
inkscape:connector-curvature="0"
id="path252"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 170,54.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path254"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,124,48.5)"
id="text256"><tspan
x="0 3.7499084 9.7397623 12.72969 15.719617 21.719471 24.709396 27.699324 33.699177"
y="0"
id="tspan258"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(b, 1, 2)</tspan></text>
<path
d="m 143,84.316 c 0,-3.699 0,-9.687 0,-15.57"
inkscape:connector-curvature="0"
id="path260"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 145.449,68.527 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path262"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 253,135.5 -38,0"
inkscape:connector-curvature="0"
id="path264"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 215,135.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path266"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 208,128.5 0,-7"
inkscape:connector-curvature="0"
id="path268"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 208,121.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path270"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 215,114.5 38,0"
inkscape:connector-curvature="0"
id="path272"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 253,114.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path274"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 260,121.5 0,7"
inkscape:connector-curvature="0"
id="path276"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 260,128.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path278"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,215.5,122.5)"
id="text280"><tspan
x="0 3.7499084 8.9897804 11.979708 14.969635 20.969488 23.959415 26.949343 32.949196"
y="0"
id="tspan282"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(c, 2, 3)</tspan></text>
<path
d="m 234,158.316 c 0,-3.699 0,-9.687 0,-15.57"
inkscape:connector-curvature="0"
id="path284"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 236.449,142.527 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path286"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 346,209.5 -40,0"
inkscape:connector-curvature="0"
id="path288"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 306,209.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path290"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 299,202.5 0,-7"
inkscape:connector-curvature="0"
id="path292"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 299,195.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path294"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 306,188.5 40,0"
inkscape:connector-curvature="0"
id="path296"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 346,188.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path298"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 353,195.5 0,7"
inkscape:connector-curvature="0"
id="path300"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 353,202.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path302"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,307,196.5)"
id="text304"><tspan
x="0 3.7499084 9.7397623 12.72969 15.719617 21.719471 24.709396 27.699324 33.699177"
y="0"
id="tspan306"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(d, 3, 4)</tspan></text>
<path
d="m 326,232.316 c 0,-3.699 0,-9.687 0,-15.57"
inkscape:connector-curvature="0"
id="path308"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 328.449,216.527 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path310"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /></g></g></g></g></g></g></svg>

+ 522
- 0
docs/_static/sppf/sppf_abcd_noint.svg View File

@@ -0,0 +1,522 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="357.5"
height="213.75"
id="svg2"
xml:space="preserve"><metadata
id="metadata8"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs6"><clipPath
id="clipPath16"><path
d="M 0,0 364,0 364,250 0,250 0,0 z"
inkscape:connector-curvature="0"
id="path18" /></clipPath><clipPath
id="clipPath20"><path
d="m 36,214 293,0 0,-179 -293,0 0,179 z"
inkscape:connector-curvature="0"
id="path22" /></clipPath></defs><g
transform="matrix(1.25,0,0,-1.25,0,213.75)"
id="g10"><g
transform="translate(-39,-40)"
id="g12"><g
id="g14" /><g
id="g24"><g
clip-path="url(#clipPath16)"
id="g26"><g
id="g28"><path
d="m 36,214 293,0 0,-179 -293,0 0,179 z"
inkscape:connector-curvature="0"
id="path30"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /></g><g
id="g32"><g
clip-path="url(#clipPath20)"
id="g34"><path
d="m 36,214 293,0 0,-179 -293,0 0,179 z"
inkscape:connector-curvature="0"
id="path36"
style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 201,209.5 -40,0"
inkscape:connector-curvature="0"
id="path38"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 161,209.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path40"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 154,202.5 0,-7"
inkscape:connector-curvature="0"
id="path42"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 154,195.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path44"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 161,188.5 40,0"
inkscape:connector-curvature="0"
id="path46"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 201,188.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path48"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 208,195.5 0,7"
inkscape:connector-curvature="0"
id="path50"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 208,202.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path52"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,161.5,196.5)"
id="text54"><tspan
x="0 3.7499084 10.489744 13.479671 16.469599 22.469452 25.459379 28.449306 34.449158"
y="0"
id="tspan56"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(S, 0, 4)</tspan></text>
<path
d="m 184,162 c 0,-4.668 -6,-4.668 -6,0 0,4.668 6,4.668 6,0 z"
inkscape:connector-curvature="0"
id="path58"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 181,188.496 c 0,-4.926 0,-10.793 0,-15.75"
inkscape:connector-curvature="0"
id="path60"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 183.449,172.586 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path62"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 87,135.5 -40,0"
inkscape:connector-curvature="0"
id="path64"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 47,135.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path66"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 40,128.5 0,-7"
inkscape:connector-curvature="0"
id="path68"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 40,121.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path70"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 47,114.5 40,0"
inkscape:connector-curvature="0"
id="path72"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 87,114.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path74"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 94,121.5 0,7"
inkscape:connector-curvature="0"
id="path76"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 94,128.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path78"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,47.5,122.5)"
id="text80"><tspan
x="0 3.7499084 10.499743 13.489671 16.479597 22.479452 25.469379 28.459305 34.45916"
y="0"
id="tspan82"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(A, 0, 1)</tspan></text>
<path
d="m 177.949,161.012 c -10.836,-3.52 -48.363,-15.7 -76.801,-24.93"
inkscape:connector-curvature="0"
id="path84"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 101.555,133.641 -7.418,0.168 5.902,4.492 1.516,-4.66 z"
inkscape:connector-curvature="0"
id="path86"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 163,135.5 -40,0"
inkscape:connector-curvature="0"
id="path88"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 123,135.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path90"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 116,128.5 0,-7"
inkscape:connector-curvature="0"
id="path92"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 116,121.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path94"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 123,114.5 40,0"
inkscape:connector-curvature="0"
id="path96"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 163,114.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path98"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 170,121.5 0,7"
inkscape:connector-curvature="0"
id="path100"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 170,128.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path102"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,123.5,122.5)"
id="text104"><tspan
x="0 3.7499084 10.499743 13.489671 16.479597 22.479452 25.469379 28.459305 34.45916"
y="0"
id="tspan106"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(B, 1, 2)</tspan></text>
<path
d="m 178.508,159.574 c -3.895,-3.793 -11.738,-11.429 -19.063,-18.562"
inkscape:connector-curvature="0"
id="path108"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 160.742,138.855 -6.726,-3.128 3.308,6.64 3.418,-3.512 z"
inkscape:connector-curvature="0"
id="path110"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 239,135.5 -40,0"
inkscape:connector-curvature="0"
id="path112"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 199,135.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path114"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 192,128.5 0,-7"
inkscape:connector-curvature="0"
id="path116"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 192,121.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path118"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 199,114.5 40,0"
inkscape:connector-curvature="0"
id="path120"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 239,114.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path122"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 246,121.5 0,7"
inkscape:connector-curvature="0"
id="path124"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 246,128.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path126"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,199.5,122.5)"
id="text128"><tspan
x="0 3.7499084 10.499743 13.489671 16.479597 22.479452 25.469379 28.459305 34.45916"
y="0"
id="tspan130"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(C, 2, 3)</tspan></text>
<path
d="m 183.492,159.574 c 3.895,-3.793 11.738,-11.429 19.063,-18.562"
inkscape:connector-curvature="0"
id="path132"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 204.676,142.367 3.308,-6.64 -6.726,3.128 3.418,3.512 z"
inkscape:connector-curvature="0"
id="path134"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 317,135.5 -42,0"
inkscape:connector-curvature="0"
id="path136"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 275,135.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path138"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 268,128.5 0,-7"
inkscape:connector-curvature="0"
id="path140"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 268,121.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path142"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 275,114.5 42,0"
inkscape:connector-curvature="0"
id="path144"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 317,114.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path146"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 324,121.5 0,7"
inkscape:connector-curvature="0"
id="path148"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 324,128.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path150"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,276,122.5)"
id="text152"><tspan
x="0 3.7499084 11.249725 14.239653 17.22958 23.229433 26.21936 29.209288 35.209141"
y="0"
id="tspan154"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(D, 3, 4)</tspan></text>
<path
d="m 184.074,161.012 c 10.883,-3.5 48.426,-15.582 77.059,-24.793"
inkscape:connector-curvature="0"
id="path156"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 261.918,138.539 5.91,-4.477 -7.414,-0.187 1.504,4.664 z"
inkscape:connector-curvature="0"
id="path158"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 70,88 c 0,-4.668 -6,-4.668 -6,0 0,4.668 6,4.668 6,0 z"
inkscape:connector-curvature="0"
id="path160"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 67,114.496 c 0,-4.926 0,-10.793 0,-15.75"
inkscape:connector-curvature="0"
id="path162"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 69.449,98.586 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path164"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 146,88 c 0,-4.668 -6,-4.668 -6,0 0,4.668 6,4.668 6,0 z"
inkscape:connector-curvature="0"
id="path166"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 143,114.496 c 0,-4.926 0,-10.793 0,-15.75"
inkscape:connector-curvature="0"
id="path168"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 145.449,98.586 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path170"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 222,88 c 0,-4.668 -6,-4.668 -6,0 0,4.668 6,4.668 6,0 z"
inkscape:connector-curvature="0"
id="path172"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 219,114.496 c 0,-4.926 0,-10.793 0,-15.75"
inkscape:connector-curvature="0"
id="path174"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 221.449,98.586 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path176"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 299,88 c 0,-4.668 -6,-4.668 -6,0 0,4.668 6,4.668 6,0 z"
inkscape:connector-curvature="0"
id="path178"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 296,114.496 c 0,-4.926 0,-10.793 0,-15.75"
inkscape:connector-curvature="0"
id="path180"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 298.449,98.586 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path182"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 87,61.5 -40,0"
inkscape:connector-curvature="0"
id="path184"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 47,61.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path186"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 40,54.5 0,-7"
inkscape:connector-curvature="0"
id="path188"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 40,47.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path190"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 47,40.5 40,0"
inkscape:connector-curvature="0"
id="path192"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 87,40.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path194"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 94,47.5 0,7"
inkscape:connector-curvature="0"
id="path196"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 94,54.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path198"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,48,48.5)"
id="text200"><tspan
x="0 3.7499084 9.7397623 12.72969 15.719617 21.719471 24.709396 27.699324 33.699177"
y="0"
id="tspan202"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(a, 0, 1)</tspan></text>
<path
d="m 67,84.316 c 0,-3.699 0,-9.687 0,-15.57"
inkscape:connector-curvature="0"
id="path204"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 69.449,68.527 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path206"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 163,61.5 -40,0"
inkscape:connector-curvature="0"
id="path208"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 123,61.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path210"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 116,54.5 0,-7"
inkscape:connector-curvature="0"
id="path212"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 116,47.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path214"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 123,40.5 40,0"
inkscape:connector-curvature="0"
id="path216"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 163,40.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path218"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 170,47.5 0,7"
inkscape:connector-curvature="0"
id="path220"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 170,54.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path222"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,124,48.5)"
id="text224"><tspan
x="0 3.7499084 9.7397623 12.72969 15.719617 21.719471 24.709396 27.699324 33.699177"
y="0"
id="tspan226"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(b, 1, 2)</tspan></text>
<path
d="m 143,84.316 c 0,-3.699 0,-9.687 0,-15.57"
inkscape:connector-curvature="0"
id="path228"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 145.449,68.527 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path230"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 238,61.5 -38,0"
inkscape:connector-curvature="0"
id="path232"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 200,61.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path234"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 193,54.5 0,-7"
inkscape:connector-curvature="0"
id="path236"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 193,47.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path238"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 200,40.5 38,0"
inkscape:connector-curvature="0"
id="path240"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 238,40.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path242"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 245,47.5 0,7"
inkscape:connector-curvature="0"
id="path244"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 245,54.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path246"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,200.5,48.5)"
id="text248"><tspan
x="0 3.7499084 8.9897804 11.979708 14.969635 20.969488 23.959415 26.949343 32.949196"
y="0"
id="tspan250"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(c, 2, 3)</tspan></text>
<path
d="m 219,84.316 c 0,-3.699 0,-9.687 0,-15.57"
inkscape:connector-curvature="0"
id="path252"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 221.449,68.527 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path254"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 316,61.5 -40,0"
inkscape:connector-curvature="0"
id="path256"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 276,61.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path258"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 269,54.5 0,-7"
inkscape:connector-curvature="0"
id="path260"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 269,47.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path262"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 276,40.5 40,0"
inkscape:connector-curvature="0"
id="path264"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 316,40.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path266"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 323,47.5 0,7"
inkscape:connector-curvature="0"
id="path268"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 323,54.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path270"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,277,48.5)"
id="text272"><tspan
x="0 3.7499084 9.7397623 12.72969 15.719617 21.719471 24.709396 27.699324 33.699177"
y="0"
id="tspan274"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(d, 3, 4)</tspan></text>
<path
d="m 296,84.316 c 0,-3.699 0,-9.687 0,-15.57"
inkscape:connector-curvature="0"
id="path276"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 298.449,68.527 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path278"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /></g></g></g></g></g></g></svg>

+ 682
- 0
docs/_static/sppf/sppf_cycle.svg View File

@@ -0,0 +1,682 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="1248.75"
height="442.5"
id="svg2"
xml:space="preserve"><metadata
id="metadata8"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs6"><clipPath
id="clipPath16"><path
d="M 0,0 999,0 999,372 0,372 0,0 z"
inkscape:connector-curvature="0"
id="path18" /></clipPath><clipPath
id="clipPath20"><path
d="m 0,0 1077,0 0,432 L 0,432 0,0 z"
inkscape:connector-curvature="0"
id="path22" /></clipPath><clipPath
id="clipPath24"><path
d="m 36,396 1006,0 0,-361 -1006,0 0,361 z"
inkscape:connector-curvature="0"
id="path26" /></clipPath></defs><g
transform="matrix(1.25,0,0,-1.25,0,442.5)"
id="g10"><g
transform="translate(0,-9)"
id="g12"><g
id="g14" /><g
id="g28"><g
clip-path="url(#clipPath16)"
id="g30"><g
transform="translate(-39,-30)"
id="g32"><g
id="g34" /><g
id="g36"><g
clip-path="url(#clipPath20)"
id="g38"><g
id="g40"><path
d="m 36,396 1006,0 0,-361 -1006,0 0,361 z"
inkscape:connector-curvature="0"
id="path42"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /></g><g
id="g44"><g
clip-path="url(#clipPath24)"
id="g46"><path
d="m 36,396 1006,0 0,-361 -1006,0 0,361 z"
inkscape:connector-curvature="0"
id="path48"
style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 422,391.5 -40,0"
inkscape:connector-curvature="0"
id="path50"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 382,391.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path52"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 375,384.5 0,-7"
inkscape:connector-curvature="0"
id="path54"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 375,377.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path56"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 382,370.5 40,0"
inkscape:connector-curvature="0"
id="path58"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 422,370.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path60"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 429,377.5 0,7"
inkscape:connector-curvature="0"
id="path62"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 429,384.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path64"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,382.5,378.5)"
id="text66"><tspan
x="0 3.7499084 10.489744 13.479671 16.469599 22.469452 25.459379 28.449306 34.449158"
y="0"
id="tspan68"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(S, 0, 1)</tspan></text>
<path
d="m 141.91,326 c 0,-8.199 -22.793,-14.848 -50.91,-14.848 -28.117,0 -50.91,6.649 -50.91,14.848 0,8.199 22.793,14.848 50.91,14.848 28.117,0 50.91,-6.649 50.91,-14.848 z"
inkscape:connector-curvature="0"
id="path70"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,63,323.5)"
id="text72"><tspan
x="0 3.7499084 10.489744 13.479671 16.469599 19.459526 27.709324 30.699251 36.689106 42.678959 45.668884 51.668739"
y="0"
id="tspan74"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(S ::= a•,0)</tspan></text>
<path
d="M 374.699,376.789 C 330.242,369.855 239.492,355.363 163,341 c -7.59,-1.426 -15.613,-3.016 -23.441,-4.609"
inkscape:connector-curvature="0"
id="path76"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 139.793,333.938 -7.348,0.988 6.364,3.812 0.984,-4.8 z"
inkscape:connector-curvature="0"
id="path78"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 301.188,326 c 0,-8.199 -26.051,-14.848 -58.188,-14.848 -32.137,0 -58.188,6.649 -58.188,14.848 0,8.199 26.051,14.848 58.188,14.848 32.137,0 58.188,-6.649 58.188,-14.848 z"
inkscape:connector-curvature="0"
id="path80"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,209.5,323.5)"
id="text82"><tspan
x="0 3.7499084 10.489744 13.479671 16.469599 19.459526 27.709324 30.699251 37.439087 40.429012 47.16885 53.168701 56.15863 62.148483"
y="0"
id="tspan84"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(S ::= S S•,1)</tspan></text>
<path
d="m 374.668,371.547 c -24.824,-8.59 -61.801,-21.379 -90.254,-31.223"
inkscape:connector-curvature="0"
id="path86"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 285.164,337.992 -7.418,0.028 5.816,4.605 1.602,-4.633 z"
inkscape:connector-curvature="0"
id="path88"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 460.188,326 c 0,-8.199 -26.051,-14.848 -58.188,-14.848 -32.137,0 -58.188,6.649 -58.188,14.848 0,8.199 26.051,14.848 58.188,14.848 32.137,0 58.188,-6.649 58.188,-14.848 z"
inkscape:connector-curvature="0"
id="path90"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,368.5,323.5)"
id="text92"><tspan
x="0 3.7499084 10.489744 13.479671 16.469599 19.459526 27.709324 30.699251 37.439087 40.429012 47.16885 53.168701 56.15863 62.148483"
y="0"
id="tspan94"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(S ::= S S•,0)</tspan></text>
<path
d="m 389.266,370.395 c -2.594,-6.274 -3.516,-14.465 -2.762,-22.098"
inkscape:connector-curvature="0"
id="path96"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 389,348.258 -1.188,-7.32 -3.636,6.464 4.824,0.856 z"
inkscape:connector-curvature="0"
id="path98"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 111,281.5 -40,0"
inkscape:connector-curvature="0"
id="path100"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 71,281.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path102"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 64,274.5 0,-7"
inkscape:connector-curvature="0"
id="path104"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 64,267.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path106"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 71,260.5 40,0"
inkscape:connector-curvature="0"
id="path108"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 111,260.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path110"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 118,267.5 0,7"
inkscape:connector-curvature="0"
id="path112"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 118,274.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path114"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,72,268.5)"
id="text116"><tspan
x="0 3.7499084 9.7397623 12.72969 15.719617 21.719471 24.709396 27.699324 33.699177"
y="0"
id="tspan118"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(a, 0, 1)</tspan></text>
<path
d="m 91,310.973 c 0,-6.821 0,-14.942 0,-22.075"
inkscape:connector-curvature="0"
id="path120"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 93.449,288.652 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path122"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 211,281.5 98,0 0,-21 -98,0 0,21 z"
inkscape:connector-curvature="0"
id="path124"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,219,268.5)"
id="text126"><tspan
x="0 3.7499084 10.489744 13.479671 16.469599 19.459526 27.709324 30.699251 37.439087 40.429012 46.418869 49.408794 56.15863 59.148556 62.138485 68.138336 71.128265 77.12812"
y="0"
id="tspan128"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(S ::= S • S ,0,1)</tspan></text>
<path
d="m 247.645,310.973 c 2.152,-6.969 4.726,-15.285 6.964,-22.528"
inkscape:connector-curvature="0"
id="path130"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 256.98,289.066 -0.273,-7.414 -4.406,5.965 4.679,1.449 z"
inkscape:connector-curvature="0"
id="path132"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 637,281.5 -40,0"
inkscape:connector-curvature="0"
id="path134"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 597,281.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path136"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 590,274.5 0,-7"
inkscape:connector-curvature="0"
id="path138"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 590,267.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path140"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 597,260.5 40,0"
inkscape:connector-curvature="0"
id="path142"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 637,260.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path144"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 644,267.5 0,7"
inkscape:connector-curvature="0"
id="path146"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 644,274.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path148"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,597.5,268.5)"
id="text150"><tspan
x="0 3.7499084 10.489744 13.479671 16.469599 22.469452 25.459379 28.449306 34.449158"
y="0"
id="tspan152"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(S, 1, 1)</tspan></text>
<path
d="M 289.297,316.785 C 300.016,314.77 311.391,312.727 322,311 415.98,295.719 527.473,281.715 582.602,275.066"
inkscape:connector-curvature="0"
id="path154"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 583.02,277.484 6.66,-3.269 -7.242,-1.598 0.582,4.867 z"
inkscape:connector-curvature="0"
id="path156"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 416.746,340.523 c 2.055,7.036 2.305,15.547 0.75,22.942"
inkscape:connector-curvature="0"
id="path158"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 415.082,362.984 0.262,7.411 4.418,-5.957 -4.68,-1.454 z"
inkscape:connector-curvature="0"
id="path160"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 819,281.5 98,0 0,-21 -98,0 0,21 z"
inkscape:connector-curvature="0"
id="path162"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,827,268.5)"
id="text164"><tspan
x="0 3.7499084 10.489744 13.479671 16.469599 19.459526 27.709324 30.699251 37.439087 40.429012 46.418869 49.408794 56.15863 59.148556 62.138485 68.138336 71.128265 77.12812"
y="0"
id="tspan166"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(S ::= S • S ,0,0)</tspan></text>
<path
d="m 455.004,319.746 c 88.605,-10.461 266.012,-31.398 356.742,-42.105"
inkscape:connector-curvature="0"
id="path168"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 812.258,280.047 6.664,-3.254 -7.238,-1.613 0.574,4.867 z"
inkscape:connector-curvature="0"
id="path170"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 339.934,216 c 0,-8.199 -28.176,-14.848 -62.934,-14.848 -34.758,0 -62.934,6.649 -62.934,14.848 0,8.199 28.176,14.848 62.934,14.848 34.758,0 62.934,-6.649 62.934,-14.848 z"
inkscape:connector-curvature="0"
id="path172"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,240.5,213.5)"
id="text174"><tspan
x="0 3.7499084 10.489744 13.479671 16.469599 19.459526 27.709324 30.699251 37.439087 40.429012 46.418869 49.408794 56.15863 59.148556 62.138485 68.138336"
y="0"
id="tspan176"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(S ::= S • S ,0)</tspan></text>
<path
d="m 263.277,260.395 c 1.95,-6.309 4.5,-14.555 6.871,-22.227"
inkscape:connector-curvature="0"
id="path178"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 272.527,238.77 -0.273,-7.415 -4.41,5.965 4.683,1.45 z"
inkscape:connector-curvature="0"
id="path180"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 516.203,216 c 0,-8.199 -22.476,-14.848 -50.203,-14.848 -27.727,0 -50.203,6.649 -50.203,14.848 0,8.199 22.476,14.848 50.203,14.848 27.727,0 50.203,-6.649 50.203,-14.848 z"
inkscape:connector-curvature="0"
id="path182"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,438.5,213.5)"
id="text184"><tspan
x="0 3.7499084 10.489744 13.479671 16.469599 19.459526 27.709324"
y="0"
id="tspan186"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(S ::= </tspan></text>
<text
transform="matrix(1,0,0,-1,469.19925,213.5)"
id="text188"><tspan
x="0"
y="0"
id="tspan190"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">ε</tspan></text>
<text
transform="matrix(1,0,0,-1,474.43912,213.5)"
id="text192"><tspan
x="0 5.9898539 8.9897804 14.979634"
y="0"
id="tspan194"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">•,1)</tspan></text>
<path
d="m 589.656,261.039 c -23.82,-8.676 -58.609,-21.348 -85.226,-31.043"
inkscape:connector-curvature="0"
id="path196"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 505.266,227.695 -7.414,-0.093 5.738,4.699 1.676,-4.606 z"
inkscape:connector-curvature="0"
id="path198"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 675.188,216 c 0,-8.199 -26.051,-14.848 -58.188,-14.848 -32.137,0 -58.188,6.649 -58.188,14.848 0,8.199 26.051,14.848 58.188,14.848 32.137,0 58.188,-6.649 58.188,-14.848 z"
inkscape:connector-curvature="0"
id="path200"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,583.5,213.5)"
id="text202"><tspan
x="0 3.7499084 10.489744 13.479671 16.469599 19.459526 27.709324 30.699251 37.439087 40.429012 47.16885 53.168701 56.15863 62.148483"
y="0"
id="tspan204"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(S ::= S S•,1)</tspan></text>
<path
d="m 604.266,260.395 c -2.594,-6.274 -3.516,-14.465 -2.762,-22.098"
inkscape:connector-curvature="0"
id="path206"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 604,238.258 -1.188,-7.32 -3.636,6.464 4.824,0.856 z"
inkscape:connector-curvature="0"
id="path208"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 329.922,224.246 c 48.89,10.731 118.82,34.688 152.078,86.754 7.176,11.238 7.008,18.656 0,30 -10,16.18 -28.75,26.035 -45.582,31.914"
inkscape:connector-curvature="0"
id="path210"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 435.258,370.711 -5.906,4.484 7.414,0.18 -1.508,-4.664 z"
inkscape:connector-curvature="0"
id="path212"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 485,171.5 -38,0"
inkscape:connector-curvature="0"
id="path214"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 447,171.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path216"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 440,164.5 0,-7"
inkscape:connector-curvature="0"
id="path218"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 440,157.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path220"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 447,150.5 38,0"
inkscape:connector-curvature="0"
id="path222"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 485,150.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path224"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 492,157.5 0,7"
inkscape:connector-curvature="0"
id="path226"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 492,164.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path228"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,447.5,158.5)"
id="text230"><tspan
x="0"
y="0"
id="tspan232"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(</tspan></text>
<text
transform="matrix(1,0,0,-1,451.24991,158.5)"
id="text234"><tspan
x="0"
y="0"
id="tspan236"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">ε</tspan></text>
<text
transform="matrix(1,0,0,-1,456.48978,158.5)"
id="text238"><tspan
x="0 2.9899271 5.9898539 11.979708 14.979634 17.969561 23.959415"
y="0"
id="tspan240"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">, 1, 1)</tspan></text>
<path
d="m 466,200.973 c 0,-6.821 0,-14.942 0,-22.075"
inkscape:connector-curvature="0"
id="path242"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 468.449,178.652 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path244"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 631.746,230.523 c 2.055,7.036 2.305,15.547 0.75,22.942"
inkscape:connector-curvature="0"
id="path246"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 630.082,252.984 0.262,7.411 4.418,-5.957 -4.68,-1.454 z"
inkscape:connector-curvature="0"
id="path248"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 572,171.5 98,0 0,-21 -98,0 0,21 z"
inkscape:connector-curvature="0"
id="path250"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,580,158.5)"
id="text252"><tspan
x="0 3.7499084 10.489744 13.479671 16.469599 19.459526 27.709324 30.699251 37.439087 40.429012 46.418869 49.408794 56.15863 59.148556 62.138485 68.138336 71.128265 77.12812"
y="0"
id="tspan254"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(S ::= S • S ,1,1)</tspan></text>
<path
d="m 618.094,200.973 c 0.496,-6.821 1.086,-14.942 1.605,-22.075"
inkscape:connector-curvature="0"
id="path256"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 622.16,178.812 -1.933,-7.16 -2.954,6.805 4.887,0.355 z"
inkscape:connector-curvature="0"
id="path258"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 700.934,106 c 0,-8.199 -28.176,-14.848 -62.934,-14.848 -34.758,0 -62.934,6.649 -62.934,14.848 0,8.199 28.176,14.848 62.934,14.848 34.758,0 62.934,-6.649 62.934,-14.848 z"
inkscape:connector-curvature="0"
id="path260"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,601.5,103.5)"
id="text262"><tspan
x="0 3.7499084 10.489744 13.479671 16.469599 19.459526 27.709324 30.699251 37.439087 40.429012 46.418869 49.408794 56.15863 59.148556 62.138485 68.138336"
y="0"
id="tspan264"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(S ::= S • S ,1)</tspan></text>
<path
d="m 624.277,150.395 c 1.95,-6.309 4.5,-14.555 6.871,-22.227"
inkscape:connector-curvature="0"
id="path266"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 633.527,128.77 -0.273,-7.415 -4.41,5.965 4.683,1.45 z"
inkscape:connector-curvature="0"
id="path268"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 662.836,119.992 c 10.824,7.453 22.531,17.645 29.164,30.008 3.754,6.996 11.863,69.898 5,81 -10,16.18 -28.75,26.035 -45.582,31.914"
inkscape:connector-curvature="0"
id="path270"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 650.258,260.711 -5.906,4.484 7.414,0.18 -1.508,-4.664 z"
inkscape:connector-curvature="0"
id="path272"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 930.934,216 c 0,-8.199 -28.176,-14.848 -62.934,-14.848 -34.758,0 -62.934,6.649 -62.934,14.848 0,8.199 28.176,14.848 62.934,14.848 34.758,0 62.934,-6.649 62.934,-14.848 z"
inkscape:connector-curvature="0"
id="path274"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,831.5,213.5)"
id="text276"><tspan
x="0 3.7499084 10.489744 13.479671 16.469599 19.459526 27.709324 30.699251 37.439087 40.429012 46.418869 49.408794 56.15863 59.148556 62.138485 68.138336"
y="0"
id="tspan278"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(S ::= S • S ,0)</tspan></text>
<path
d="m 868,260.395 c 0,-6.18 0,-14.219 0,-21.758"
inkscape:connector-curvature="0"
id="path280"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 870.449,238.355 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path282"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 906,171.5 -40,0"
inkscape:connector-curvature="0"
id="path284"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 866,171.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path286"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 859,164.5 0,-7"
inkscape:connector-curvature="0"
id="path288"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 859,157.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path290"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 866,150.5 40,0"
inkscape:connector-curvature="0"
id="path292"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 906,150.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path294"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 913,157.5 0,7"
inkscape:connector-curvature="0"
id="path296"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 913,164.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path298"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,866.5,158.5)"
id="text300"><tspan
x="0 3.7499084 10.489744 13.479671 16.469599 22.469452 25.459379 28.449306 34.449158"
y="0"
id="tspan302"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(S, 0, 0)</tspan></text>
<path
d="m 872.918,200.973 c 2.281,-6.969 5.004,-15.285 7.371,-22.528"
inkscape:connector-curvature="0"
id="path304"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 882.664,179.066 -0.152,-7.414 -4.504,5.891 4.656,1.523 z"
inkscape:connector-curvature="0"
id="path306"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 878.203,106 c 0,-8.199 -22.476,-14.848 -50.203,-14.848 -27.727,0 -50.203,6.649 -50.203,14.848 0,8.199 22.476,14.848 50.203,14.848 27.727,0 50.203,-6.649 50.203,-14.848 z"
inkscape:connector-curvature="0"
id="path308"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,800.5,103.5)"
id="text310"><tspan
x="0 3.7499084 10.489744 13.479671 16.469599 19.459526 27.709324"
y="0"
id="tspan312"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(S ::= </tspan></text>
<text
transform="matrix(1,0,0,-1,831.19925,103.5)"
id="text314"><tspan
x="0"
y="0"
id="tspan316"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">ε</tspan></text>
<text
transform="matrix(1,0,0,-1,836.43912,103.5)"
id="text318"><tspan
x="0 5.9898539 8.9897804 14.979634"
y="0"
id="tspan320"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">•,0)</tspan></text>
<path
d="m 874.816,150.395 c -7.414,-7.032 -17.367,-16.469 -26.187,-24.833"
inkscape:connector-curvature="0"
id="path322"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 850.078,123.562 -6.762,-3.039 3.391,6.594 3.371,-3.555 z"
inkscape:connector-curvature="0"
id="path324"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 1037.188,106 c 0,-8.199 -26.051,-14.848 -58.188,-14.848 -32.137,0 -58.188,6.649 -58.188,14.848 0,8.199 26.051,14.848 58.188,14.848 32.137,0 58.188,-6.649 58.188,-14.848 z"
inkscape:connector-curvature="0"
id="path326"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,945.5,103.5)"
id="text328"><tspan
x="0 3.7499084 10.489744 13.479671 16.469599 19.459526 27.709324 30.699251 37.439087 40.429012 47.16885 53.168701 56.15863 62.148483"
y="0"
id="tspan330"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(S ::= S S•,0)</tspan></text>
<path
d="m 891.195,150.395 c 10.446,-8.172 29.223,-19.598 47.098,-28.809"
inkscape:connector-curvature="0"
id="path332"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 939.668,123.637 5.152,-5.336 -7.351,0.957 2.199,4.379 z"
inkscape:connector-curvature="0"
id="path334"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 847,61.5 -38,0"
inkscape:connector-curvature="0"
id="path336"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 809,61.5 c -3.5,0 -7,-3.5 -7,-7"
inkscape:connector-curvature="0"
id="path338"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 802,54.5 0,-7"
inkscape:connector-curvature="0"
id="path340"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 802,47.5 c 0,-3.5 3.5,-7 7,-7"
inkscape:connector-curvature="0"
id="path342"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 809,40.5 38,0"
inkscape:connector-curvature="0"
id="path344"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 847,40.5 c 3.5,0 7,3.5 7,7"
inkscape:connector-curvature="0"
id="path346"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 854,47.5 0,7"
inkscape:connector-curvature="0"
id="path348"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 854,54.5 c 0,3.5 -3.5,7 -7,7"
inkscape:connector-curvature="0"
id="path350"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><text
transform="matrix(1,0,0,-1,809.5,48.5)"
id="text352"><tspan
x="0"
y="0"
id="tspan354"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">(</tspan></text>
<text
transform="matrix(1,0,0,-1,813.24991,48.5)"
id="text356"><tspan
x="0"
y="0"
id="tspan358"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">ε</tspan></text>
<text
transform="matrix(1,0,0,-1,818.48978,48.5)"
id="text360"><tspan
x="0 2.9899271 5.9898539 11.979708 14.979634 17.969561 23.959415"
y="0"
id="tspan362"
style="font-size:9.99975586px;font-variant:normal;font-weight:normal;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVuSans">, 0, 0)</tspan></text>
<path
d="m 828,90.973 c 0,-6.821 0,-14.942 0,-22.075"
inkscape:connector-curvature="0"
id="path364"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 830.449,68.652 -2.449,-7 -2.449,7 4.898,0 z"
inkscape:connector-curvature="0"
id="path366"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="M 979.949,121.254 C 980.715,146.762 978.676,198.461 953,231 c -9.465,11.992 -23.383,20.566 -37.227,26.613"
inkscape:connector-curvature="0"
id="path368"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="M 914.551,255.465 909,260.383 916.406,260 914.551,255.465 z"
inkscape:connector-curvature="0"
id="path370"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 968.672,120.891 c -12.512,8.875 -31.633,19.984 -48.789,28.261"
inkscape:connector-curvature="0"
id="path372"
style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><path
d="m 918.48,147.102 -5.304,5.183 7.379,-0.742 -2.075,-4.441 z"
inkscape:connector-curvature="0"
id="path374"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /></g></g></g></g></g></g></g></g></g></svg>

+ 1
- 1
docs/features.md View File

@@ -23,7 +23,7 @@
## Extra features ## Extra features


- Import rules and tokens from other Lark grammars, for code reuse and modularity. - Import rules and tokens from other Lark grammars, for code reuse and modularity.
- Support for external regex module ([see here](classes.md#using-unicode-character-classes-with-regex))
- Support for external regex module ([see here](classes.html#using-unicode-character-classes-with-regex))
- Import grammars from Nearley.js ([read more](nearley.md)) - Import grammars from Nearley.js ([read more](nearley.md))
- CYK parser - CYK parser
- Visualize your parse trees as dot or png files ([see_example](https://github.com/lark-parser/lark/blob/master/examples/fruitflies.py)) - Visualize your parse trees as dot or png files ([see_example](https://github.com/lark-parser/lark/blob/master/examples/fruitflies.py))


+ 7
- 3
docs/grammar.md View File

@@ -112,9 +112,13 @@ You can use flags on regexps and strings. For example:
```perl ```perl
SELECT: "select"i //# Will ignore case, and match SELECT or Select, etc. SELECT: "select"i //# Will ignore case, and match SELECT or Select, etc.
MULTILINE_TEXT: /.+/s MULTILINE_TEXT: /.+/s
SIGNED_INTEGER: /
[+-]? # the sign
(0|[1-9][0-9]*) # the digits
/x
``` ```


Supported flags are one of: `imslu`. See Python's regex documentation for more details on each one.
Supported flags are one of: `imslux`. See Python's regex documentation for more details on each one.


Regexps/strings of different flags can only be concatenated in Python 3.6+ Regexps/strings of different flags can only be concatenated in Python 3.6+


@@ -251,7 +255,7 @@ COMMENT: "#" /[^\n]/*
``` ```
### %import ### %import


Allows to import terminals and rules from lark grammars.
Allows one to import terminals and rules from lark grammars.


When importing rules, all their dependencies will be imported into a namespace, to avoid collisions. It's not possible to override their dependencies (e.g. like you would when inheriting a class). When importing rules, all their dependencies will be imported into a namespace, to avoid collisions. It's not possible to override their dependencies (e.g. like you would when inheriting a class).


@@ -264,7 +268,7 @@ When importing rules, all their dependencies will be imported into a namespace,
%import <module> (<TERM1>, <TERM2>, <rule1>, <rule2>) %import <module> (<TERM1>, <TERM2>, <rule1>, <rule2>)
``` ```


If the module path is absolute, Lark will attempt to load it from the built-in directory (currently, only `common.lark` is available).
If the module path is absolute, Lark will attempt to load it from the built-in directory (which currently contains `common.lark`, `python.lark`, and `unicode.lark`).


If the module path is relative, such as `.path.to.file`, Lark will attempt to load it from the current working directory. Grammars must have the `.lark` extension. If the module path is relative, such as `.path.to.file`, Lark will attempt to load it from the current working directory. Grammars must have the `.lark` extension.




+ 14
- 3
docs/parsers.md View File

@@ -25,9 +25,18 @@ Lark provides the following options to combat ambiguity:


3) As an advanced feature, users may use specialized visitors to iterate the SPPF themselves. 3) As an advanced feature, users may use specialized visitors to iterate the SPPF themselves.


**dynamic_complete**
**lexer="dynamic_complete"**

Earley's "dynamic" lexer uses regular expressions in order to tokenize the text. It tries every possible combination of terminals, but it matches each terminal exactly once, returning the longest possible match.

That means, for example, that when `lexer="dynamic"` (which is the default), the terminal `/a+/`, when given the text `"aa"`, will return one result, `aa`, even though `a` would also be correct.

This behavior was chosen because it is much faster, and it is usually what you would expect.

Setting `lexer="dynamic_complete"` instructs the lexer to consider every possible regexp match. This ensures that the parser will consider and resolve every ambiguity, even inside the terminals themselves. This lexer provides the same capabilities as scannerless Earley, but with different performance tradeoffs.

Warning: This lexer can be much slower, especially for open-ended terminals such as `/.*/`


**TODO: Add documentation on dynamic_complete**


## LALR(1) ## LALR(1)


@@ -37,7 +46,9 @@ Lark comes with an efficient implementation that outperforms every other parsing


Lark extends the traditional YACC-based architecture with a *contextual lexer*, which automatically provides feedback from the parser to the lexer, making the LALR(1) algorithm stronger than ever. Lark extends the traditional YACC-based architecture with a *contextual lexer*, which automatically provides feedback from the parser to the lexer, making the LALR(1) algorithm stronger than ever.


The contextual lexer communicates with the parser, and uses the parser's lookahead prediction to narrow its choice of tokens. So at each point, the lexer only matches the subgroup of terminals that are legal at that parser state, instead of all of the terminals. It’s surprisingly effective at resolving common terminal collisions, and allows one to parse languages that LALR(1) was previously incapable of parsing.
The contextual lexer communicates with the parser, and uses the parser's lookahead prediction to narrow its choice of terminals. So at each point, the lexer only matches the subgroup of terminals that are legal at that parser state, instead of all of the terminals. It’s surprisingly effective at resolving common terminal collisions, and allows one to parse languages that LALR(1) was previously incapable of parsing.

(If you're familiar with YACC, you can think of it as automatic lexer-states)


This is an improvement to LALR(1) that is unique to Lark. This is an improvement to LALR(1) that is unique to Lark.




+ 1
- 1
docs/philosophy.md View File

@@ -16,7 +16,7 @@ Lark's mission is to make the process of writing them as simple and abstract as


5. Performance is still very important 5. Performance is still very important


6. Follow the Zen Of Python, whenever possible and applicable
6. Follow the Zen of Python, whenever possible and applicable




In accordance with these principles, I arrived at the following design choices: In accordance with these principles, I arrived at the following design choices:


+ 11
- 4
docs/visitors.rst View File

@@ -30,15 +30,17 @@ Example:
:: ::


class IncreaseAllNumbers(Visitor): class IncreaseAllNumbers(Visitor):
def number(self, tree):
assert tree.data == "number"
tree.children[0] += 1
def number(self, tree):
assert tree.data == "number"
tree.children[0] += 1


IncreaseAllNumbers().visit(parse_tree) IncreaseAllNumbers().visit(parse_tree)


.. autoclass:: lark.visitors.Visitor .. autoclass:: lark.visitors.Visitor
:members: visit, visit_topdown, __default__


.. autoclass:: lark.visitors.Visitor_Recursive .. autoclass:: lark.visitors.Visitor_Recursive
:members: visit, visit_topdown, __default__


Interpreter Interpreter
----------- -----------
@@ -63,7 +65,7 @@ Transformer
----------- -----------


.. autoclass:: lark.visitors.Transformer .. autoclass:: lark.visitors.Transformer
:members: __default__, __default_token__
:members: transform, __default__, __default_token__, __mul__


Example: Example:
:: ::
@@ -90,6 +92,11 @@ Example:


T(visit_tokens=True).transform(tree) T(visit_tokens=True).transform(tree)


.. autoclass:: lark.visitors.Transformer_NonRecursive

.. autoclass:: lark.visitors.Transformer_InPlace

.. autoclass:: lark.visitors.Transformer_InPlaceRecursive


v_args v_args
------ ------


+ 79
- 0
examples/advanced/error_reporting_earley.py View File

@@ -0,0 +1,79 @@
"""
Example-Driven Error Reporting
==============================

A demonstration of example-driven error reporting with the Earley parser
(See also: error_reporting_lalr.py)
"""
from lark import Lark, UnexpectedInput

from _json_parser import json_grammar # Using the grammar from the json_parser example

json_parser = Lark(json_grammar)

class JsonSyntaxError(SyntaxError):
def __str__(self):
context, line, column = self.args
return '%s at line %s, column %s.\n\n%s' % (self.label, line, column, context)

class JsonMissingValue(JsonSyntaxError):
label = 'Missing Value'

class JsonMissingOpening(JsonSyntaxError):
label = 'Missing Opening'

class JsonMissingClosing(JsonSyntaxError):
label = 'Missing Closing'

class JsonMissingComma(JsonSyntaxError):
label = 'Missing Comma'

class JsonTrailingComma(JsonSyntaxError):
label = 'Trailing Comma'


def parse(json_text):
try:
j = json_parser.parse(json_text)
except UnexpectedInput as u:
exc_class = u.match_examples(json_parser.parse, {
JsonMissingOpening: ['{"foo": ]}',
'{"foor": }}',
'{"foo": }'],
JsonMissingClosing: ['{"foo": [}',
'{',
'{"a": 1',
'[1'],
JsonMissingComma: ['[1 2]',
'[false 1]',
'["b" 1]',
'{"a":true 1:4}',
'{"a":1 1:4}',
'{"a":"b" 1:4}'],
JsonTrailingComma: ['[,]',
'[1,]',
'[1,2,]',
'{"foo":1,}',
'{"foo":false,"bar":true,}']
}, use_accepts=True)
if not exc_class:
raise
raise exc_class(u.get_context(json_text), u.line, u.column)


def test():
try:
parse('{"example1": "value"')
except JsonMissingClosing as e:
print(e)

try:
parse('{"example2": ] ')
except JsonMissingOpening as e:
print(e)


if __name__ == '__main__':
test()



+ 1
- 1
examples/advanced/error_reporting_lalr.py View File

@@ -3,7 +3,7 @@ Example-Driven Error Reporting
============================== ==============================


A demonstration of example-driven error reporting with the LALR parser A demonstration of example-driven error reporting with the LALR parser
(See also: error_reporting_earley.py)
""" """
from lark import Lark, UnexpectedInput from lark import Lark, UnexpectedInput




+ 6
- 14
examples/advanced/python3.lark View File

@@ -23,7 +23,7 @@ decorated: decorators (classdef | funcdef | async_funcdef)
async_funcdef: "async" funcdef async_funcdef: "async" funcdef
funcdef: "def" NAME "(" parameters? ")" ["->" test] ":" suite funcdef: "def" NAME "(" parameters? ")" ["->" test] ":" suite


parameters: paramvalue ("," paramvalue)* ["," [ starparams | kwparams]]
parameters: paramvalue ("," paramvalue)* ["," "/"] ["," [starparams | kwparams]]
| starparams | starparams
| kwparams | kwparams
starparams: "*" typedparam? ("," paramvalue)* ["," kwparams] starparams: "*" typedparam? ("," paramvalue)* ["," kwparams]
@@ -163,22 +163,14 @@ yield_arg: "from" test | testlist


number: DEC_NUMBER | HEX_NUMBER | BIN_NUMBER | OCT_NUMBER | FLOAT_NUMBER | IMAG_NUMBER number: DEC_NUMBER | HEX_NUMBER | BIN_NUMBER | OCT_NUMBER | FLOAT_NUMBER | IMAG_NUMBER
string: STRING | LONG_STRING string: STRING | LONG_STRING
// Tokens

NAME: /[a-zA-Z_]\w*/
COMMENT: /#[^\n]*/
_NEWLINE: ( /\r?\n[\t ]*/ | COMMENT )+


// Import terminals from standard library (grammars/python.lark)
%import python (NAME, COMMENT, STRING, LONG_STRING)
%import python (DEC_NUMBER, HEX_NUMBER, OCT_NUMBER, BIN_NUMBER, FLOAT_NUMBER, IMAG_NUMBER)


STRING : /[ubf]?r?("(?!"").*?(?<!\\)(\\\\)*?"|'(?!'').*?(?<!\\)(\\\\)*?')/i
LONG_STRING: /[ubf]?r?(""".*?(?<!\\)(\\\\)*?"""|'''.*?(?<!\\)(\\\\)*?''')/is
// Other terminals


DEC_NUMBER: /0|[1-9]\d*/i
HEX_NUMBER.2: /0x[\da-f]*/i
OCT_NUMBER.2: /0o[0-7]*/i
BIN_NUMBER.2 : /0b[0-1]*/i
FLOAT_NUMBER.2: /((\d+\.\d*|\.\d+)(e[-+]?\d+)?|\d+(e[-+]?\d+))/i
IMAG_NUMBER.2: /\d+j/i | FLOAT_NUMBER "j"i
_NEWLINE: ( /\r?\n[\t ]*/ | COMMENT )+


%ignore /[\t \f]+/ // WS %ignore /[\t \f]+/ // WS
%ignore /\\[\t \f]*\r?\n/ // LINE_CONT %ignore /\\[\t \f]*\r?\n/ // LINE_CONT


+ 7
- 3
examples/lark_grammar.py View File

@@ -7,13 +7,13 @@ A reference implementation of the Lark grammar (using LALR(1))
import lark import lark
from pathlib import Path from pathlib import Path


parser = lark.Lark.open('lark.lark', rel_to=__file__, parser="lalr")

examples_path = Path(__file__).parent examples_path = Path(__file__).parent
lark_path = Path(lark.__file__).parent lark_path = Path(lark.__file__).parent


parser = lark.Lark.open(lark_path / 'grammars/lark.lark', rel_to=__file__, parser="lalr")


grammar_files = [ grammar_files = [
examples_path / 'lark.lark',
examples_path / 'advanced/python2.lark', examples_path / 'advanced/python2.lark',
examples_path / 'advanced/python3.lark', examples_path / 'advanced/python3.lark',
examples_path / 'relative-imports/multiples.lark', examples_path / 'relative-imports/multiples.lark',
@@ -21,7 +21,11 @@ grammar_files = [
examples_path / 'relative-imports/multiple3.lark', examples_path / 'relative-imports/multiple3.lark',
examples_path / 'tests/no_newline_at_end.lark', examples_path / 'tests/no_newline_at_end.lark',
examples_path / 'tests/negative_priority.lark', examples_path / 'tests/negative_priority.lark',
examples_path / 'standalone/json.lark',
lark_path / 'grammars/common.lark', lark_path / 'grammars/common.lark',
lark_path / 'grammars/lark.lark',
lark_path / 'grammars/unicode.lark',
lark_path / 'grammars/python.lark',
] ]


def test(): def test():


+ 4
- 0
lark-stubs/exceptions.pyi View File

@@ -9,6 +9,10 @@ class LarkError(Exception):
pass pass




class ConfigurationError(LarkError, ValueError):
pass


class GrammarError(LarkError): class GrammarError(LarkError):
pass pass




+ 24
- 4
lark-stubs/lark.pyi View File

@@ -2,7 +2,7 @@


from typing import ( from typing import (
TypeVar, Type, List, Dict, IO, Iterator, Callable, Union, Optional, TypeVar, Type, List, Dict, IO, Iterator, Callable, Union, Optional,
Literal, Protocol, Iterable,
Literal, Protocol, Tuple, Iterable,
) )
from .visitors import Transformer from .visitors import Transformer
from .lexer import Token, Lexer, TerminalDef from .lexer import Token, Lexer, TerminalDef
@@ -34,11 +34,25 @@ class LarkOptions:
cache: Union[bool, str] cache: Union[bool, str]
g_regex_flags: int g_regex_flags: int
use_bytes: bool use_bytes: bool
import_paths: List[Union[str, Callable[[Union[None, str, PackageResource], str], Tuple[str, str]]]]
source_path: Optional[str]


class PackageResource(object):
pkg_name: str
path: str
def __init__(self, pkg_name: str, path: str): ...

class FromPackageLoader:
def __init__(self, pkg_name: str, search_paths: Tuple[str, ...] = ...): ...
def __call__(self, base_path: Union[None, str, PackageResource], grammar_path: str) -> Tuple[PackageResource, str]: ...




class Lark: class Lark:
source: str
grammar_source: str
source_path: str
source_grammar: str
options: LarkOptions options: LarkOptions
lexer: Lexer lexer: Lexer
terminals: List[TerminalDef] terminals: List[TerminalDef]
@@ -49,7 +63,7 @@ class Lark:
*, *,
start: Union[None, str, List[str]] = "start", start: Union[None, str, List[str]] = "start",
parser: Literal["earley", "lalr", "cyk"] = "auto", parser: Literal["earley", "lalr", "cyk"] = "auto",
lexer: Union[Literal["auto", "standard", "contextual", "dynamic", "dynamic_complete"], Lexer] = "auto",
lexer: Union[Literal["auto", "standard", "contextual", "dynamic", "dynamic_complete"], Type[Lexer]] = "auto",
transformer: Optional[Transformer] = None, transformer: Optional[Transformer] = None,
postlex: Optional[PostLex] = None, postlex: Optional[PostLex] = None,
ambiguity: Literal["explicit", "resolve"] = "resolve", ambiguity: Literal["explicit", "resolve"] = "resolve",
@@ -62,6 +76,8 @@ class Lark:
cache: Union[bool, str] = False, cache: Union[bool, str] = False,
g_regex_flags: int = ..., g_regex_flags: int = ...,
use_bytes: bool = False, use_bytes: bool = False,
import_paths: List[Union[str, Callable[[Union[None, str, PackageResource], str], Tuple[str, str]]]] = ...,
source_path: Optional[str]=None,
): ):
... ...


@@ -71,6 +87,10 @@ class Lark:
@classmethod @classmethod
def open(cls: Type[_T], grammar_filename: str, rel_to: Optional[str] = None, **options) -> _T: def open(cls: Type[_T], grammar_filename: str, rel_to: Optional[str] = None, **options) -> _T:
... ...
@classmethod
def open_from_package(cls: Type[_T], package: str, grammar_path: str, search_paths: Tuple[str, ...] = ..., **options) -> _T:
...


def lex(self, text: str) -> Iterator[Token]: def lex(self, text: str) -> Iterator[Token]:
... ...


+ 21
- 8
lark-stubs/lexer.pyi View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from types import ModuleType from types import ModuleType
from typing import ( from typing import (
TypeVar, Type, Tuple, List, Dict, Iterator, Collection, Callable, Optional,
TypeVar, Type, Tuple, List, Dict, Iterator, Collection, Callable, Optional, FrozenSet, Any,
Pattern as REPattern, Pattern as REPattern,
) )
from abc import abstractmethod, ABC from abc import abstractmethod, ABC
@@ -85,6 +85,9 @@ class Token(str):
end_column: int end_column: int
end_pos: int end_pos: int


def __init__(self, type_: str, value: Any, pos_in_stream: int = None, line: int = None, column: int = None, end_line: int = None, end_column: int = None, end_pos: int = None):
...

def update(self, type_: Optional[str] = None, value: Optional[str] = None) -> Token: def update(self, type_: Optional[str] = None, value: Optional[str] = None) -> Token:
... ...


@@ -100,10 +103,22 @@ class Lexer(ABC):
lex: Callable[..., Iterator[Token]] lex: Callable[..., Iterator[Token]]




class LexerConf:
tokens: Collection[TerminalDef]
re_module: ModuleType
ignore: Collection[str] = ()
postlex: Any =None
callbacks: Optional[Dict[str, _Callback]] = None
g_regex_flags: int = 0
skip_validation: bool = False
use_bytes: bool = False



class TraditionalLexer(Lexer): class TraditionalLexer(Lexer):
terminals: Collection[TerminalDef] terminals: Collection[TerminalDef]
ignore_types: List[str]
newline_types: List[str]
ignore_types: FrozenSet[str]
newline_types: FrozenSet[str]
user_callbacks: Dict[str, _Callback] user_callbacks: Dict[str, _Callback]
callback: Dict[str, _Callback] callback: Dict[str, _Callback]
mres: List[Tuple[REPattern, Dict[int, str]]] mres: List[Tuple[REPattern, Dict[int, str]]]
@@ -111,11 +126,7 @@ class TraditionalLexer(Lexer):


def __init__( def __init__(
self, self,
terminals: Collection[TerminalDef],
re_: ModuleType,
ignore: Collection[str] = ...,
user_callbacks: Dict[str, _Callback] = ...,
g_regex_flags: int = ...
conf: LexerConf
): ):
... ...


@@ -128,6 +139,8 @@ class TraditionalLexer(Lexer):
def lex(self, stream: str) -> Iterator[Token]: def lex(self, stream: str) -> Iterator[Token]:
... ...


def next_token(self, lex_state: Any, parser_state: Any = None) -> Token:
...


class ContextualLexer(Lexer): class ContextualLexer(Lexer):
lexers: Dict[str, TraditionalLexer] lexers: Dict[str, TraditionalLexer]


+ 2
- 2
lark/__init__.py View File

@@ -3,8 +3,8 @@ from .tree import Tree
from .visitors import Transformer, Visitor, v_args, Discard, Transformer_NonRecursive from .visitors import Transformer, Visitor, v_args, Discard, Transformer_NonRecursive
from .visitors import InlineTransformer, inline_args # XXX Deprecated from .visitors import InlineTransformer, inline_args # XXX Deprecated
from .exceptions import (ParseError, LexError, GrammarError, UnexpectedToken, from .exceptions import (ParseError, LexError, GrammarError, UnexpectedToken,
UnexpectedInput, UnexpectedCharacters, LarkError)
UnexpectedInput, UnexpectedCharacters, UnexpectedEOF, LarkError)
from .lexer import Token from .lexer import Token
from .lark import Lark from .lark import Lark


__version__ = "0.10.1"
__version__ = "0.11.1"

+ 9
- 3
lark/common.py View File

@@ -5,8 +5,9 @@ from .lexer import TerminalDef


###{standalone ###{standalone



class LexerConf(Serialize): class LexerConf(Serialize):
__serialize_fields__ = 'terminals', 'ignore', 'g_regex_flags', 'use_bytes'
__serialize_fields__ = 'terminals', 'ignore', 'g_regex_flags', 'use_bytes', 'lexer_type'
__serialize_namespace__ = TerminalDef, __serialize_namespace__ = TerminalDef,


def __init__(self, terminals, re_module, ignore=(), postlex=None, callbacks=None, g_regex_flags=0, skip_validation=False, use_bytes=False): def __init__(self, terminals, re_module, ignore=(), postlex=None, callbacks=None, g_regex_flags=0, skip_validation=False, use_bytes=False):
@@ -18,19 +19,24 @@ class LexerConf(Serialize):
self.re_module = re_module self.re_module = re_module
self.skip_validation = skip_validation self.skip_validation = skip_validation
self.use_bytes = use_bytes self.use_bytes = use_bytes
self.lexer_type = None
@property @property
def tokens(self): def tokens(self):
warn("LexerConf.tokens is deprecated. Use LexerConf.terminals instead", DeprecationWarning) warn("LexerConf.tokens is deprecated. Use LexerConf.terminals instead", DeprecationWarning)
return self.terminals return self.terminals


###}


class ParserConf:

class ParserConf(Serialize):
__serialize_fields__ = 'rules', 'start', 'parser_type'

def __init__(self, rules, callbacks, start): def __init__(self, rules, callbacks, start):
assert isinstance(start, list) assert isinstance(start, list)
self.rules = rules self.rules = rules
self.callbacks = callbacks self.callbacks = callbacks
self.start = start self.start = start


self.parser_type = None


###}

+ 37
- 10
lark/exceptions.py View File

@@ -6,22 +6,27 @@ from .utils import STRING_TYPE, logger
class LarkError(Exception): class LarkError(Exception):
pass pass



class ConfigurationError(LarkError, ValueError):
pass


def assert_config(value, options, msg='Got %r, expected one of %s'):
if value not in options:
raise ConfigurationError(msg % (value, options))


class GrammarError(LarkError): class GrammarError(LarkError):
pass pass



class ParseError(LarkError): class ParseError(LarkError):
pass pass



class LexError(LarkError): class LexError(LarkError):
pass pass


class UnexpectedEOF(ParseError):
def __init__(self, expected):
self.expected = expected

message = ("Unexpected end-of-input. Expected one of: \n\t* %s\n" % '\n\t* '.join(x.name for x in self.expected))
super(UnexpectedEOF, self).__init__(message)



class UnexpectedInput(LarkError): class UnexpectedInput(LarkError):
"""UnexpectedInput Error. """UnexpectedInput Error.
@@ -44,6 +49,7 @@ class UnexpectedInput(LarkError):
The parser doesn't hold a copy of the text it has to parse, The parser doesn't hold a copy of the text it has to parse,
so you have to provide it again so you have to provide it again
""" """
assert self.pos_in_stream is not None, self
pos = self.pos_in_stream pos = self.pos_in_stream
start = max(pos - span, 0) start = max(pos - span, 0)
end = pos + span end = pos + span
@@ -88,7 +94,7 @@ class UnexpectedInput(LarkError):
parse_fn(malformed) parse_fn(malformed)
except UnexpectedInput as ut: except UnexpectedInput as ut:
if ut.state == self.state: if ut.state == self.state:
if use_accepts and ut.accepts != self.accepts:
if use_accepts and hasattr(self, 'accepts') and ut.accepts != self.accepts:
logger.debug("Different accepts with same state[%d]: %s != %s at example [%s][%s]" % logger.debug("Different accepts with same state[%d]: %s != %s at example [%s][%s]" %
(self.state, self.accepts, ut.accepts, i, j)) (self.state, self.accepts, ut.accepts, i, j))
continue continue
@@ -105,7 +111,7 @@ class UnexpectedInput(LarkError):


except AttributeError: except AttributeError:
pass pass
if not candidate[0]:
if candidate[0] is None:
logger.debug("Same State match at example [%s][%s]" % (i, j)) logger.debug("Same State match at example [%s][%s]" % (i, j))
candidate = label, False candidate = label, False


@@ -127,10 +133,24 @@ class UnexpectedInput(LarkError):
ts = names ts = names
return "Expected one of: \n\t* %s\n" % '\n\t* '.join(ts) return "Expected one of: \n\t* %s\n" % '\n\t* '.join(ts)


class UnexpectedEOF(ParseError, UnexpectedInput):
def __init__(self, expected, state=None):
self.expected = expected
self.state = state
from .lexer import Token
self.token = Token("<EOF>", "") #, line=-1, column=-1, pos_in_stream=-1)
self.pos_in_stream = -1
self.line = -1
self.column = -1

message = ("Unexpected end-of-input. Expected one of: \n\t* %s\n" % '\n\t* '.join(x.name for x in self.expected))
super(UnexpectedEOF, self).__init__(message)





class UnexpectedCharacters(LexError, UnexpectedInput): class UnexpectedCharacters(LexError, UnexpectedInput):
def __init__(self, seq, lex_pos, line, column, allowed=None, considered_tokens=None, state=None, token_history=None, _all_terminals=None): def __init__(self, seq, lex_pos, line, column, allowed=None, considered_tokens=None, state=None, token_history=None, _all_terminals=None):
# TODO considered_tokens and allowed can be figured out using state
self.line = line self.line = line
self.column = column self.column = column
self.pos_in_stream = lex_pos self.pos_in_stream = lex_pos
@@ -168,7 +188,8 @@ class UnexpectedToken(ParseError, UnexpectedInput):


see: :ref:`ParserPuppet`. see: :ref:`ParserPuppet`.
""" """
def __init__(self, token, expected, considered_rules=None, state=None, puppet=None, all_terminals=None):
def __init__(self, token, expected, considered_rules=None, state=None, puppet=None, all_terminals=None, token_history=None):
# TODO considered_rules and expected can be figured out using state
self.line = getattr(token, 'line', '?') self.line = getattr(token, 'line', '?')
self.column = getattr(token, 'column', '?') self.column = getattr(token, 'column', '?')
self.pos_in_stream = getattr(token, 'pos_in_stream', None) self.pos_in_stream = getattr(token, 'pos_in_stream', None)
@@ -179,6 +200,7 @@ class UnexpectedToken(ParseError, UnexpectedInput):
self.considered_rules = considered_rules self.considered_rules = considered_rules
self.puppet = puppet self.puppet = puppet
self._all_terminals = all_terminals self._all_terminals = all_terminals
self.token_history = token_history




super(UnexpectedToken, self).__init__() super(UnexpectedToken, self).__init__()
@@ -191,6 +213,9 @@ class UnexpectedToken(ParseError, UnexpectedInput):
# Be aware: Broken __str__ for Exceptions are terrible to debug. Make sure there is as little room as possible for errors # Be aware: Broken __str__ for Exceptions are terrible to debug. Make sure there is as little room as possible for errors
message = ("Unexpected token %r at line %s, column %s.\n%s" message = ("Unexpected token %r at line %s, column %s.\n%s"
% (self.token, self.line, self.column, self._format_terminals(self.accepts or self.expected))) % (self.token, self.line, self.column, self._format_terminals(self.accepts or self.expected)))
if self.token_history:
message += "Previous tokens: %r\n" % self.token_history

return message return message




@@ -207,4 +232,6 @@ class VisitError(LarkError):


message = 'Error trying to process rule "%s":\n\n%s' % (rule, orig_exc) message = 'Error trying to process rule "%s":\n\n%s' % (rule, orig_exc)
super(VisitError, self).__init__(message) super(VisitError, self).__init__(message)


###} ###}

+ 0
- 3
lark/grammar.py View File

@@ -40,14 +40,12 @@ class Terminal(Symbol):
return '%s(%r, %r)' % (type(self).__name__, self.name, self.filter_out) return '%s(%r, %r)' % (type(self).__name__, self.name, self.filter_out)





class NonTerminal(Symbol): class NonTerminal(Symbol):
__serialize_fields__ = 'name', __serialize_fields__ = 'name',


is_term = False is_term = False





class RuleOptions(Serialize): class RuleOptions(Serialize):
__serialize_fields__ = 'keep_all_tokens', 'expand1', 'priority', 'template_source', 'empty_indices' __serialize_fields__ = 'keep_all_tokens', 'expand1', 'priority', 'template_source', 'empty_indices'


@@ -104,5 +102,4 @@ class Rule(Serialize):
return self.origin == other.origin and self.expansion == other.expansion return self.origin == other.origin and self.expansion == other.expansion





###} ###}

+ 10
- 1
lark/grammars/common.lark View File

@@ -1,3 +1,6 @@
// Basic terminals for common use


// //
// Numbers // Numbers
// //
@@ -21,7 +24,7 @@ SIGNED_NUMBER: ["+"|"-"] NUMBER
// Strings // Strings
// //
_STRING_INNER: /.*?/ _STRING_INNER: /.*?/
_STRING_ESC_INNER: _STRING_INNER /(?<!\\)(\\\\)*?/
_STRING_ESC_INNER: _STRING_INNER /(?<!\\)(\\\\)*?/


ESCAPED_STRING : "\"" _STRING_ESC_INNER "\"" ESCAPED_STRING : "\"" _STRING_ESC_INNER "\""


@@ -48,3 +51,9 @@ CR : /\r/
LF : /\n/ LF : /\n/
NEWLINE: (CR? LF)+ NEWLINE: (CR? LF)+



// Comments
SH_COMMENT: /#[^\n]*/
CPP_COMMENT: /\/\/[^\n]*/
C_COMMENT: "/*" /.*?/s "*/"
SQL_COMMENT: /--[^\n]*/

examples/lark.lark → lark/grammars/lark.lark View File

@@ -45,7 +45,7 @@ OP: /[+*]|[?](?![a-z])/
RULE: /!?[_?]?[a-z][_a-z0-9]*/ RULE: /!?[_?]?[a-z][_a-z0-9]*/
TOKEN: /_?[A-Z][_A-Z0-9]*/ TOKEN: /_?[A-Z][_A-Z0-9]*/
STRING: _STRING "i"? STRING: _STRING "i"?
REGEXP: /\/(?!\/)(\\\/|\\\\|[^\/\n])*?\/[imslux]*/
REGEXP: /\/(?!\/)(\\\/|\\\\|[^\/])*?\/[imslux]*/
_NL: /(\r?\n)+\s*/ _NL: /(\r?\n)+\s*/


%import common.ESCAPED_STRING -> _STRING %import common.ESCAPED_STRING -> _STRING

+ 19
- 0
lark/grammars/python.lark View File

@@ -0,0 +1,19 @@
// Python terminals

NAME: /[a-zA-Z_]\w*/
COMMENT: /#[^\n]*/

STRING : /[ubf]?r?("(?!"").*?(?<!\\)(\\\\)*?"|'(?!'').*?(?<!\\)(\\\\)*?')/i
LONG_STRING: /[ubf]?r?(""".*?(?<!\\)(\\\\)*?"""|'''.*?(?<!\\)(\\\\)*?''')/is

DEC_NUMBER: /0|[1-9][\d_]*/i
HEX_NUMBER.2: /0x[\da-f]*/i
OCT_NUMBER.2: /0o[0-7]*/i
BIN_NUMBER.2 : /0b[0-1]*/i
FLOAT_NUMBER.2: /((\d+\.[\d_]*|\.[\d_]+)(e[-+]?\d+)?|\d+(e[-+]?\d+))/i
IMAG_NUMBER.2: /\d+j/i | FLOAT_NUMBER "j"i


// Comma-separated list (with an optional trailing comma)
cs_list{item}: item ("," item)* ","?
_cs_list{item}: item ("," item)* ","?

+ 7
- 0
lark/grammars/unicode.lark View File

@@ -0,0 +1,7 @@
// TODO: LETTER, WORD, etc.

//
// Whitespace
//
WS_INLINE: /[ \t\xa0]/+
WS: /[ \t\xa0\f\r\n]/+

+ 83
- 33
lark/lark.py View File

@@ -1,12 +1,13 @@
from __future__ import absolute_import from __future__ import absolute_import
from lark.exceptions import UnexpectedCharacters, UnexpectedInput, UnexpectedToken
from lark.exceptions import UnexpectedCharacters, UnexpectedInput, UnexpectedToken, ConfigurationError, assert_config


import sys, os, pickle, hashlib import sys, os, pickle, hashlib
from io import open from io import open

import tempfile
from warnings import warn


from .utils import STRING_TYPE, Serialize, SerializeMemoizer, FS, isascii, logger from .utils import STRING_TYPE, Serialize, SerializeMemoizer, FS, isascii, logger
from .load_grammar import load_grammar
from .load_grammar import load_grammar, FromPackageLoader
from .tree import Tree from .tree import Tree
from .common import LexerConf, ParserConf from .common import LexerConf, ParserConf


@@ -23,6 +24,7 @@ except ImportError:


###{standalone ###{standalone



class LarkOptions(Serialize): class LarkOptions(Serialize):
"""Specifies the options for Lark """Specifies the options for Lark


@@ -33,9 +35,10 @@ class LarkOptions(Serialize):
start start
The start symbol. Either a string, or a list of strings for multiple possible starts (Default: "start") The start symbol. Either a string, or a list of strings for multiple possible starts (Default: "start")
debug debug
Display debug information, such as warnings (default: False)
Display debug information and extra warnings. Use only when debugging (default: False)
When used with Earley, it generates a forest graph as "sppf.png", if 'dot' is installed.
transformer transformer
Applies the transformer to every parse tree (equivlent to applying it after the parse, but faster)
Applies the transformer to every parse tree (equivalent to applying it after the parse, but faster)
propagate_positions propagate_positions
Propagates (line, column, end_line, end_column) attributes into all tree branches. Propagates (line, column, end_line, end_column) attributes into all tree branches.
maybe_placeholders maybe_placeholders
@@ -91,6 +94,10 @@ class LarkOptions(Serialize):
Accept an input of type ``bytes`` instead of ``str`` (Python 3 only). Accept an input of type ``bytes`` instead of ``str`` (Python 3 only).
edit_terminals edit_terminals
A callback for editing the terminals before parse. A callback for editing the terminals before parse.
import_paths
A List of either paths or loader functions to specify from where grammars are imported
source_path
Override the source of from where the grammar was loaded. Useful for relative imports and unconventional grammar loading


**=== End Options ===** **=== End Options ===**
""" """
@@ -125,6 +132,8 @@ class LarkOptions(Serialize):
'edit_terminals': None, 'edit_terminals': None,
'g_regex_flags': 0, 'g_regex_flags': 0,
'use_bytes': False, 'use_bytes': False,
'import_paths': [],
'source_path': None,
} }


def __init__(self, options_dict): def __init__(self, options_dict):
@@ -146,14 +155,15 @@ class LarkOptions(Serialize):


self.__dict__['options'] = options self.__dict__['options'] = options


assert self.parser in ('earley', 'lalr', 'cyk', None)

assert_config(self.parser, ('earley', 'lalr', 'cyk', None))


if self.parser == 'earley' and self.transformer: if self.parser == 'earley' and self.transformer:
raise ValueError('Cannot specify an embedded transformer when using the Earley algorithm.'
raise ConfigurationError('Cannot specify an embedded transformer when using the Earley algorithm.'
'Please use your transformer on the resulting parse tree, or use a different algorithm (i.e. LALR)') 'Please use your transformer on the resulting parse tree, or use a different algorithm (i.e. LALR)')


if o: if o:
raise ValueError("Unknown options: %s" % o.keys())
raise ConfigurationError("Unknown options: %s" % o.keys())


def __getattr__(self, name): def __getattr__(self, name):
try: try:
@@ -162,7 +172,7 @@ class LarkOptions(Serialize):
raise AttributeError(e) raise AttributeError(e)


def __setattr__(self, name, value): def __setattr__(self, name, value):
assert name in self.options
assert_config(name, self.options.keys(), "%r isn't a valid option. Expected one of: %s")
self.options[name] = value self.options[name] = value


def serialize(self, memo): def serialize(self, memo):
@@ -208,10 +218,13 @@ class Lark(Serialize):
re_module = re re_module = re


# Some, but not all file-like objects have a 'name' attribute # Some, but not all file-like objects have a 'name' attribute
try:
self.source = grammar.name
except AttributeError:
self.source = '<string>'
if self.options.source_path is None:
try:
self.source_path = grammar.name
except AttributeError:
self.source_path = '<string>'
else:
self.source_path = self.options.source_path


# Drain file-like objects to get their contents # Drain file-like objects to get their contents
try: try:
@@ -222,29 +235,29 @@ class Lark(Serialize):
grammar = read() grammar = read()


assert isinstance(grammar, STRING_TYPE) assert isinstance(grammar, STRING_TYPE)
self.grammar_source = grammar
self.source_grammar = grammar
if self.options.use_bytes: if self.options.use_bytes:
if not isascii(grammar): if not isascii(grammar):
raise ValueError("Grammar must be ascii only, when use_bytes=True")
raise ConfigurationError("Grammar must be ascii only, when use_bytes=True")
if sys.version_info[0] == 2 and self.options.use_bytes != 'force': if sys.version_info[0] == 2 and self.options.use_bytes != 'force':
raise NotImplementedError("`use_bytes=True` may have issues on python2."
raise ConfigurationError("`use_bytes=True` may have issues on python2."
"Use `use_bytes='force'` to use it at your own risk.") "Use `use_bytes='force'` to use it at your own risk.")


cache_fn = None cache_fn = None
if self.options.cache: if self.options.cache:
if self.options.parser != 'lalr': if self.options.parser != 'lalr':
raise NotImplementedError("cache only works with parser='lalr' for now")
raise ConfigurationError("cache only works with parser='lalr' for now")
if isinstance(self.options.cache, STRING_TYPE): if isinstance(self.options.cache, STRING_TYPE):
cache_fn = self.options.cache cache_fn = self.options.cache
else: else:
if self.options.cache is not True: if self.options.cache is not True:
raise ValueError("cache argument must be bool or str")
raise ConfigurationError("cache argument must be bool or str")
unhashable = ('transformer', 'postlex', 'lexer_callbacks', 'edit_terminals') unhashable = ('transformer', 'postlex', 'lexer_callbacks', 'edit_terminals')
from . import __version__ from . import __version__
options_str = ''.join(k+str(v) for k, v in options.items() if k not in unhashable) options_str = ''.join(k+str(v) for k, v in options.items() if k not in unhashable)
s = grammar + options_str + __version__ s = grammar + options_str + __version__
md5 = hashlib.md5(s.encode()).hexdigest() md5 = hashlib.md5(s.encode()).hexdigest()
cache_fn = '.lark_cache_%s.tmp' % md5
cache_fn = tempfile.gettempdir() + '/.lark_cache_%s.tmp' % md5


if FS.exists(cache_fn): if FS.exists(cache_fn):
logger.debug('Loading grammar from cache: %s', cache_fn) logger.debug('Loading grammar from cache: %s', cache_fn)
@@ -265,27 +278,28 @@ class Lark(Serialize):
else: else:
assert False, self.options.parser assert False, self.options.parser
lexer = self.options.lexer lexer = self.options.lexer
assert lexer in ('standard', 'contextual', 'dynamic', 'dynamic_complete') or issubclass(lexer, Lexer)
if isinstance(lexer, type):
assert issubclass(lexer, Lexer) # XXX Is this really important? Maybe just ensure interface compliance
else:
assert_config(lexer, ('standard', 'contextual', 'dynamic', 'dynamic_complete'))


if self.options.ambiguity == 'auto': if self.options.ambiguity == 'auto':
if self.options.parser == 'earley': if self.options.parser == 'earley':
self.options.ambiguity = 'resolve' self.options.ambiguity = 'resolve'
else: else:
disambig_parsers = ['earley', 'cyk']
assert self.options.parser in disambig_parsers, (
'Only %s supports disambiguation right now') % ', '.join(disambig_parsers)
assert_config(self.options.parser, ('earley', 'cyk'), "%r doesn't support disambiguation. Use one of these parsers instead: %s")


if self.options.priority == 'auto': if self.options.priority == 'auto':
self.options.priority = 'normal' self.options.priority = 'normal'


if self.options.priority not in _VALID_PRIORITY_OPTIONS: if self.options.priority not in _VALID_PRIORITY_OPTIONS:
raise ValueError("invalid priority option: %r. Must be one of %r" % (self.options.priority, _VALID_PRIORITY_OPTIONS))
raise ConfigurationError("invalid priority option: %r. Must be one of %r" % (self.options.priority, _VALID_PRIORITY_OPTIONS))
assert self.options.ambiguity not in ('resolve__antiscore_sum', ), 'resolve__antiscore_sum has been replaced with the option priority="invert"' assert self.options.ambiguity not in ('resolve__antiscore_sum', ), 'resolve__antiscore_sum has been replaced with the option priority="invert"'
if self.options.ambiguity not in _VALID_AMBIGUITY_OPTIONS: if self.options.ambiguity not in _VALID_AMBIGUITY_OPTIONS:
raise ValueError("invalid ambiguity option: %r. Must be one of %r" % (self.options.ambiguity, _VALID_AMBIGUITY_OPTIONS))
raise ConfigurationError("invalid ambiguity option: %r. Must be one of %r" % (self.options.ambiguity, _VALID_AMBIGUITY_OPTIONS))


# Parse the grammar file and compose the grammars (TODO)
self.grammar = load_grammar(grammar, self.source, re_module, self.options.keep_all_tokens)
# Parse the grammar file and compose the grammars
self.grammar = load_grammar(grammar, self.source_path, self.options.import_paths, self.options.keep_all_tokens)


if self.options.postlex is not None: if self.options.postlex is not None:
terminals_to_keep = set(self.options.postlex.always_accept) terminals_to_keep = set(self.options.postlex.always_accept)
@@ -310,7 +324,7 @@ class Lark(Serialize):
# Else, if the user asked to disable priorities, strip them from the # Else, if the user asked to disable priorities, strip them from the
# rules. This allows the Earley parsers to skip an extra forest walk # rules. This allows the Earley parsers to skip an extra forest walk
# for improved performance, if you don't need them (or didn't specify any). # for improved performance, if you don't need them (or didn't specify any).
elif self.options.priority == None:
elif self.options.priority is None:
for rule in self.rules: for rule in self.rules:
if rule.options.priority is not None: if rule.options.priority is not None:
rule.options.priority = None rule.options.priority = None
@@ -350,7 +364,7 @@ class Lark(Serialize):
self.rules, self.rules,
self.options.tree_class or Tree, self.options.tree_class or Tree,
self.options.propagate_positions, self.options.propagate_positions,
self.options.parser!='lalr' and self.options.ambiguity=='explicit',
self.options.parser != 'lalr' and self.options.ambiguity == 'explicit',
self.options.maybe_placeholders self.options.maybe_placeholders
) )
self._callbacks = self._parse_tree_builder.create_callback(self.options.transformer) self._callbacks = self._parse_tree_builder.create_callback(self.options.transformer)
@@ -389,18 +403,18 @@ class Lark(Serialize):
memo = SerializeMemoizer.deserialize(memo, {'Rule': Rule, 'TerminalDef': TerminalDef}, {}) memo = SerializeMemoizer.deserialize(memo, {'Rule': Rule, 'TerminalDef': TerminalDef}, {})
options = dict(data['options']) options = dict(data['options'])
if (set(kwargs) - _LOAD_ALLOWED_OPTIONS) & set(LarkOptions._defaults): if (set(kwargs) - _LOAD_ALLOWED_OPTIONS) & set(LarkOptions._defaults):
raise ValueError("Some options are not allowed when loading a Parser: {}"
raise ConfigurationError("Some options are not allowed when loading a Parser: {}"
.format(set(kwargs) - _LOAD_ALLOWED_OPTIONS)) .format(set(kwargs) - _LOAD_ALLOWED_OPTIONS))
options.update(kwargs) options.update(kwargs)
self.options = LarkOptions.deserialize(options, memo) self.options = LarkOptions.deserialize(options, memo)
self.rules = [Rule.deserialize(r, memo) for r in data['rules']] self.rules = [Rule.deserialize(r, memo) for r in data['rules']]
self.source = '<deserialized>'
self.source_path = '<deserialized>'
self._prepare_callbacks() self._prepare_callbacks()
self.parser = self.parser_class.deserialize( self.parser = self.parser_class.deserialize(
data['parser'], data['parser'],
memo, memo,
self._callbacks, self._callbacks,
self.options, # Not all, but multiple attributes are used
self.options, # Not all, but multiple attributes are used
) )
self.terminals = self.parser.lexer_conf.terminals self.terminals = self.parser.lexer_conf.terminals
self._terminals_dict = {t.name: t for t in self.terminals} self._terminals_dict = {t.name: t for t in self.terminals}
@@ -429,8 +443,26 @@ class Lark(Serialize):
with open(grammar_filename, encoding='utf8') as f: with open(grammar_filename, encoding='utf8') as f:
return cls(f, **options) return cls(f, **options)


@classmethod
def open_from_package(cls, package, grammar_path, search_paths=("",), **options):
"""Create an instance of Lark with the grammar loaded from within the package `package`.
This allows grammar loading from zipapps.

Imports in the grammar will use the `package` and `search_paths` provided, through `FromPackageLoader`

Example:

Lark.open_from_package(__name__, "example.lark", ("grammars",), parser=...)
"""
package = FromPackageLoader(package, search_paths)
full_path, text = package(None, grammar_path)
options.setdefault('source_path', full_path)
options.setdefault('import_paths', [])
options['import_paths'].append(package)
return cls(text, **options)

def __repr__(self): def __repr__(self):
return 'Lark(open(%r), parser=%r, lexer=%r, ...)' % (self.source, self.options.parser, self.options.lexer)
return 'Lark(open(%r), parser=%r, lexer=%r, ...)' % (self.source_path, self.options.parser, self.options.lexer)




def lex(self, text): def lex(self, text):
@@ -490,5 +522,23 @@ class Lark(Serialize):
except UnexpectedCharacters as e2: except UnexpectedCharacters as e2:
e = e2 e = e2


@property
def source(self):
warn("Lark.source attribute has been renamed to Lark.source_path", DeprecationWarning)
return self.source_path

@source.setter
def source(self, value):
self.source_path = value

@property
def grammar_source(self):
warn("Lark.grammar_source attribute has been renamed to Lark.source_grammar", DeprecationWarning)
return self.source_grammar

@grammar_source.setter
def grammar_source(self, value):
self.source_grammar = value



###} ###}

+ 23
- 16
lark/lexer.py View File

@@ -1,4 +1,4 @@
## Lexer Implementation
# Lexer Implementation


import re import re


@@ -8,6 +8,7 @@ from .exceptions import UnexpectedCharacters, LexError, UnexpectedToken
###{standalone ###{standalone
from copy import copy from copy import copy



class Pattern(Serialize): class Pattern(Serialize):


def __init__(self, value, flags=(), raw=None): def __init__(self, value, flags=(), raw=None):
@@ -21,6 +22,7 @@ class Pattern(Serialize):
# Pattern Hashing assumes all subclasses have a different priority! # Pattern Hashing assumes all subclasses have a different priority!
def __hash__(self): def __hash__(self):
return hash((type(self), self.value, self.flags)) return hash((type(self), self.value, self.flags))

def __eq__(self, other): def __eq__(self, other):
return type(self) == type(other) and self.value == other.value and self.flags == other.flags return type(self) == type(other) and self.value == other.value and self.flags == other.flags


@@ -54,6 +56,7 @@ class PatternStr(Pattern):
return len(self.value) return len(self.value)
max_width = min_width max_width = min_width



class PatternRE(Pattern): class PatternRE(Pattern):
__serialize_fields__ = 'value', 'flags', '_width' __serialize_fields__ = 'value', 'flags', '_width'


@@ -71,6 +74,7 @@ class PatternRE(Pattern):
@property @property
def min_width(self): def min_width(self):
return self._get_width()[0] return self._get_width()[0]

@property @property
def max_width(self): def max_width(self):
return self._get_width()[1] return self._get_width()[1]
@@ -141,7 +145,7 @@ class Token(Str):
return cls(type_, value, borrow_t.pos_in_stream, borrow_t.line, borrow_t.column, borrow_t.end_line, borrow_t.end_column, borrow_t.end_pos) return cls(type_, value, borrow_t.pos_in_stream, borrow_t.line, borrow_t.column, borrow_t.end_line, borrow_t.end_column, borrow_t.end_pos)


def __reduce__(self): def __reduce__(self):
return (self.__class__, (self.type, self.value, self.pos_in_stream, self.line, self.column, ))
return (self.__class__, (self.type, self.value, self.pos_in_stream, self.line, self.column))


def __repr__(self): def __repr__(self):
return 'Token(%r, %r)' % (self.type, self.value) return 'Token(%r, %r)' % (self.type, self.value)
@@ -195,6 +199,7 @@ class UnlessCallback:
break break
return t return t



class CallChain: class CallChain:
def __init__(self, callback1, callback2, cond): def __init__(self, callback1, callback2, cond):
self.callback1 = callback1 self.callback1 = callback1
@@ -206,16 +211,13 @@ class CallChain:
return self.callback2(t) if self.cond(t2) else t2 return self.callback2(t) if self.cond(t2) else t2







def _create_unless(terminals, g_regex_flags, re_, use_bytes): def _create_unless(terminals, g_regex_flags, re_, use_bytes):
tokens_by_type = classify(terminals, lambda t: type(t.pattern)) tokens_by_type = classify(terminals, lambda t: type(t.pattern))
assert len(tokens_by_type) <= 2, tokens_by_type.keys() assert len(tokens_by_type) <= 2, tokens_by_type.keys()
embedded_strs = set() embedded_strs = set()
callback = {} callback = {}
for retok in tokens_by_type.get(PatternRE, []): for retok in tokens_by_type.get(PatternRE, []):
unless = [] # {}
unless = []
for strtok in tokens_by_type.get(PatternStr, []): for strtok in tokens_by_type.get(PatternStr, []):
if strtok.priority > retok.priority: if strtok.priority > retok.priority:
continue continue
@@ -247,13 +249,15 @@ def _build_mres(terminals, max_size, g_regex_flags, match_whole, re_, use_bytes)
except AssertionError: # Yes, this is what Python provides us.. :/ except AssertionError: # Yes, this is what Python provides us.. :/
return _build_mres(terminals, max_size//2, g_regex_flags, match_whole, re_, use_bytes) return _build_mres(terminals, max_size//2, g_regex_flags, match_whole, re_, use_bytes)


mres.append((mre, {i:n for n,i in mre.groupindex.items()} ))
mres.append((mre, {i: n for n, i in mre.groupindex.items()}))
terminals = terminals[max_size:] terminals = terminals[max_size:]
return mres return mres



def build_mres(terminals, g_regex_flags, re_, use_bytes, match_whole=False): def build_mres(terminals, g_regex_flags, re_, use_bytes, match_whole=False):
return _build_mres(terminals, len(terminals), g_regex_flags, match_whole, re_, use_bytes) return _build_mres(terminals, len(terminals), g_regex_flags, match_whole, re_, use_bytes)



def _regexp_has_newline(r): def _regexp_has_newline(r):
r"""Expressions that may indicate newlines in a regexp: r"""Expressions that may indicate newlines in a regexp:
- newlines (\n) - newlines (\n)
@@ -264,6 +268,7 @@ def _regexp_has_newline(r):
""" """
return '\n' in r or '\\n' in r or '\\s' in r or '[^' in r or ('(?s' in r and '.' in r) return '\n' in r or '\\n' in r or '\\s' in r or '[^' in r or ('(?s' in r and '.' in r)



class Lexer(object): class Lexer(object):
"""Lexer interface """Lexer interface


@@ -302,7 +307,7 @@ class TraditionalLexer(Lexer):
self.newline_types = frozenset(t.name for t in terminals if _regexp_has_newline(t.pattern.to_regexp())) self.newline_types = frozenset(t.name for t in terminals if _regexp_has_newline(t.pattern.to_regexp()))
self.ignore_types = frozenset(conf.ignore) self.ignore_types = frozenset(conf.ignore)


terminals.sort(key=lambda x:(-x.priority, -x.pattern.max_width, -len(x.pattern.value), x.name))
terminals.sort(key=lambda x: (-x.priority, -x.pattern.max_width, -len(x.pattern.value), x.name))
self.terminals = terminals self.terminals = terminals
self.user_callbacks = conf.callbacks self.user_callbacks = conf.callbacks
self.g_regex_flags = conf.g_regex_flags self.g_regex_flags = conf.g_regex_flags
@@ -311,7 +316,7 @@ class TraditionalLexer(Lexer):
self._mres = None self._mres = None


def _build(self): def _build(self):
terminals, self.callback = _create_unless(self.terminals, self.g_regex_flags, re_=self.re, use_bytes=self.use_bytes)
terminals, self.callback = _create_unless(self.terminals, self.g_regex_flags, self.re, self.use_bytes)
assert all(self.callback.values()) assert all(self.callback.values())


for type_, f in self.user_callbacks.items(): for type_, f in self.user_callbacks.items():
@@ -338,9 +343,9 @@ class TraditionalLexer(Lexer):
def lex(self, state, parser_state): def lex(self, state, parser_state):
with suppress(EOFError): with suppress(EOFError):
while True: while True:
yield self.next_token(state)
yield self.next_token(state, parser_state)


def next_token(self, lex_state):
def next_token(self, lex_state, parser_state=None):
line_ctr = lex_state.line_ctr line_ctr = lex_state.line_ctr
while line_ctr.char_pos < len(lex_state.text): while line_ctr.char_pos < len(lex_state.text):
res = self.match(lex_state.text, line_ctr.char_pos) res = self.match(lex_state.text, line_ctr.char_pos)
@@ -350,7 +355,7 @@ class TraditionalLexer(Lexer):
allowed = {"<END-OF-FILE>"} allowed = {"<END-OF-FILE>"}
raise UnexpectedCharacters(lex_state.text, line_ctr.char_pos, line_ctr.line, line_ctr.column, raise UnexpectedCharacters(lex_state.text, line_ctr.char_pos, line_ctr.line, line_ctr.column,
allowed=allowed, token_history=lex_state.last_token and [lex_state.last_token], allowed=allowed, token_history=lex_state.last_token and [lex_state.last_token],
_all_terminals=self.terminals)
state=parser_state, _all_terminals=self.terminals)


value, type_ = res value, type_ = res


@@ -363,7 +368,7 @@ class TraditionalLexer(Lexer):
if t.type in self.callback: if t.type in self.callback:
t = self.callback[t.type](t) t = self.callback[t.type](t)
if not isinstance(t, Token): if not isinstance(t, Token):
raise ValueError("Callbacks must return a token (returned %r)" % t)
raise LexError("Callbacks must return a token (returned %r)" % t)
lex_state.last_token = t lex_state.last_token = t
return t return t
else: else:
@@ -375,6 +380,7 @@ class TraditionalLexer(Lexer):
# EOF # EOF
raise EOFError(self) raise EOFError(self)



class LexerState: class LexerState:
__slots__ = 'text', 'line_ctr', 'last_token' __slots__ = 'text', 'line_ctr', 'last_token'


@@ -386,6 +392,7 @@ class LexerState:
def __copy__(self): def __copy__(self):
return type(self)(self.text, copy(self.line_ctr), self.last_token) return type(self)(self.text, copy(self.line_ctr), self.last_token)



class ContextualLexer(Lexer): class ContextualLexer(Lexer):


def __init__(self, conf, states, always_accept=()): def __init__(self, conf, states, always_accept=()):
@@ -424,17 +431,17 @@ class ContextualLexer(Lexer):
try: try:
while True: while True:
lexer = self.lexers[parser_state.position] lexer = self.lexers[parser_state.position]
yield lexer.next_token(lexer_state)
yield lexer.next_token(lexer_state, parser_state)
except EOFError: except EOFError:
pass pass
except UnexpectedCharacters as e: except UnexpectedCharacters as e:
# In the contextual lexer, UnexpectedCharacters can mean that the terminal is defined, but not in the current context. # In the contextual lexer, UnexpectedCharacters can mean that the terminal is defined, but not in the current context.
# This tests the input against the global context, to provide a nicer error. # This tests the input against the global context, to provide a nicer error.
token = self.root_lexer.next_token(lexer_state) token = self.root_lexer.next_token(lexer_state)
raise UnexpectedToken(token, e.allowed, state=parser_state.position, all_terminals=self.root_lexer.terminals)
raise UnexpectedToken(token, e.allowed, state=parser_state.position, token_history=[lexer_state.last_token], all_terminals=self.root_lexer.terminals)


class LexerThread: class LexerThread:
"A thread that ties a lexer instance and a lexer state, to be used by the parser"
"""A thread that ties a lexer instance and a lexer state, to be used by the parser"""


def __init__(self, lexer, text): def __init__(self, lexer, text):
self.lexer = lexer self.lexer = lexer


+ 164
- 64
lark/load_grammar.py View File

@@ -1,15 +1,17 @@
"Parses and creates Grammar objects"
"""Parses and creates Grammar objects"""


import os.path import os.path
import sys import sys
from copy import copy, deepcopy from copy import copy, deepcopy
from io import open from io import open
import pkgutil
from ast import literal_eval


from .utils import bfs, eval_escaping, Py36, logger, classify_bool, isascii
from .utils import bfs, Py36, logger, classify_bool
from .lexer import Token, TerminalDef, PatternStr, PatternRE from .lexer import Token, TerminalDef, PatternStr, PatternRE


from .parse_tree_builder import ParseTreeBuilder from .parse_tree_builder import ParseTreeBuilder
from .parser_frontends import LALR_TraditionalLexer
from .parser_frontends import ParsingFrontend
from .common import LexerConf, ParserConf from .common import LexerConf, ParserConf
from .grammar import RuleOptions, Rule, Terminal, NonTerminal, Symbol from .grammar import RuleOptions, Rule, Terminal, NonTerminal, Symbol
from .utils import classify, suppress, dedup_list, Str from .utils import classify, suppress, dedup_list, Str
@@ -20,7 +22,7 @@ from .visitors import Transformer, Visitor, v_args, Transformer_InPlace, Transfo
inline_args = v_args(inline=True) inline_args = v_args(inline=True)


__path__ = os.path.dirname(__file__) __path__ = os.path.dirname(__file__)
IMPORT_PATHS = [os.path.join(__path__, 'grammars')]
IMPORT_PATHS = ['grammars']


EXT = '.lark' EXT = '.lark'


@@ -165,6 +167,7 @@ RULES = {
'literal': ['REGEXP', 'STRING'], 'literal': ['REGEXP', 'STRING'],
} }



@inline_args @inline_args
class EBNF_to_BNF(Transformer_InPlace): class EBNF_to_BNF(Transformer_InPlace):
def __init__(self): def __init__(self):
@@ -258,9 +261,9 @@ class SimplifyRule_Visitor(Visitor):
for i, child in enumerate(tree.children): for i, child in enumerate(tree.children):
if isinstance(child, Tree) and child.data == 'expansions': if isinstance(child, Tree) and child.data == 'expansions':
tree.data = 'expansions' tree.data = 'expansions'
tree.children = [self.visit(ST('expansion', [option if i==j else other
for j, other in enumerate(tree.children)]))
for option in dedup_list(child.children)]
tree.children = [self.visit(ST('expansion', [option if i == j else other
for j, other in enumerate(tree.children)]))
for option in dedup_list(child.children)]
self._flatten(tree) self._flatten(tree)
break break


@@ -283,8 +286,10 @@ class SimplifyRule_Visitor(Visitor):
class RuleTreeToText(Transformer): class RuleTreeToText(Transformer):
def expansions(self, x): def expansions(self, x):
return x return x

def expansion(self, symbols): def expansion(self, symbols):
return symbols, None return symbols, None

def alias(self, x): def alias(self, x):
(expansion, _alias), alias = x (expansion, _alias), alias = x
assert _alias is None, (alias, expansion, '-', _alias) # Double alias not allowed assert _alias is None, (alias, expansion, '-', _alias) # Double alias not allowed
@@ -299,8 +304,9 @@ class CanonizeTree(Transformer_InPlace):
tokenmods, value = args tokenmods, value = args
return tokenmods + [value] return tokenmods + [value]



class PrepareAnonTerminals(Transformer_InPlace): class PrepareAnonTerminals(Transformer_InPlace):
"Create a unique list of anonymous terminals. Attempt to give meaningful names to them when we add them"
"""Create a unique list of anonymous terminals. Attempt to give meaningful names to them when we add them"""


def __init__(self, terminals): def __init__(self, terminals):
self.terminals = terminals self.terminals = terminals
@@ -309,7 +315,6 @@ class PrepareAnonTerminals(Transformer_InPlace):
self.i = 0 self.i = 0
self.rule_options = None self.rule_options = None



@inline_args @inline_args
def pattern(self, p): def pattern(self, p):
value = p.value value = p.value
@@ -328,14 +333,16 @@ class PrepareAnonTerminals(Transformer_InPlace):
try: try:
term_name = _TERMINAL_NAMES[value] term_name = _TERMINAL_NAMES[value]
except KeyError: except KeyError:
if value.isalnum() and value[0].isalpha() and value.upper() not in self.term_set and isascii(value):
term_name = value.upper()
if value.isalnum() and value[0].isalpha() and value.upper() not in self.term_set:
with suppress(UnicodeEncodeError):
value.upper().encode('ascii') # Make sure we don't have unicode in our terminal names
term_name = value.upper()


if term_name in self.term_set: if term_name in self.term_set:
term_name = None term_name = None


elif isinstance(p, PatternRE): elif isinstance(p, PatternRE):
if p in self.term_reverse: # Kind of a weird placement.name
if p in self.term_reverse: # Kind of a weird placement.name
term_name = self.term_reverse[p].name term_name = self.term_reverse[p].name
else: else:
assert False, p assert False, p
@@ -357,7 +364,7 @@ class PrepareAnonTerminals(Transformer_InPlace):




class _ReplaceSymbols(Transformer_InPlace): class _ReplaceSymbols(Transformer_InPlace):
" Helper for ApplyTemplates "
"""Helper for ApplyTemplates"""


def __init__(self): def __init__(self):
self.names = {} self.names = {}
@@ -372,8 +379,9 @@ class _ReplaceSymbols(Transformer_InPlace):
return self.__default__('template_usage', [self.names[c[0]].name] + c[1:], None) return self.__default__('template_usage', [self.names[c[0]].name] + c[1:], None)
return self.__default__('template_usage', c, None) return self.__default__('template_usage', c, None)



class ApplyTemplates(Transformer_InPlace): class ApplyTemplates(Transformer_InPlace):
" Apply the templates, creating new rules that represent the used templates "
"""Apply the templates, creating new rules that represent the used templates"""


def __init__(self, rule_defs): def __init__(self, rule_defs):
self.rule_defs = rule_defs self.rule_defs = rule_defs
@@ -399,6 +407,30 @@ def _rfind(s, choices):
return max(s.rfind(c) for c in choices) return max(s.rfind(c) for c in choices)




def eval_escaping(s):
w = ''
i = iter(s)
for n in i:
w += n
if n == '\\':
try:
n2 = next(i)
except StopIteration:
raise GrammarError("Literal ended unexpectedly (bad escaping): `%r`" % s)
if n2 == '\\':
w += '\\\\'
elif n2 not in 'uxnftr':
w += '\\'
w += n2
w = w.replace('\\"', '"').replace("'", "\\'")

to_eval = "u'''%s'''" % w
try:
s = literal_eval(to_eval)
except SyntaxError as e:
raise GrammarError(s, e)

return s




def _literal_to_pattern(literal): def _literal_to_pattern(literal):
@@ -439,7 +471,7 @@ class PrepareLiterals(Transformer_InPlace):
assert start.type == end.type == 'STRING' assert start.type == end.type == 'STRING'
start = start.value[1:-1] start = start.value[1:-1]
end = end.value[1:-1] end = end.value[1:-1]
assert len(eval_escaping(start)) == len(eval_escaping(end)) == 1, (start, end, len(eval_escaping(start)), len(eval_escaping(end)))
assert len(eval_escaping(start)) == len(eval_escaping(end)) == 1
regexp = '[%s-%s]' % (start, end) regexp = '[%s-%s]' % (start, end)
return ST('pattern', [PatternRE(regexp)]) return ST('pattern', [PatternRE(regexp)])


@@ -458,6 +490,7 @@ def _make_joined_pattern(regexp, flags_set):


return PatternRE(regexp, flags) return PatternRE(regexp, flags)



class TerminalTreeToPattern(Transformer): class TerminalTreeToPattern(Transformer):
def pattern(self, ps): def pattern(self, ps):
p ,= ps p ,= ps
@@ -501,6 +534,7 @@ class TerminalTreeToPattern(Transformer):
def value(self, v): def value(self, v):
return v[0] return v[0]



class PrepareSymbols(Transformer_InPlace): class PrepareSymbols(Transformer_InPlace):
def value(self, v): def value(self, v):
v ,= v v ,= v
@@ -512,13 +546,16 @@ class PrepareSymbols(Transformer_InPlace):
return Terminal(Str(v.value), filter_out=v.startswith('_')) return Terminal(Str(v.value), filter_out=v.startswith('_'))
assert False assert False



def _choice_of_rules(rules): def _choice_of_rules(rules):
return ST('expansions', [ST('expansion', [Token('RULE', name)]) for name in rules]) return ST('expansions', [ST('expansion', [Token('RULE', name)]) for name in rules])



def nr_deepcopy_tree(t): def nr_deepcopy_tree(t):
"Deepcopy tree `t` without recursion"
"""Deepcopy tree `t` without recursion"""
return Transformer_NonRecursive(False).transform(t) return Transformer_NonRecursive(False).transform(t)



class Grammar: class Grammar:
def __init__(self, rule_defs, term_defs, ignore): def __init__(self, rule_defs, term_defs, ignore):
self.term_defs = term_defs self.term_defs = term_defs
@@ -545,7 +582,7 @@ class Grammar:
raise GrammarError("Terminals cannot be empty (%s)" % name) raise GrammarError("Terminals cannot be empty (%s)" % name)


transformer = PrepareLiterals() * TerminalTreeToPattern() transformer = PrepareLiterals() * TerminalTreeToPattern()
terminals = [TerminalDef(name, transformer.transform( term_tree ), priority)
terminals = [TerminalDef(name, transformer.transform(term_tree), priority)
for name, (term_tree, priority) in term_defs if term_tree] for name, (term_tree, priority) in term_defs if term_tree]


# ================= # =================
@@ -564,10 +601,10 @@ class Grammar:
ebnf_to_bnf = EBNF_to_BNF() ebnf_to_bnf = EBNF_to_BNF()
rules = [] rules = []
i = 0 i = 0
while i < len(rule_defs): # We have to do it like this because rule_defs might grow due to templates
while i < len(rule_defs): # We have to do it like this because rule_defs might grow due to templates
name, params, rule_tree, options = rule_defs[i] name, params, rule_tree, options = rule_defs[i]
i += 1 i += 1
if len(params) != 0: # Dont transform templates
if len(params) != 0: # Dont transform templates
continue continue
rule_options = RuleOptions(keep_all_tokens=True) if options and options.keep_all_tokens else None rule_options = RuleOptions(keep_all_tokens=True) if options and options.keep_all_tokens else None
ebnf_to_bnf.rule_options = rule_options ebnf_to_bnf.rule_options = rule_options
@@ -592,7 +629,7 @@ class Grammar:


for i, (expansion, alias) in enumerate(expansions): for i, (expansion, alias) in enumerate(expansions):
if alias and name.startswith('_'): if alias and name.startswith('_'):
raise GrammarError("Rule %s is marked for expansion (it starts with an underscore) and isn't allowed to have aliases (alias=%s)" % (name, alias))
raise GrammarError("Rule %s is marked for expansion (it starts with an underscore) and isn't allowed to have aliases (alias=%s)"% (name, alias))


empty_indices = [x==_EMPTY for x in expansion] empty_indices = [x==_EMPTY for x in expansion]
if any(empty_indices): if any(empty_indices):
@@ -621,14 +658,13 @@ class Grammar:
# Remove duplicates # Remove duplicates
compiled_rules = list(set(compiled_rules)) compiled_rules = list(set(compiled_rules))



# Filter out unused rules # Filter out unused rules
while True: while True:
c = len(compiled_rules) c = len(compiled_rules)
used_rules = {s for r in compiled_rules used_rules = {s for r in compiled_rules
for s in r.expansion
if isinstance(s, NonTerminal)
and s != r.origin}
for s in r.expansion
if isinstance(s, NonTerminal)
and s != r.origin}
used_rules |= {NonTerminal(s) for s in start} used_rules |= {NonTerminal(s) for s in start}
compiled_rules, unused = classify_bool(compiled_rules, lambda r: r.origin in used_rules) compiled_rules, unused = classify_bool(compiled_rules, lambda r: r.origin in used_rules)
for r in unused: for r in unused:
@@ -647,9 +683,63 @@ class Grammar:
return terminals, compiled_rules, self.ignore return terminals, compiled_rules, self.ignore




class PackageResource(object):
"""
Represents a path inside a Package. Used by `FromPackageLoader`
"""
def __init__(self, pkg_name, path):
self.pkg_name = pkg_name
self.path = path

def __str__(self):
return "<%s: %s>" % (self.pkg_name, self.path)

def __repr__(self):
return "%s(%r, %r)" % (type(self).__name__, self.pkg_name, self.path)


class FromPackageLoader(object):
"""
Provides a simple way of creating custom import loaders that load from packages via ``pkgutil.get_data`` instead of using `open`.
This allows them to be compatible even from within zip files.

Relative imports are handled, so you can just freely use them.

pkg_name: The name of the package. You can probably provide `__name__` most of the time
search_paths: All the path that will be search on absolute imports.
"""
def __init__(self, pkg_name, search_paths=("", )):
self.pkg_name = pkg_name
self.search_paths = search_paths

def __repr__(self):
return "%s(%r, %r)" % (type(self).__name__, self.pkg_name, self.search_paths)

def __call__(self, base_path, grammar_path):
if base_path is None:
to_try = self.search_paths
else:
# Check whether or not the importing grammar was loaded by this module.
if not isinstance(base_path, PackageResource) or base_path.pkg_name != self.pkg_name:
# Technically false, but FileNotFound doesn't exist in python2.7, and this message should never reach the end user anyway
raise IOError()
to_try = [base_path.path]
for path in to_try:
full_path = os.path.join(path, grammar_path)
try:
text = pkgutil.get_data(self.pkg_name, full_path)
except IOError:
continue
else:
return PackageResource(self.pkg_name, full_path), text.decode()
raise IOError()


stdlib_loader = FromPackageLoader('lark', IMPORT_PATHS)


_imported_grammars = {} _imported_grammars = {}



def import_from_grammar_into_namespace(grammar, namespace, aliases): def import_from_grammar_into_namespace(grammar, namespace, aliases):
"""Returns all rules and terminals of grammar, prepended """Returns all rules and terminals of grammar, prepended
with a 'namespace' prefix, except for those which are aliased. with a 'namespace' prefix, except for those which are aliased.
@@ -670,8 +760,6 @@ def import_from_grammar_into_namespace(grammar, namespace, aliases):
raise GrammarError("Missing symbol '%s' in grammar %s" % (symbol, namespace)) raise GrammarError("Missing symbol '%s' in grammar %s" % (symbol, namespace))
return _find_used_symbols(tree) - set(params) return _find_used_symbols(tree) - set(params)




def get_namespace_name(name, params): def get_namespace_name(name, params):
if params is not None: if params is not None:
try: try:
@@ -692,19 +780,17 @@ def import_from_grammar_into_namespace(grammar, namespace, aliases):
else: else:
assert symbol.type == 'RULE' assert symbol.type == 'RULE'
_, params, tree, options = imported_rules[symbol] _, params, tree, options = imported_rules[symbol]
params_map = {p: ('%s__%s' if p[0]!='_' else '_%s__%s' ) % (namespace, p) for p in params}
params_map = {p: ('%s__%s' if p[0]!='_' else '_%s__%s') % (namespace, p) for p in params}
for t in tree.iter_subtrees(): for t in tree.iter_subtrees():
for i, c in enumerate(t.children): for i, c in enumerate(t.children):
if isinstance(c, Token) and c.type in ('RULE', 'TERMINAL'): if isinstance(c, Token) and c.type in ('RULE', 'TERMINAL'):
t.children[i] = Token(c.type, get_namespace_name(c, params_map)) t.children[i] = Token(c.type, get_namespace_name(c, params_map))
params = [params_map[p] for p in params] # We can not rely on ordered dictionaries
params = [params_map[p] for p in params] # We can not rely on ordered dictionaries
rule_defs.append((get_namespace_name(symbol, params_map), params, tree, options)) rule_defs.append((get_namespace_name(symbol, params_map), params, tree, options))



return term_defs, rule_defs return term_defs, rule_defs





def resolve_term_references(term_defs): def resolve_term_references(term_defs):
# TODO Solve with transitive closure (maybe) # TODO Solve with transitive closure (maybe)


@@ -744,7 +830,7 @@ def options_from_rule(name, params, *x):
else: else:
expansions ,= x expansions ,= x
priority = None priority = None
params = [t.value for t in params.children] if params is not None else [] # For the grammar parser
params = [t.value for t in params.children] if params is not None else [] # For the grammar parser


keep_all_tokens = name.startswith('!') keep_all_tokens = name.startswith('!')
name = name.lstrip('!') name = name.lstrip('!')
@@ -758,10 +844,12 @@ def options_from_rule(name, params, *x):
def symbols_from_strcase(expansion): def symbols_from_strcase(expansion):
return [Terminal(x, filter_out=x.startswith('_')) if x.isupper() else NonTerminal(x) for x in expansion] return [Terminal(x, filter_out=x.startswith('_')) if x.isupper() else NonTerminal(x) for x in expansion]



@inline_args @inline_args
class PrepareGrammar(Transformer_InPlace): class PrepareGrammar(Transformer_InPlace):
def terminal(self, name): def terminal(self, name):
return name return name

def nonterminal(self, name): def nonterminal(self, name):
return name return name


@@ -771,10 +859,11 @@ def _find_used_symbols(tree):
return {t for x in tree.find_data('expansion') return {t for x in tree.find_data('expansion')
for t in x.scan_values(lambda t: t.type in ('RULE', 'TERMINAL'))} for t in x.scan_values(lambda t: t.type in ('RULE', 'TERMINAL'))}



class GrammarLoader: class GrammarLoader:
ERRORS = [ ERRORS = [
('Unclosed parenthesis', ['a: (\n']), ('Unclosed parenthesis', ['a: (\n']),
('Umatched closing parenthesis', ['a: )\n', 'a: [)\n', 'a: (]\n']),
('Unmatched closing parenthesis', ['a: )\n', 'a: [)\n', 'a: (]\n']),
('Expecting rule or terminal definition (missing colon)', ['a\n', 'A\n', 'a->\n', 'A->\n', 'a A\n']), ('Expecting rule or terminal definition (missing colon)', ['a\n', 'A\n', 'a->\n', 'A->\n', 'a A\n']),
('Illegal name for rules or terminals', ['Aa:\n']), ('Illegal name for rules or terminals', ['Aa:\n']),
('Alias expects lowercase name', ['a: -> "a"\n']), ('Alias expects lowercase name', ['a: -> "a"\n']),
@@ -786,43 +875,53 @@ class GrammarLoader:
('%ignore expects a value', ['%ignore %import\n']), ('%ignore expects a value', ['%ignore %import\n']),
] ]


def __init__(self, re_module, global_keep_all_tokens):
def __init__(self, global_keep_all_tokens):
terminals = [TerminalDef(name, PatternRE(value)) for name, value in TERMINALS.items()] terminals = [TerminalDef(name, PatternRE(value)) for name, value in TERMINALS.items()]


rules = [options_from_rule(name, None, x) for name, x in RULES.items()]
rules = [Rule(NonTerminal(r), symbols_from_strcase(x.split()), i, None, o) for r, _p, xs, o in rules for i, x in enumerate(xs)]
rules = [options_from_rule(name, None, x) for name, x in RULES.items()]
rules = [Rule(NonTerminal(r), symbols_from_strcase(x.split()), i, None, o)
for r, _p, xs, o in rules for i, x in enumerate(xs)]
callback = ParseTreeBuilder(rules, ST).create_callback() callback = ParseTreeBuilder(rules, ST).create_callback()
lexer_conf = LexerConf(terminals, re_module, ['WS', 'COMMENT'])
import re
lexer_conf = LexerConf(terminals, re, ['WS', 'COMMENT'])
parser_conf = ParserConf(rules, callback, ['start']) parser_conf = ParserConf(rules, callback, ['start'])
self.parser = LALR_TraditionalLexer(lexer_conf, parser_conf)
lexer_conf.lexer_type = 'standard'
parser_conf.parser_type = 'lalr'
self.parser = ParsingFrontend(lexer_conf, parser_conf, {})


self.canonize_tree = CanonizeTree() self.canonize_tree = CanonizeTree()
self.re_module = re_module
self.global_keep_all_tokens = global_keep_all_tokens self.global_keep_all_tokens = global_keep_all_tokens


def import_grammar(self, grammar_path, base_paths=[]):
def import_grammar(self, grammar_path, base_path=None, import_paths=[]):
if grammar_path not in _imported_grammars: if grammar_path not in _imported_grammars:
import_paths = base_paths + IMPORT_PATHS
for import_path in import_paths:
with suppress(IOError):
joined_path = os.path.join(import_path, grammar_path)
with open(joined_path, encoding='utf8') as f:
text = f.read()
grammar = self.load_grammar(text, joined_path)
# import_paths take priority over base_path since they should handle relative imports and ignore everything else.
to_try = import_paths + ([base_path] if base_path is not None else []) + [stdlib_loader]
for source in to_try:
try:
if callable(source):
joined_path, text = source(base_path, grammar_path)
else:
joined_path = os.path.join(source, grammar_path)
with open(joined_path, encoding='utf8') as f:
text = f.read()
except IOError:
continue
else:
grammar = self.load_grammar(text, joined_path, import_paths)
_imported_grammars[grammar_path] = grammar _imported_grammars[grammar_path] = grammar
break break
else: else:
open(grammar_path, encoding='utf8') # Force a file not found error
# Search failed. Make Python throw a nice error.
open(grammar_path, encoding='utf8')
assert False assert False


return _imported_grammars[grammar_path] return _imported_grammars[grammar_path]


def load_grammar(self, grammar_text, grammar_name='<?>'):
"Parse grammar_text, verify, and create Grammar object. Display nice messages on error."
def load_grammar(self, grammar_text, grammar_name='<?>', import_paths=[]):
"""Parse grammar_text, verify, and create Grammar object. Display nice messages on error."""


try: try:
tree = self.canonize_tree.transform( self.parser.parse(grammar_text+'\n') )
tree = self.canonize_tree.transform(self.parser.parse(grammar_text+'\n'))
except UnexpectedCharacters as e: except UnexpectedCharacters as e:
context = e.get_context(grammar_text) context = e.get_context(grammar_text)
raise GrammarError("Unexpected input at line %d column %d in %s: \n\n%s" % raise GrammarError("Unexpected input at line %d column %d in %s: \n\n%s" %
@@ -872,7 +971,7 @@ class GrammarLoader:
aliases = {name: arg1 or name} # Aliases if exist aliases = {name: arg1 or name} # Aliases if exist


if path_node.data == 'import_lib': # Import from library if path_node.data == 'import_lib': # Import from library
base_paths = []
base_path = None
else: # Relative import else: # Relative import
if grammar_name == '<string>': # Import relative to script file path if grammar is coded in script if grammar_name == '<string>': # Import relative to script file path if grammar is coded in script
try: try:
@@ -882,16 +981,19 @@ class GrammarLoader:
else: else:
base_file = grammar_name # Import relative to grammar file path if external grammar file base_file = grammar_name # Import relative to grammar file path if external grammar file
if base_file: if base_file:
base_paths = [os.path.split(base_file)[0]]
if isinstance(base_file, PackageResource):
base_path = PackageResource(base_file.pkg_name, os.path.split(base_file.path)[0])
else:
base_path = os.path.split(base_file)[0]
else: else:
base_paths = [os.path.abspath(os.path.curdir)]
base_path = os.path.abspath(os.path.curdir)


try: try:
import_base_paths, import_aliases = imports[dotted_path]
assert base_paths == import_base_paths, 'Inconsistent base_paths for %s.' % '.'.join(dotted_path)
import_base_path, import_aliases = imports[dotted_path]
assert base_path == import_base_path, 'Inconsistent base_path for %s.' % '.'.join(dotted_path)
import_aliases.update(aliases) import_aliases.update(aliases)
except KeyError: except KeyError:
imports[dotted_path] = base_paths, aliases
imports[dotted_path] = base_path, aliases


elif stmt.data == 'declare': elif stmt.data == 'declare':
for t in stmt.children: for t in stmt.children:
@@ -900,9 +1002,9 @@ class GrammarLoader:
assert False, stmt assert False, stmt


# import grammars # import grammars
for dotted_path, (base_paths, aliases) in imports.items():
for dotted_path, (base_path, aliases) in imports.items():
grammar_path = os.path.join(*dotted_path) + EXT grammar_path = os.path.join(*dotted_path) + EXT
g = self.import_grammar(grammar_path, base_paths=base_paths)
g = self.import_grammar(grammar_path, base_path=base_path, import_paths=import_paths)
new_td, new_rd = import_from_grammar_into_namespace(g, '__'.join(dotted_path), aliases) new_td, new_rd = import_from_grammar_into_namespace(g, '__'.join(dotted_path), aliases)


term_defs += new_td term_defs += new_td
@@ -972,7 +1074,7 @@ class GrammarLoader:
raise GrammarError("Template '%s' used but not defined (in rule %s)" % (sym, name)) raise GrammarError("Template '%s' used but not defined (in rule %s)" % (sym, name))
if len(args) != rule_names[sym]: if len(args) != rule_names[sym]:
raise GrammarError("Wrong number of template arguments used for %s " raise GrammarError("Wrong number of template arguments used for %s "
"(expected %s, got %s) (in rule %s)"%(sym, rule_names[sym], len(args), name))
"(expected %s, got %s) (in rule %s)" % (sym, rule_names[sym], len(args), name))
for sym in _find_used_symbols(expansions): for sym in _find_used_symbols(expansions):
if sym.type == 'TERMINAL': if sym.type == 'TERMINAL':
if sym not in terminal_names: if sym not in terminal_names:
@@ -981,10 +1083,8 @@ class GrammarLoader:
if sym not in rule_names and sym not in params: if sym not in rule_names and sym not in params:
raise GrammarError("Rule '%s' used but not defined (in rule %s)" % (sym, name)) raise GrammarError("Rule '%s' used but not defined (in rule %s)" % (sym, name))



return Grammar(rules, term_defs, ignore_names) return Grammar(rules, term_defs, ignore_names)





def load_grammar(grammar, source, re_, global_keep_all_tokens):
return GrammarLoader(re_, global_keep_all_tokens).load_grammar(grammar, source)
def load_grammar(grammar, source, import_paths, global_keep_all_tokens):
return GrammarLoader(global_keep_all_tokens).load_grammar(grammar, source, import_paths)

+ 19
- 8
lark/parse_tree_builder.py View File

@@ -1,7 +1,7 @@
from .exceptions import GrammarError from .exceptions import GrammarError
from .lexer import Token from .lexer import Token
from .tree import Tree from .tree import Tree
from .visitors import InlineTransformer # XXX Deprecated
from .visitors import InlineTransformer # XXX Deprecated
from .visitors import Transformer_InPlace from .visitors import Transformer_InPlace
from .visitors import _vargs_meta, _vargs_meta_inline from .visitors import _vargs_meta, _vargs_meta_inline


@@ -20,6 +20,7 @@ class ExpandSingleChild:
else: else:
return self.node_builder(children) return self.node_builder(children)



class PropagatePositions: class PropagatePositions:
def __init__(self, node_builder): def __init__(self, node_builder):
self.node_builder = node_builder self.node_builder = node_builder
@@ -87,8 +88,9 @@ class ChildFilter:


return self.node_builder(filtered) return self.node_builder(filtered)



class ChildFilterLALR(ChildFilter): class ChildFilterLALR(ChildFilter):
"Optimized childfilter for LALR (assumes no duplication in parse tree, so it's safe to change it)"
"""Optimized childfilter for LALR (assumes no duplication in parse tree, so it's safe to change it)"""


def __call__(self, children): def __call__(self, children):
filtered = [] filtered = []
@@ -108,6 +110,7 @@ class ChildFilterLALR(ChildFilter):


return self.node_builder(filtered) return self.node_builder(filtered)



class ChildFilterLALR_NoPlaceholders(ChildFilter): class ChildFilterLALR_NoPlaceholders(ChildFilter):
"Optimized childfilter for LALR (assumes no duplication in parse tree, so it's safe to change it)" "Optimized childfilter for LALR (assumes no duplication in parse tree, so it's safe to change it)"
def __init__(self, to_include, node_builder): def __init__(self, to_include, node_builder):
@@ -126,9 +129,11 @@ class ChildFilterLALR_NoPlaceholders(ChildFilter):
filtered.append(children[i]) filtered.append(children[i])
return self.node_builder(filtered) return self.node_builder(filtered)



def _should_expand(sym): def _should_expand(sym):
return not sym.is_term and sym.name.startswith('_') return not sym.is_term and sym.name.startswith('_')



def maybe_create_child_filter(expansion, keep_all_tokens, ambiguous, _empty_indices): def maybe_create_child_filter(expansion, keep_all_tokens, ambiguous, _empty_indices):
# Prepare empty_indices as: How many Nones to insert at each index? # Prepare empty_indices as: How many Nones to insert at each index?
if _empty_indices: if _empty_indices:
@@ -156,21 +161,22 @@ def maybe_create_child_filter(expansion, keep_all_tokens, ambiguous, _empty_indi
# LALR without placeholders # LALR without placeholders
return partial(ChildFilterLALR_NoPlaceholders, [(i, x) for i,x,_ in to_include]) return partial(ChildFilterLALR_NoPlaceholders, [(i, x) for i,x,_ in to_include])



class AmbiguousExpander: class AmbiguousExpander:
"""Deal with the case where we're expanding children ('_rule') into a parent but the children """Deal with the case where we're expanding children ('_rule') into a parent but the children
are ambiguous. i.e. (parent->_ambig->_expand_this_rule). In this case, make the parent itself are ambiguous. i.e. (parent->_ambig->_expand_this_rule). In this case, make the parent itself
ambiguous with as many copies as their are ambiguous children, and then copy the ambiguous children ambiguous with as many copies as their are ambiguous children, and then copy the ambiguous children
into the right parents in the right places, essentially shifting the ambiguiuty up the tree."""
into the right parents in the right places, essentially shifting the ambiguity up the tree."""
def __init__(self, to_expand, tree_class, node_builder): def __init__(self, to_expand, tree_class, node_builder):
self.node_builder = node_builder self.node_builder = node_builder
self.tree_class = tree_class self.tree_class = tree_class
self.to_expand = to_expand self.to_expand = to_expand


def __call__(self, children): def __call__(self, children):
def _is_ambig_tree(child):
return hasattr(child, 'data') and child.data == '_ambig'
def _is_ambig_tree(t):
return hasattr(t, 'data') and t.data == '_ambig'


#### When we're repeatedly expanding ambiguities we can end up with nested ambiguities.
# -- When we're repeatedly expanding ambiguities we can end up with nested ambiguities.
# All children of an _ambig node should be a derivation of that ambig node, hence # All children of an _ambig node should be a derivation of that ambig node, hence
# it is safe to assume that if we see an _ambig node nested within an ambig node # it is safe to assume that if we see an _ambig node nested within an ambig node
# it is safe to simply expand it into the parent _ambig node as an alternative derivation. # it is safe to simply expand it into the parent _ambig node as an alternative derivation.
@@ -186,15 +192,17 @@ class AmbiguousExpander:
if not ambiguous: if not ambiguous:
return self.node_builder(children) return self.node_builder(children)


expand = [ iter(child.children) if i in ambiguous else repeat(child) for i, child in enumerate(children) ]
expand = [iter(child.children) if i in ambiguous else repeat(child) for i, child in enumerate(children)]
return self.tree_class('_ambig', [self.node_builder(list(f[0])) for f in product(zip(*expand))]) return self.tree_class('_ambig', [self.node_builder(list(f[0])) for f in product(zip(*expand))])



def maybe_create_ambiguous_expander(tree_class, expansion, keep_all_tokens): def maybe_create_ambiguous_expander(tree_class, expansion, keep_all_tokens):
to_expand = [i for i, sym in enumerate(expansion) to_expand = [i for i, sym in enumerate(expansion)
if keep_all_tokens or ((not (sym.is_term and sym.filter_out)) and _should_expand(sym))] if keep_all_tokens or ((not (sym.is_term and sym.filter_out)) and _should_expand(sym))]
if to_expand: if to_expand:
return partial(AmbiguousExpander, to_expand, tree_class) return partial(AmbiguousExpander, to_expand, tree_class)



class AmbiguousIntermediateExpander: class AmbiguousIntermediateExpander:
""" """
Propagate ambiguous intermediate nodes and their derivations up to the Propagate ambiguous intermediate nodes and their derivations up to the
@@ -275,12 +283,14 @@ class AmbiguousIntermediateExpander:


return self.node_builder(children) return self.node_builder(children)



def ptb_inline_args(func): def ptb_inline_args(func):
@wraps(func) @wraps(func)
def f(children): def f(children):
return func(*children) return func(*children)
return f return f



def inplace_transformer(func): def inplace_transformer(func):
@wraps(func) @wraps(func)
def f(children): def f(children):
@@ -289,9 +299,11 @@ def inplace_transformer(func):
return func(tree) return func(tree)
return f return f



def apply_visit_wrapper(func, name, wrapper): def apply_visit_wrapper(func, name, wrapper):
if wrapper is _vargs_meta or wrapper is _vargs_meta_inline: if wrapper is _vargs_meta or wrapper is _vargs_meta_inline:
raise NotImplementedError("Meta args not supported for internal transformer") raise NotImplementedError("Meta args not supported for internal transformer")

@wraps(func) @wraps(func)
def f(children): def f(children):
return wrapper(func, name, children, None) return wrapper(func, name, children, None)
@@ -323,7 +335,6 @@ class ParseTreeBuilder:


yield rule, wrapper_chain yield rule, wrapper_chain



def create_callback(self, transformer=None): def create_callback(self, transformer=None):
callbacks = {} callbacks = {}




+ 155
- 189
lark/parser_frontends.py View File

@@ -1,12 +1,11 @@
from .exceptions import ConfigurationError, GrammarError, assert_config
from .utils import get_regexp_width, Serialize from .utils import get_regexp_width, Serialize
from .parsers.grammar_analysis import GrammarAnalyzer from .parsers.grammar_analysis import GrammarAnalyzer
from .lexer import LexerThread, TraditionalLexer, ContextualLexer, Lexer, Token, TerminalDef from .lexer import LexerThread, TraditionalLexer, ContextualLexer, Lexer, Token, TerminalDef
from .parsers import earley, xearley, cyk from .parsers import earley, xearley, cyk
from .parsers.lalr_parser import LALR_Parser from .parsers.lalr_parser import LALR_Parser
from .grammar import Rule
from .tree import Tree from .tree import Tree
from .common import LexerConf
from .exceptions import UnexpectedInput
from .common import LexerConf, ParserConf
try: try:
import regex import regex
except ImportError: except ImportError:
@@ -15,63 +14,118 @@ import re


###{standalone ###{standalone


def get_frontend(parser, lexer):
if parser=='lalr':
if lexer is None:
raise ValueError('The LALR parser requires use of a lexer')
elif lexer == 'standard':
return LALR_TraditionalLexer
elif lexer == 'contextual':
return LALR_ContextualLexer
elif issubclass(lexer, Lexer):
class CustomLexerWrapper(Lexer):
def __init__(self, lexer_conf):
self.lexer = lexer(lexer_conf)
def lex(self, lexer_state, parser_state):
return self.lexer.lex(lexer_state.text)

class LALR_CustomLexerWrapper(LALR_CustomLexer):
def __init__(self, lexer_conf, parser_conf, options=None):
super(LALR_CustomLexerWrapper, self).__init__(
lexer, lexer_conf, parser_conf, options=options)
def init_lexer(self):
future_interface = getattr(lexer, '__future_interface__', False)
if future_interface:
self.lexer = lexer(self.lexer_conf)
else:
self.lexer = CustomLexerWrapper(self.lexer_conf)

return LALR_CustomLexerWrapper
else:
raise ValueError('Unknown lexer: %s' % lexer)
elif parser=='earley':
if lexer=='standard':
return Earley
elif lexer=='dynamic':
return XEarley
elif lexer=='dynamic_complete':
return XEarley_CompleteLex
elif lexer=='contextual':
raise ValueError('The Earley parser does not support the contextual parser')
def _wrap_lexer(lexer_class):
future_interface = getattr(lexer_class, '__future_interface__', False)
if future_interface:
return lexer_class
else:
class CustomLexerWrapper(Lexer):
def __init__(self, lexer_conf):
self.lexer = lexer_class(lexer_conf)
def lex(self, lexer_state, parser_state):
return self.lexer.lex(lexer_state.text)
return CustomLexerWrapper


class MakeParsingFrontend:
def __init__(self, parser_type, lexer_type):
self.parser_type = parser_type
self.lexer_type = lexer_type

def __call__(self, lexer_conf, parser_conf, options):
assert isinstance(lexer_conf, LexerConf)
assert isinstance(parser_conf, ParserConf)
parser_conf.parser_type = self.parser_type
lexer_conf.lexer_type = self.lexer_type
return ParsingFrontend(lexer_conf, parser_conf, options)

@classmethod
def deserialize(cls, data, memo, callbacks, options):
lexer_conf = LexerConf.deserialize(data['lexer_conf'], memo)
parser_conf = ParserConf.deserialize(data['parser_conf'], memo)
parser = LALR_Parser.deserialize(data['parser'], memo, callbacks, options.debug)
parser_conf.callbacks = callbacks

terminals = [item for item in memo.values() if isinstance(item, TerminalDef)]

lexer_conf.callbacks = _get_lexer_callbacks(options.transformer, terminals)
lexer_conf.re_module = regex if options.regex else re
lexer_conf.use_bytes = options.use_bytes
lexer_conf.g_regex_flags = options.g_regex_flags
lexer_conf.skip_validation = True
lexer_conf.postlex = options.postlex

return ParsingFrontend(lexer_conf, parser_conf, options, parser=parser)




class ParsingFrontend(Serialize):
__serialize_fields__ = 'lexer_conf', 'parser_conf', 'parser', 'options'

def __init__(self, lexer_conf, parser_conf, options, parser=None):
self.parser_conf = parser_conf
self.lexer_conf = lexer_conf
self.options = options

# Set-up parser
if parser: # From cache
self.parser = parser
else: else:
raise ValueError('Unknown lexer: %s' % lexer)
elif parser == 'cyk':
if lexer == 'standard':
return CYK
create_parser = {
'lalr': create_lalr_parser,
'earley': create_earley_parser,
'cyk': CYK_FrontEnd,
}[parser_conf.parser_type]
self.parser = create_parser(lexer_conf, parser_conf, options)

# Set-up lexer
lexer_type = lexer_conf.lexer_type
self.skip_lexer = False
if lexer_type in ('dynamic', 'dynamic_complete'):
self.skip_lexer = True
return

try:
create_lexer = {
'standard': create_traditional_lexer,
'contextual': create_contextual_lexer,
}[lexer_type]
except KeyError:
assert issubclass(lexer_type, Lexer), lexer_type
self.lexer = _wrap_lexer(lexer_type)(lexer_conf)
else: else:
raise ValueError('CYK parser requires using standard parser.')
else:
raise ValueError('Unknown parser: %s' % parser)
self.lexer = create_lexer(lexer_conf, self.parser, lexer_conf.postlex)


if lexer_conf.postlex:
self.lexer = PostLexConnector(self.lexer, lexer_conf.postlex)


class _ParserFrontend(Serialize):
def _parse(self, start, input, *args):
def parse(self, text, start=None):
if start is None: if start is None:
start = self.start
start = self.parser_conf.start
if len(start) > 1: if len(start) > 1:
raise ValueError("Lark initialized with more than 1 possible start rule. Must specify which start rule to parse", start)
raise ConfigurationError("Lark initialized with more than 1 possible start rule. Must specify which start rule to parse", start)
start ,= start start ,= start
return self.parser.parse(input, start, *args)

if self.skip_lexer:
return self.parser.parse(text, start)

lexer_thread = LexerThread(self.lexer, text)
return self.parser.parse(lexer_thread, start)


def get_frontend(parser, lexer):
assert_config(parser, ('lalr', 'earley', 'cyk'))
if not isinstance(lexer, type): # not custom lexer?
expected = {
'lalr': ('standard', 'contextual'),
'earley': ('standard', 'dynamic', 'dynamic_complete'),
'cyk': ('standard', ),
}[parser]
assert_config(lexer, expected, 'Parser %r does not support lexer %%r, expected one of %%s' % parser)

return MakeParsingFrontend(parser, lexer)




def _get_lexer_callbacks(transformer, terminals): def _get_lexer_callbacks(transformer, terminals):
@@ -95,174 +149,86 @@ class PostLexConnector:
return self.postlexer.process(i) return self.postlexer.process(i)




class WithLexer(_ParserFrontend):
lexer = None
parser = None
lexer_conf = None
start = None

__serialize_fields__ = 'parser', 'lexer_conf', 'start'
__serialize_namespace__ = LexerConf,

def __init__(self, lexer_conf, parser_conf, options=None):
self.lexer_conf = lexer_conf
self.start = parser_conf.start
self.postlex = lexer_conf.postlex

@classmethod
def deserialize(cls, data, memo, callbacks, options):
inst = super(WithLexer, cls).deserialize(data, memo)

inst.postlex = options.postlex
inst.parser = LALR_Parser.deserialize(inst.parser, memo, callbacks, options.debug)

terminals = [item for item in memo.values() if isinstance(item, TerminalDef)]
inst.lexer_conf.callbacks = _get_lexer_callbacks(options.transformer, terminals)
inst.lexer_conf.re_module = regex if options.regex else re
inst.lexer_conf.use_bytes = options.use_bytes
inst.lexer_conf.g_regex_flags = options.g_regex_flags
inst.lexer_conf.skip_validation = True
inst.init_lexer()

return inst

def _serialize(self, data, memo):
data['parser'] = data['parser'].serialize(memo)


def make_lexer(self, text):
lexer = self.lexer
if self.postlex:
lexer = PostLexConnector(self.lexer, self.postlex)
return LexerThread(lexer, text)
def create_traditional_lexer(lexer_conf, parser, postlex):
return TraditionalLexer(lexer_conf)


def parse(self, text, start=None):
try:
return self._parse(start, self.make_lexer(text))
except UnexpectedInput as e:
if e._all_terminals is None:
e._all_terminals = self.lexer_conf.terminals
raise e

def init_traditional_lexer(self):
self.lexer = TraditionalLexer(self.lexer_conf)

class LALR_WithLexer(WithLexer):
def __init__(self, lexer_conf, parser_conf, options=None):
debug = options.debug if options else False
self.parser = LALR_Parser(parser_conf, debug=debug)
WithLexer.__init__(self, lexer_conf, parser_conf, options)

self.init_lexer()
def create_contextual_lexer(lexer_conf, parser, postlex):
states = {idx:list(t.keys()) for idx, t in parser._parse_table.states.items()}
always_accept = postlex.always_accept if postlex else ()
return ContextualLexer(lexer_conf, states, always_accept=always_accept)


def init_lexer(self, **kw):
raise NotImplementedError()
def create_lalr_parser(lexer_conf, parser_conf, options=None):
debug = options.debug if options else False
return LALR_Parser(parser_conf, debug=debug)


class LALR_TraditionalLexer(LALR_WithLexer):
def init_lexer(self):
self.init_traditional_lexer()

class LALR_ContextualLexer(LALR_WithLexer):
def init_lexer(self):
states = {idx:list(t.keys()) for idx, t in self.parser._parse_table.states.items()}
always_accept = self.postlex.always_accept if self.postlex else ()
self.lexer = ContextualLexer(self.lexer_conf, states, always_accept=always_accept)


create_earley_parser = NotImplemented
CYK_FrontEnd = NotImplemented
###} ###}


class LALR_CustomLexer(LALR_WithLexer):
def __init__(self, lexer_cls, lexer_conf, parser_conf, options=None):
self.lexer = lexer_cls(lexer_conf)
debug = options.debug if options else False
self.parser = LALR_Parser(parser_conf, debug=debug)
WithLexer.__init__(self, lexer_conf, parser_conf, options)


class Earley(WithLexer):
def __init__(self, lexer_conf, parser_conf, options=None):
WithLexer.__init__(self, lexer_conf, parser_conf, options)
self.init_traditional_lexer()

resolve_ambiguity = options.ambiguity == 'resolve'
debug = options.debug if options else False
tree_class = options.tree_class or Tree if options.ambiguity != 'forest' else None
self.parser = earley.Parser(parser_conf, self.match, resolve_ambiguity=resolve_ambiguity, debug=debug, tree_class=tree_class)

def make_lexer(self, text):
return WithLexer.make_lexer(self, text).lex(None)

def match(self, term, token):
return term.name == token.type


class XEarley(_ParserFrontend):
def __init__(self, lexer_conf, parser_conf, options=None, **kw):
self.terminals_by_name = {t.name:t for t in lexer_conf.terminals}
self.start = parser_conf.start

self._prepare_match(lexer_conf)
resolve_ambiguity = options.ambiguity == 'resolve'
debug = options.debug if options else False
tree_class = options.tree_class or Tree if options.ambiguity != 'forest' else None
self.parser = xearley.Parser(parser_conf,
self.match,
ignore=lexer_conf.ignore,
resolve_ambiguity=resolve_ambiguity,
debug=debug,
tree_class=tree_class,
**kw
)

def match(self, term, text, index=0):
return self.regexps[term.name].match(text, index)

def _prepare_match(self, lexer_conf):
class EarleyRegexpMatcher:
def __init__(self, lexer_conf):
self.regexps = {} self.regexps = {}
for t in lexer_conf.terminals: for t in lexer_conf.terminals:
if t.priority != 1: if t.priority != 1:
raise ValueError("Dynamic Earley doesn't support weights on terminals", t, t.priority)
raise GrammarError("Dynamic Earley doesn't support weights on terminals", t, t.priority)
regexp = t.pattern.to_regexp() regexp = t.pattern.to_regexp()
try: try:
width = get_regexp_width(regexp)[0] width = get_regexp_width(regexp)[0]
except ValueError: except ValueError:
raise ValueError("Bad regexp in token %s: %s" % (t.name, regexp))
raise GrammarError("Bad regexp in token %s: %s" % (t.name, regexp))
else: else:
if width == 0: if width == 0:
raise ValueError("Dynamic Earley doesn't allow zero-width regexps", t)
raise GrammarError("Dynamic Earley doesn't allow zero-width regexps", t)
if lexer_conf.use_bytes: if lexer_conf.use_bytes:
regexp = regexp.encode('utf-8') regexp = regexp.encode('utf-8')


self.regexps[t.name] = lexer_conf.re_module.compile(regexp, lexer_conf.g_regex_flags) self.regexps[t.name] = lexer_conf.re_module.compile(regexp, lexer_conf.g_regex_flags)


def parse(self, text, start):
try:
return self._parse(start, text)
except UnexpectedInput as e:
if e._all_terminals is None:
e._all_terminals = self.terminals_by_name
raise e
def match(self, term, text, index=0):
return self.regexps[term.name].match(text, index)


class XEarley_CompleteLex(XEarley):
def __init__(self, *args, **kw):
XEarley.__init__(self, *args, complete_lex=True, **kw)


def create_earley_parser__dynamic(lexer_conf, parser_conf, options=None, **kw):
earley_matcher = EarleyRegexpMatcher(lexer_conf)
return xearley.Parser(parser_conf, earley_matcher.match, ignore=lexer_conf.ignore, **kw)


def _match_earley_basic(term, token):
return term.name == token.type


class CYK(WithLexer):
def create_earley_parser__basic(lexer_conf, parser_conf, options, **kw):
return earley.Parser(parser_conf, _match_earley_basic, **kw)


def __init__(self, lexer_conf, parser_conf, options=None):
WithLexer.__init__(self, lexer_conf, parser_conf, options)
self.init_traditional_lexer()
def create_earley_parser(lexer_conf, parser_conf, options):
resolve_ambiguity = options.ambiguity == 'resolve'
debug = options.debug if options else False
tree_class = options.tree_class or Tree if options.ambiguity != 'forest' else None

extra = {}
if lexer_conf.lexer_type == 'dynamic':
f = create_earley_parser__dynamic
elif lexer_conf.lexer_type == 'dynamic_complete':
extra['complete_lex'] =True
f = create_earley_parser__dynamic
else:
f = create_earley_parser__basic


return f(lexer_conf, parser_conf, options, resolve_ambiguity=resolve_ambiguity, debug=debug, tree_class=tree_class, **extra)



class CYK_FrontEnd:
def __init__(self, lexer_conf, parser_conf, options=None):
self._analysis = GrammarAnalyzer(parser_conf) self._analysis = GrammarAnalyzer(parser_conf)
self.parser = cyk.Parser(parser_conf.rules) self.parser = cyk.Parser(parser_conf.rules)


self.callbacks = parser_conf.callbacks self.callbacks = parser_conf.callbacks


def parse(self, text, start):
tokens = list(self.make_lexer(text).lex(None))
parse = self._parse(start, tokens)
parse = self._transform(parse)
return parse
def parse(self, lexer_thread, start):
tokens = list(lexer_thread.lex(None))
tree = self.parser.parse(tokens, start)
return self._transform(tree)


def _transform(self, tree): def _transform(self, tree):
subtrees = list(tree.iter_subtrees()) subtrees = list(tree.iter_subtrees())


+ 16
- 12
lark/parsers/earley.py View File

@@ -1,4 +1,4 @@
"""This module implements an scanerless Earley parser.
"""This module implements an Earley parser.


The core Earley algorithm used here is based on Elizabeth Scott's implementation, here: The core Earley algorithm used here is based on Elizabeth Scott's implementation, here:
https://www.sciencedirect.com/science/article/pii/S1571066108001497 https://www.sciencedirect.com/science/article/pii/S1571066108001497
@@ -6,8 +6,7 @@ The core Earley algorithm used here is based on Elizabeth Scott's implementation
That is probably the best reference for understanding the algorithm here. That is probably the best reference for understanding the algorithm here.


The Earley parser outputs an SPPF-tree as per that document. The SPPF tree format The Earley parser outputs an SPPF-tree as per that document. The SPPF tree format
is better documented here:
http://www.bramvandersanden.com/post/2014/06/shared-packed-parse-forest/
is explained here: https://lark-parser.readthedocs.io/en/latest/_static/sppf/sppf.html
""" """


from collections import deque from collections import deque
@@ -147,7 +146,7 @@ class Parser:
column.add(new_item) column.add(new_item)
items.append(new_item) items.append(new_item)


def _parse(self, stream, columns, to_scan, start_symbol=None):
def _parse(self, lexer, columns, to_scan, start_symbol=None):
def is_quasi_complete(item): def is_quasi_complete(item):
if item.is_complete: if item.is_complete:
return True return True
@@ -246,7 +245,7 @@ class Parser:


if not next_set and not next_to_scan: if not next_set and not next_to_scan:
expect = {i.expect.name for i in to_scan} expect = {i.expect.name for i in to_scan}
raise UnexpectedToken(token, expect, considered_rules = set(to_scan))
raise UnexpectedToken(token, expect, considered_rules=set(to_scan), state=frozenset(i.s for i in to_scan))


return next_to_scan return next_to_scan


@@ -262,20 +261,24 @@ class Parser:
# Completions will be added to the SPPF tree, and predictions will be recursively # Completions will be added to the SPPF tree, and predictions will be recursively
# processed down to terminals/empty nodes to be added to the scanner for the next # processed down to terminals/empty nodes to be added to the scanner for the next
# step. # step.
expects = {i.expect for i in to_scan}
i = 0 i = 0
for token in stream:
for token in lexer.lex(expects):
self.predict_and_complete(i, to_scan, columns, transitives) self.predict_and_complete(i, to_scan, columns, transitives)


to_scan = scan(i, token, to_scan) to_scan = scan(i, token, to_scan)
i += 1 i += 1


expects.clear()
expects |= {i.expect for i in to_scan}

self.predict_and_complete(i, to_scan, columns, transitives) self.predict_and_complete(i, to_scan, columns, transitives)


## Column is now the final column in the parse. ## Column is now the final column in the parse.
assert i == len(columns)-1 assert i == len(columns)-1
return to_scan return to_scan


def parse(self, stream, start):
def parse(self, lexer, start):
assert start, start assert start, start
start_symbol = NonTerminal(start) start_symbol = NonTerminal(start)


@@ -292,12 +295,16 @@ class Parser:
else: else:
columns[0].add(item) columns[0].add(item)


to_scan = self._parse(stream, columns, to_scan, start_symbol)
to_scan = self._parse(lexer, columns, to_scan, start_symbol)


# If the parse was successful, the start # If the parse was successful, the start
# symbol should have been completed in the last step of the Earley cycle, and will be in # symbol should have been completed in the last step of the Earley cycle, and will be in
# this column. Find the item for the start_symbol, which is the root of the SPPF tree. # this column. Find the item for the start_symbol, which is the root of the SPPF tree.
solutions = [n.node for n in columns[-1] if n.is_complete and n.node is not None and n.s == start_symbol and n.start == 0] solutions = [n.node for n in columns[-1] if n.is_complete and n.node is not None and n.s == start_symbol and n.start == 0]
if not solutions:
expected_terminals = [t.expect for t in to_scan]
raise UnexpectedEOF(expected_terminals, state=frozenset(i.s for i in to_scan))

if self.debug: if self.debug:
from .earley_forest import ForestToPyDotVisitor from .earley_forest import ForestToPyDotVisitor
try: try:
@@ -308,10 +315,7 @@ class Parser:
debug_walker.visit(solutions[0], "sppf.png") debug_walker.visit(solutions[0], "sppf.png")




if not solutions:
expected_tokens = [t.expect for t in to_scan]
raise UnexpectedEOF(expected_tokens)
elif len(solutions) > 1:
if len(solutions) > 1:
assert False, 'Earley should not generate multiple start symbol items!' assert False, 'Earley should not generate multiple start symbol items!'


if self.tree_class is not None: if self.tree_class is not None:


+ 41
- 20
lark/parsers/earley_forest.py View File

@@ -459,15 +459,20 @@ class PackedData():
that comes from the left child and the right child. that comes from the left child and the right child.
""" """


class _NoData():
pass

NO_DATA = _NoData()

def __init__(self, node, data): def __init__(self, node, data):
self.left = None
self.right = None
self.left = self.NO_DATA
self.right = self.NO_DATA
if data: if data:
if node.left:
if node.left is not None:
self.left = data[0] self.left = data[0]
if len(data) > 1 and node.right:
if len(data) > 1:
self.right = data[1] self.right = data[1]
elif node.right:
else:
self.right = data[0] self.right = data[0]


class ForestToParseTree(ForestTransformer): class ForestToParseTree(ForestTransformer):
@@ -490,19 +495,22 @@ class ForestToParseTree(ForestTransformer):
self.prioritizer = prioritizer self.prioritizer = prioritizer
self.resolve_ambiguity = resolve_ambiguity self.resolve_ambiguity = resolve_ambiguity
self._on_cycle_retreat = False self._on_cycle_retreat = False
self._cycle_node = None
self._successful_visits = set()


def on_cycle(self, node, path): def on_cycle(self, node, path):
logger.warning("Cycle encountered in the SPPF at node: %s. "
logger.debug("Cycle encountered in the SPPF at node: %s. "
"As infinite ambiguities cannot be represented in a tree, " "As infinite ambiguities cannot be represented in a tree, "
"this family of derivations will be discarded.", node) "this family of derivations will be discarded.", node)
if self.resolve_ambiguity:
# TODO: choose a different path if cycle is encountered
logger.warning("At this time, using ambiguity resolution for SPPFs "
"with cycles may result in None being returned.")
self._cycle_node = node
self._on_cycle_retreat = True self._on_cycle_retreat = True


def _check_cycle(self, node): def _check_cycle(self, node):
if self._on_cycle_retreat: if self._on_cycle_retreat:
if id(node) == id(self._cycle_node):
self._cycle_node = None
self._on_cycle_retreat = False
return
raise Discard() raise Discard()


def _collapse_ambig(self, children): def _collapse_ambig(self, children):
@@ -531,11 +539,17 @@ class ForestToParseTree(ForestTransformer):
raise Discard() raise Discard()


def transform_symbol_node(self, node, data): def transform_symbol_node(self, node, data):
if id(node) not in self._successful_visits:
raise Discard()
self._successful_visits.remove(id(node))
self._check_cycle(node) self._check_cycle(node)
data = self._collapse_ambig(data) data = self._collapse_ambig(data)
return self._call_ambig_func(node, data) return self._call_ambig_func(node, data)


def transform_intermediate_node(self, node, data): def transform_intermediate_node(self, node, data):
if id(node) not in self._successful_visits:
raise Discard()
self._successful_visits.remove(id(node))
self._check_cycle(node) self._check_cycle(node)
if len(data) > 1: if len(data) > 1:
children = [self.tree_class('_inter', c) for c in data] children = [self.tree_class('_inter', c) for c in data]
@@ -544,36 +558,40 @@ class ForestToParseTree(ForestTransformer):


def transform_packed_node(self, node, data): def transform_packed_node(self, node, data):
self._check_cycle(node) self._check_cycle(node)
if self.resolve_ambiguity and id(node.parent) in self._successful_visits:
raise Discard()
children = [] children = []
assert len(data) <= 2 assert len(data) <= 2
data = PackedData(node, data) data = PackedData(node, data)
if data.left is not None:
if data.left is not PackedData.NO_DATA:
if node.left.is_intermediate and isinstance(data.left, list): if node.left.is_intermediate and isinstance(data.left, list):
children += data.left children += data.left
else: else:
children.append(data.left) children.append(data.left)
if data.right is not None:
if data.right is not PackedData.NO_DATA:
children.append(data.right) children.append(data.right)
if node.parent.is_intermediate: if node.parent.is_intermediate:
return children return children
return self._call_rule_func(node, children) return self._call_rule_func(node, children)


def visit_symbol_node_in(self, node): def visit_symbol_node_in(self, node):
self._on_cycle_retreat = False
super(ForestToParseTree, self).visit_symbol_node_in(node) super(ForestToParseTree, self).visit_symbol_node_in(node)
if self._on_cycle_retreat:
return
if self.prioritizer and node.is_ambiguous and isinf(node.priority): if self.prioritizer and node.is_ambiguous and isinf(node.priority):
self.prioritizer.visit(node) self.prioritizer.visit(node)
if self.resolve_ambiguity:
return node.children[0]
return node.children return node.children


def visit_packed_node_in(self, node): def visit_packed_node_in(self, node):
self._on_cycle_retreat = False self._on_cycle_retreat = False
return super(ForestToParseTree, self).visit_packed_node_in(node)
to_visit = super(ForestToParseTree, self).visit_packed_node_in(node)
if not self.resolve_ambiguity or id(node.parent) not in self._successful_visits:
return to_visit


def visit_token_node(self, node):
self._on_cycle_retreat = False
return super(ForestToParseTree, self).visit_token_node(node)
def visit_packed_node_out(self, node):
super(ForestToParseTree, self).visit_packed_node_out(node)
if not self._on_cycle_retreat:
self._successful_visits.add(id(node.parent))


def handles_ambiguity(func): def handles_ambiguity(func):
"""Decorator for methods of subclasses of ``TreeForestTransformer``. """Decorator for methods of subclasses of ``TreeForestTransformer``.
@@ -679,7 +697,10 @@ class ForestToPyDotVisitor(ForestVisitor):


def visit(self, root, filename): def visit(self, root, filename):
super(ForestToPyDotVisitor, self).visit(root) super(ForestToPyDotVisitor, self).visit(root)
self.graph.write_png(filename)
try:
self.graph.write_png(filename)
except FileNotFoundError as e:
logger.error("Could not write png: ", e)


def visit_token_node(self, node): def visit_token_node(self, node):
graph_node_id = str(id(node)) graph_node_id = str(id(node))


+ 11
- 4
lark/parsers/lalr_parser.py View File

@@ -3,15 +3,16 @@
# Author: Erez Shinan (2017) # Author: Erez Shinan (2017)
# Email : erezshin@gmail.com # Email : erezshin@gmail.com
from copy import deepcopy, copy from copy import deepcopy, copy
from ..exceptions import UnexpectedCharacters, UnexpectedInput, UnexpectedToken
from ..exceptions import UnexpectedInput, UnexpectedToken
from ..lexer import Token from ..lexer import Token
from ..utils import Serialize


from .lalr_analysis import LALR_Analyzer, Shift, Reduce, IntParseTable from .lalr_analysis import LALR_Analyzer, Shift, Reduce, IntParseTable
from .lalr_puppet import ParserPuppet from .lalr_puppet import ParserPuppet


###{standalone ###{standalone


class LALR_Parser(object):
class LALR_Parser(Serialize):
def __init__(self, parser_conf, debug=False): def __init__(self, parser_conf, debug=False):
analysis = LALR_Analyzer(parser_conf, debug=debug) analysis = LALR_Analyzer(parser_conf, debug=debug)
analysis.compute_lalr() analysis.compute_lalr()
@@ -62,6 +63,12 @@ class ParserState(object):
def position(self): def position(self):
return self.state_stack[-1] return self.state_stack[-1]


# Necessary for match_examples() to work
def __eq__(self, other):
if not isinstance(other, ParserState):
return False
return self.position == other.position

def __copy__(self): def __copy__(self):
return type(self)( return type(self)(
self.parse_conf, self.parse_conf,
@@ -86,7 +93,7 @@ class ParserState(object):
action, arg = states[state][token.type] action, arg = states[state][token.type]
except KeyError: except KeyError:
expected = {s for s in states[state].keys() if s.isupper()} expected = {s for s in states[state].keys() if s.isupper()}
raise UnexpectedToken(token, expected, state=state, puppet=None)
raise UnexpectedToken(token, expected, state=self, puppet=None)


assert arg != end_state assert arg != end_state


@@ -95,7 +102,7 @@ class ParserState(object):
assert not is_end assert not is_end
state_stack.append(arg) state_stack.append(arg)
value_stack.append(token) value_stack.append(token)
return arg
return
else: else:
# reduce+shift as many times as necessary # reduce+shift as many times as necessary
rule = arg rule = arg


+ 20
- 4
lark/parsers/lalr_puppet.py View File

@@ -22,7 +22,7 @@ class ParserPuppet(object):


Note that ``token`` has to be an instance of ``Token``. Note that ``token`` has to be an instance of ``Token``.
""" """
return self.parser_state.feed_token(token)
return self.parser_state.feed_token(token, token.type == '$END')


def __copy__(self): def __copy__(self):
"""Create a new puppet with a separate state. """Create a new puppet with a separate state.
@@ -35,15 +35,18 @@ class ParserPuppet(object):
copy(self.lexer_state), copy(self.lexer_state),
) )


def copy(self):
return copy(self)

def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, ParserPuppet): if not isinstance(other, ParserPuppet):
return False return False


return self.parser_state == other.parser_state and self.lexer_state == other.lexer_state return self.parser_state == other.parser_state and self.lexer_state == other.lexer_state


# TODO Provide with an immutable puppet instance
# def __hash__(self):
# return hash((self.parser_state, self.lexer_state))
def as_immutable(self):
p = copy(self)
return ImmutableParserPuppet(p.parser, p.parser_state, p.lexer_state)


def pretty(self): def pretty(self):
"""Print the output of ``choices()`` in a way that's easier to read.""" """Print the output of ``choices()`` in a way that's easier to read."""
@@ -78,3 +81,16 @@ class ParserPuppet(object):
def resume_parse(self): def resume_parse(self):
"""Resume parsing from the current puppet state.""" """Resume parsing from the current puppet state."""
return self.parser.parse_from_state(self.parser_state) return self.parser.parse_from_state(self.parser_state)



class ImmutableParserPuppet(ParserPuppet):
result = None

def __hash__(self):
return hash((self.parser_state, self.lexer_state))

def feed_token(self, token):
c = copy(self)
c.result = ParserPuppet.feed_token(c, token)
return c

+ 6
- 4
lark/parsers/xearley.py View File

@@ -63,9 +63,10 @@ class Parser(BaseParser):
t = Token(item.expect.name, m.group(0), i, text_line, text_column) t = Token(item.expect.name, m.group(0), i, text_line, text_column)
delayed_matches[i+m.end()].append( (item, i, t) ) delayed_matches[i+m.end()].append( (item, i, t) )


# Remove any items that successfully matched in this pass from the to_scan buffer.
# This ensures we don't carry over tokens that already matched, if we're ignoring below.
to_scan.remove(item)
# XXX The following 3 lines were commented out for causing a bug. See issue #768
# # Remove any items that successfully matched in this pass from the to_scan buffer.
# # This ensures we don't carry over tokens that already matched, if we're ignoring below.
# to_scan.remove(item)


# 3) Process any ignores. This is typically used for e.g. whitespace. # 3) Process any ignores. This is typically used for e.g. whitespace.
# We carry over any unmatched items from the to_scan buffer to be matched again after # We carry over any unmatched items from the to_scan buffer to be matched again after
@@ -113,7 +114,8 @@ class Parser(BaseParser):
del delayed_matches[i+1] # No longer needed, so unburden memory del delayed_matches[i+1] # No longer needed, so unburden memory


if not next_set and not delayed_matches and not next_to_scan: if not next_set and not delayed_matches and not next_to_scan:
raise UnexpectedCharacters(stream, i, text_line, text_column, {item.expect.name for item in to_scan}, set(to_scan))
raise UnexpectedCharacters(stream, i, text_line, text_column, {item.expect.name for item in to_scan},
set(to_scan), state=frozenset(i.s for i in to_scan))


return next_to_scan return next_to_scan




+ 4
- 2
lark/tools/nearley.py View File

@@ -35,7 +35,9 @@ nearley_grammar = r"""
COMMENT: /#[^\n]*/ COMMENT: /#[^\n]*/
REGEXP: /\[.*?\]/ REGEXP: /\[.*?\]/


%import common.ESCAPED_STRING -> STRING
STRING: _STRING "i"?

%import common.ESCAPED_STRING -> _STRING
%import common.WS %import common.WS
%ignore WS %ignore WS
%ignore COMMENT %ignore COMMENT
@@ -183,7 +185,7 @@ def main(fn, start, nearley_lib, es6=False):
return create_code_for_nearley_grammar(grammar, start, os.path.join(nearley_lib, 'builtin'), os.path.abspath(os.path.dirname(fn)), es6=es6) return create_code_for_nearley_grammar(grammar, start, os.path.join(nearley_lib, 'builtin'), os.path.abspath(os.path.dirname(fn)), es6=es6)


def get_arg_parser(): def get_arg_parser():
parser = argparse.ArgumentParser('Reads Nearley grammar (with js functions) outputs an equivalent lark parser.')
parser = argparse.ArgumentParser(description='Reads a Nearley grammar (with js functions), and outputs an equivalent lark parser.')
parser.add_argument('nearley_grammar', help='Path to the file containing the nearley grammar') parser.add_argument('nearley_grammar', help='Path to the file containing the nearley grammar')
parser.add_argument('start_rule', help='Rule within the nearley grammar to make the base rule') parser.add_argument('start_rule', help='Rule within the nearley grammar to make the base rule')
parser.add_argument('nearley_lib', help='Path to root directory of nearley codebase (used for including builtins)') parser.add_argument('nearley_lib', help='Path to root directory of nearley codebase (used for including builtins)')


+ 10
- 6
lark/tree.py View File

@@ -46,14 +46,14 @@ class Tree(object):


def _pretty(self, level, indent_str): def _pretty(self, level, indent_str):
if len(self.children) == 1 and not isinstance(self.children[0], Tree): if len(self.children) == 1 and not isinstance(self.children[0], Tree):
return [ indent_str*level, self._pretty_label(), '\t', '%s' % (self.children[0],), '\n']
return [indent_str*level, self._pretty_label(), '\t', '%s' % (self.children[0],), '\n']


l = [ indent_str*level, self._pretty_label(), '\n' ]
l = [indent_str*level, self._pretty_label(), '\n']
for n in self.children: for n in self.children:
if isinstance(n, Tree): if isinstance(n, Tree):
l += n._pretty(level+1, indent_str) l += n._pretty(level+1, indent_str)
else: else:
l += [ indent_str*(level+1), '%s' % (n,), '\n' ]
l += [indent_str*(level+1), '%s' % (n,), '\n']


return l return l


@@ -102,8 +102,8 @@ class Tree(object):
###} ###}


def expand_kids_by_index(self, *indices): def expand_kids_by_index(self, *indices):
"Expand (inline) children at the given indices"
for i in sorted(indices, reverse=True): # reverse so that changing tail won't affect indices
"""Expand (inline) children at the given indices"""
for i in sorted(indices, reverse=True): # reverse so that changing tail won't affect indices
kid = self.children[i] kid = self.children[i]
self.children[i:i+1] = kid.children self.children[i:i+1] = kid.children


@@ -144,12 +144,15 @@ class Tree(object):
@property @property
def line(self): def line(self):
return self.meta.line return self.meta.line

@property @property
def column(self): def column(self):
return self.meta.column return self.meta.column

@property @property
def end_line(self): def end_line(self):
return self.meta.end_line return self.meta.end_line

@property @property
def end_column(self): def end_column(self):
return self.meta.end_column return self.meta.end_column
@@ -168,6 +171,7 @@ def pydot__tree_to_dot(tree, filename, rankdir="LR", **kwargs):
graph = pydot__tree_to_graph(tree, rankdir, **kwargs) graph = pydot__tree_to_graph(tree, rankdir, **kwargs)
graph.write(filename) graph.write(filename)



def pydot__tree_to_graph(tree, rankdir="LR", **kwargs): def pydot__tree_to_graph(tree, rankdir="LR", **kwargs):
"""Creates a colorful image that represents the tree (data+children, without meta) """Creates a colorful image that represents the tree (data+children, without meta)


@@ -196,7 +200,7 @@ def pydot__tree_to_graph(tree, rankdir="LR", **kwargs):


subnodes = [_to_pydot(child) if isinstance(child, Tree) else new_leaf(child) subnodes = [_to_pydot(child) if isinstance(child, Tree) else new_leaf(child)
for child in subtree.children] for child in subtree.children]
node = pydot.Node(i[0], style="filled", fillcolor="#%x"%color, label=subtree.data)
node = pydot.Node(i[0], style="filled", fillcolor="#%x" % color, label=subtree.data)
i[0] += 1 i[0] += 1
graph.add_node(node) graph.add_node(node)




+ 9
- 1
lark/tree_matcher.py View File

@@ -69,6 +69,14 @@ def parse_rulename(s):
return name, args return name, args





class ChildrenLexer:
def __init__(self, children):
self.children = children

def lex(self, parser_state):
return self.children

class TreeMatcher: class TreeMatcher:
"""Match the elements of a tree node, based on an ontology """Match the elements of a tree node, based on an ontology
provided by a Lark grammar. provided by a Lark grammar.
@@ -173,6 +181,6 @@ class TreeMatcher:
self._parser_cache[rulename] = parser self._parser_cache[rulename] = parser


# find a full derivation # find a full derivation
unreduced_tree = parser.parse(tree.children, rulename)
unreduced_tree = parser.parse(ChildrenLexer(tree.children), rulename)
assert unreduced_tree.data == rulename assert unreduced_tree.data == rulename
return unreduced_tree return unreduced_tree

+ 10
- 39
lark/utils.py View File

@@ -1,10 +1,9 @@
import sys
import os import os
from functools import reduce from functools import reduce
from ast import literal_eval
from collections import deque from collections import deque


###{standalone ###{standalone
import sys, re
import logging import logging
logger = logging.getLogger("lark") logger = logging.getLogger("lark")
logger.addHandler(logging.StreamHandler()) logger.addHandler(logging.StreamHandler())
@@ -12,6 +11,8 @@ logger.addHandler(logging.StreamHandler())
# By default, we should not output any log messages # By default, we should not output any log messages
logger.setLevel(logging.CRITICAL) logger.setLevel(logging.CRITICAL)


Py36 = (sys.version_info[:2] >= (3, 6))



def classify(seq, key=None, value=None): def classify(seq, key=None, value=None):
d = {} d = {}
@@ -27,7 +28,7 @@ def classify(seq, key=None, value=None):


def _deserialize(data, namespace, memo): def _deserialize(data, namespace, memo):
if isinstance(data, dict): if isinstance(data, dict):
if '__type__' in data: # Object
if '__type__' in data: # Object
class_ = namespace[data['__type__']] class_ = namespace[data['__type__']]
return class_.deserialize(data, memo) return class_.deserialize(data, memo)
elif '@' in data: elif '@' in data:
@@ -105,7 +106,6 @@ class SerializeMemoizer(Serialize):
return _deserialize(data, namespace, memo) return _deserialize(data, namespace, memo)





try: try:
STRING_TYPE = basestring STRING_TYPE = basestring
except NameError: # Python 3 except NameError: # Python 3
@@ -118,10 +118,11 @@ from contextlib import contextmanager


Str = type(u'') Str = type(u'')
try: try:
classtype = types.ClassType # Python2
classtype = types.ClassType # Python2
except AttributeError: except AttributeError:
classtype = type # Python3 classtype = type # Python3



def smart_decorator(f, create_decorator): def smart_decorator(f, create_decorator):
if isinstance(f, types.FunctionType): if isinstance(f, types.FunctionType):
return wraps(f)(create_decorator(f, True)) return wraps(f)(create_decorator(f, True))
@@ -139,17 +140,16 @@ def smart_decorator(f, create_decorator):
else: else:
return create_decorator(f.__func__.__call__, True) return create_decorator(f.__func__.__call__, True)



try: try:
import regex import regex
except ImportError: except ImportError:
regex = None regex = None


import sys, re
Py36 = (sys.version_info[:2] >= (3, 6))

import sre_parse import sre_parse
import sre_constants import sre_constants
categ_pattern = re.compile(r'\\p{[A-Za-z_]+}') categ_pattern = re.compile(r'\\p{[A-Za-z_]+}')

def get_regexp_width(expr): def get_regexp_width(expr):
if regex: if regex:
# Since `sre_parse` cannot deal with Unicode categories of the form `\p{Mn}`, we replace these with # Since `sre_parse` cannot deal with Unicode categories of the form `\p{Mn}`, we replace these with
@@ -173,9 +173,7 @@ def dedup_list(l):
preserving the original order of the list. Assumes that preserving the original order of the list. Assumes that
the list entries are hashable.""" the list entries are hashable."""
dedup = set() dedup = set()
return [ x for x in l if not (x in dedup or dedup.add(x))]


return [x for x in l if not (x in dedup or dedup.add(x))]




try: try:
@@ -197,8 +195,6 @@ except ImportError:
pass pass






try: try:
compare = cmp compare = cmp
except NameError: except NameError:
@@ -210,7 +206,6 @@ except NameError:
return -1 return -1





class Enumerator(Serialize): class Enumerator(Serialize):
def __init__(self): def __init__(self):
self.enums = {} self.enums = {}
@@ -229,31 +224,6 @@ class Enumerator(Serialize):
return r return r




def eval_escaping(s):
w = ''
i = iter(s)
for n in i:
w += n
if n == '\\':
try:
n2 = next(i)
except StopIteration:
raise ValueError("Literal ended unexpectedly (bad escaping): `%r`" % s)
if n2 == '\\':
w += '\\\\'
elif n2 not in 'uxnftr':
w += '\\'
w += n2
w = w.replace('\\"', '"').replace("'", "\\'")

to_eval = "u'''%s'''" % w
try:
s = literal_eval(to_eval)
except SyntaxError as e:
raise ValueError(s, e)

return s



def combine_alternatives(lists): def combine_alternatives(lists):
""" """
@@ -332,4 +302,5 @@ def _serialize(value, memo):
return list(value) # TODO reversible? return list(value) # TODO reversible?
elif isinstance(value, dict): elif isinstance(value, dict):
return {key:_serialize(elem, memo) for key, elem in value.items()} return {key:_serialize(elem, memo) for key, elem in value.items()}
# assert value is None or isinstance(value, (int, float, str, tuple)), value
return value return value

+ 52
- 38
lark/visitors.py View File

@@ -8,6 +8,7 @@ from .lexer import Token
###{standalone ###{standalone
from inspect import getmembers, getmro from inspect import getmembers, getmro



class Discard(Exception): class Discard(Exception):
"""When raising the Discard exception in a transformer callback, """When raising the Discard exception in a transformer callback,
that node is discarded and won't appear in the parent. that node is discarded and won't appear in the parent.
@@ -16,6 +17,7 @@ class Discard(Exception):


# Transformers # Transformers



class _Decoratable: class _Decoratable:
"Provides support for decorating methods with @v_args" "Provides support for decorating methods with @v_args"


@@ -47,7 +49,7 @@ class _Decoratable:
class Transformer(_Decoratable): class Transformer(_Decoratable):
"""Transformers visit each node of the tree, and run the appropriate method on it according to the node's data. """Transformers visit each node of the tree, and run the appropriate method on it according to the node's data.


Calls its methods (provided by user via inheritance) according to ``tree.data``.
Calls its methods (provided by the user via inheritance) according to ``tree.data``.
The returned value replaces the old one in the structure. The returned value replaces the old one in the structure.


They work bottom-up (or depth-first), starting with the leaves and ending at the root of the tree. They work bottom-up (or depth-first), starting with the leaves and ending at the root of the tree.
@@ -64,12 +66,11 @@ class Transformer(_Decoratable):
- ``Transformer_InPlaceRecursive`` - Recursive. Changes the tree in-place instead of returning new instances - ``Transformer_InPlaceRecursive`` - Recursive. Changes the tree in-place instead of returning new instances


Parameters: Parameters:
visit_tokens: By default, transformers only visit rules.
visit_tokens=True will tell ``Transformer`` to visit tokens
as well. This is a slightly slower alternative to lexer_callbacks
but it's easier to maintain and works for all algorithms
(even when there isn't a lexer).
visit_tokens (bool, optional): Should the transformer visit tokens in addition to rules.
Setting this to ``False`` is slightly faster. Defaults to ``True``.
(For processing ignored tokens, use the ``lexer_callbacks`` options)


NOTE: A transformer without methods essentially performs a non-memoized deepcopy.
""" """
__visit_tokens__ = True # For backwards compatibility __visit_tokens__ = True # For backwards compatibility


@@ -108,7 +109,6 @@ class Transformer(_Decoratable):
except Exception as e: except Exception as e:
raise VisitError(token.type, token, e) raise VisitError(token.type, token, e)



def _transform_children(self, children): def _transform_children(self, children):
for c in children: for c in children:
try: try:
@@ -126,29 +126,29 @@ class Transformer(_Decoratable):
return self._call_userfunc(tree, children) return self._call_userfunc(tree, children)


def transform(self, tree): def transform(self, tree):
"Transform the given tree, and return the final result"
return self._transform_tree(tree) return self._transform_tree(tree)


def __mul__(self, other): def __mul__(self, other):
"""Chain two transformers together, returning a new transformer.
"""
return TransformerChain(self, other) return TransformerChain(self, other)


def __default__(self, data, children, meta): def __default__(self, data, children, meta):
"""Default operation on tree (for override)
"""Default function that is called if there is no attribute matching ``data``


Function that is called on if a function with a corresponding name has not been found.
Defaults to reconstruct the Tree.
Can be overridden. Defaults to creating a new copy of the tree node (i.e. ``return Tree(data, children, meta)``)
""" """
return Tree(data, children, meta) return Tree(data, children, meta)


def __default_token__(self, token): def __default_token__(self, token):
"""Default operation on token (for override)
"""Default function that is called if there is no attribute matching ``token.type``


Function that is called on if a function with a corresponding name has not been found.
Defaults to just return the argument.
Can be overridden. Defaults to returning the token as-is.
""" """
return token return token





class InlineTransformer(Transformer): # XXX Deprecated class InlineTransformer(Transformer): # XXX Deprecated
def _call_userfunc(self, tree, new_children=None): def _call_userfunc(self, tree, new_children=None):
# Assumes tree is already transformed # Assumes tree is already transformed
@@ -175,7 +175,10 @@ class TransformerChain(object):




class Transformer_InPlace(Transformer): class Transformer_InPlace(Transformer):
"Non-recursive. Changes the tree in-place instead of returning new instances"
"""Same as Transformer, but non-recursive, and changes the tree in-place instead of returning new instances

Useful for huge trees. Conservative in memory.
"""
def _transform_tree(self, tree): # Cancel recursion def _transform_tree(self, tree): # Cancel recursion
return self._call_userfunc(tree) return self._call_userfunc(tree)


@@ -187,7 +190,12 @@ class Transformer_InPlace(Transformer):




class Transformer_NonRecursive(Transformer): class Transformer_NonRecursive(Transformer):
"Non-recursive. Doesn't change the original tree."
"""Same as Transformer but non-recursive.

Like Transformer, it doesn't change the original tree.

Useful for huge trees.
"""


def transform(self, tree): def transform(self, tree):
# Tree to postfix # Tree to postfix
@@ -195,7 +203,7 @@ class Transformer_NonRecursive(Transformer):
q = [tree] q = [tree]
while q: while q:
t = q.pop() t = q.pop()
rev_postfix.append( t )
rev_postfix.append(t)
if isinstance(t, Tree): if isinstance(t, Tree):
q += t.children q += t.children


@@ -217,15 +225,13 @@ class Transformer_NonRecursive(Transformer):
return t return t





class Transformer_InPlaceRecursive(Transformer): class Transformer_InPlaceRecursive(Transformer):
"Recursive. Changes the tree in-place instead of returning new instances"
"Same as Transformer, recursive, but changes the tree in-place instead of returning new instances"
def _transform_tree(self, tree): def _transform_tree(self, tree):
tree.children = list(self._transform_children(tree.children)) tree.children = list(self._transform_children(tree.children))
return self._call_userfunc(tree) return self._call_userfunc(tree)





# Visitors # Visitors


class VisitorBase: class VisitorBase:
@@ -233,7 +239,10 @@ class VisitorBase:
return getattr(self, tree.data, self.__default__)(tree) return getattr(self, tree.data, self.__default__)(tree)


def __default__(self, tree): def __default__(self, tree):
"Default operation on tree (for override)"
"""Default function that is called if there is no attribute matching ``tree.data``

Can be overridden. Defaults to doing nothing.
"""
return tree return tree


def __class_getitem__(cls, _): def __class_getitem__(cls, _):
@@ -241,18 +250,19 @@ class VisitorBase:




class Visitor(VisitorBase): class Visitor(VisitorBase):
"""Bottom-up visitor, non-recursive.
"""Tree visitor, non-recursive (can handle huge trees).


Visits the tree, starting with the leaves and finally the root (bottom-up)
Calls its methods (provided by user via inheritance) according to ``tree.data``
Visiting a node calls its methods (provided by the user via inheritance) according to ``tree.data``
""" """


def visit(self, tree): def visit(self, tree):
"Visits the tree, starting with the leaves and finally the root (bottom-up)"
for subtree in tree.iter_subtrees(): for subtree in tree.iter_subtrees():
self._call_userfunc(subtree) self._call_userfunc(subtree)
return tree return tree


def visit_topdown(self,tree): def visit_topdown(self,tree):
"Visit the tree, starting at the root, and ending at the leaves (top-down)"
for subtree in tree.iter_subtrees_topdown(): for subtree in tree.iter_subtrees_topdown():
self._call_userfunc(subtree) self._call_userfunc(subtree)
return tree return tree
@@ -261,11 +271,13 @@ class Visitor(VisitorBase):
class Visitor_Recursive(VisitorBase): class Visitor_Recursive(VisitorBase):
"""Bottom-up visitor, recursive. """Bottom-up visitor, recursive.


Visits the tree, starting with the leaves and finally the root (bottom-up)
Calls its methods (provided by user via inheritance) according to ``tree.data``
Visiting a node calls its methods (provided by the user via inheritance) according to ``tree.data``

Slightly faster than the non-recursive version.
""" """


def visit(self, tree): def visit(self, tree):
"Visits the tree, starting with the leaves and finally the root (bottom-up)"
for child in tree.children: for child in tree.children:
if isinstance(child, Tree): if isinstance(child, Tree):
self.visit(child) self.visit(child)
@@ -274,6 +286,7 @@ class Visitor_Recursive(VisitorBase):
return tree return tree


def visit_topdown(self,tree): def visit_topdown(self,tree):
"Visit the tree, starting at the root, and ending at the leaves (top-down)"
self._call_userfunc(tree) self._call_userfunc(tree)


for child in tree.children: for child in tree.children:
@@ -283,7 +296,6 @@ class Visitor_Recursive(VisitorBase):
return tree return tree





def visit_children_decor(func): def visit_children_decor(func):
"See Interpreter" "See Interpreter"
@wraps(func) @wraps(func)
@@ -324,8 +336,6 @@ class Interpreter(_Decoratable):
return self.visit_children(tree) return self.visit_children(tree)






# Decorators # Decorators


def _apply_decorator(obj, decorator, **kwargs): def _apply_decorator(obj, decorator, **kwargs):
@@ -337,7 +347,6 @@ def _apply_decorator(obj, decorator, **kwargs):
return _apply(decorator, **kwargs) return _apply(decorator, **kwargs)





def _inline_args__func(func): def _inline_args__func(func):
@wraps(func) @wraps(func)
def create_decorator(_f, with_self): def create_decorator(_f, with_self):
@@ -356,7 +365,6 @@ def inline_args(obj): # XXX Deprecated
return _apply_decorator(obj, _inline_args__func) return _apply_decorator(obj, _inline_args__func)





def _visitor_args_func_dec(func, visit_wrapper=None, static=False): def _visitor_args_func_dec(func, visit_wrapper=None, static=False):
def create_decorator(_f, with_self): def create_decorator(_f, with_self):
if with_self: if with_self:
@@ -376,11 +384,11 @@ def _visitor_args_func_dec(func, visit_wrapper=None, static=False):
return f return f




def _vargs_inline(f, data, children, meta):
def _vargs_inline(f, _data, children, _meta):
return f(*children) return f(*children)
def _vargs_meta_inline(f, data, children, meta):
def _vargs_meta_inline(f, _data, children, meta):
return f(meta, *children) return f(meta, *children)
def _vargs_meta(f, data, children, meta):
def _vargs_meta(f, _data, children, meta):
return f(children, meta) # TODO swap these for consistency? Backwards incompatible! return f(children, meta) # TODO swap these for consistency? Backwards incompatible!
def _vargs_tree(f, data, children, meta): def _vargs_tree(f, data, children, meta):
return f(Tree(data, children, meta)) return f(Tree(data, children, meta))
@@ -394,10 +402,14 @@ def v_args(inline=False, meta=False, tree=False, wrapper=None):
``v_args`` can modify this behavior. When used on a transformer/visitor class definition, ``v_args`` can modify this behavior. When used on a transformer/visitor class definition,
it applies to all the callback methods inside it. it applies to all the callback methods inside it.


``v_args`` can be applied to a single method, or to an entire class. When applied to both,
the options given to the method take precedence.

Parameters: Parameters:
inline: Children are provided as ``*args`` instead of a list argument (not recommended for very long lists).
meta: Provides two arguments: ``children`` and ``meta`` (instead of just the first)
tree: Provides the entire tree as the argument, instead of the children.
inline (bool, optional): Children are provided as ``*args`` instead of a list argument (not recommended for very long lists).
meta (bool, optional): Provides two arguments: ``children`` and ``meta`` (instead of just the first)
tree (bool, optional): Provides the entire tree as the argument, instead of the children.
wrapper (function, optional): Provide a function to decorate all methods.


Example: Example:
:: ::
@@ -440,7 +452,7 @@ def v_args(inline=False, meta=False, tree=False, wrapper=None):
###} ###}




#--- Visitor Utilities ---
# --- Visitor Utilities ---


class CollapseAmbiguities(Transformer): class CollapseAmbiguities(Transformer):
""" """
@@ -454,7 +466,9 @@ class CollapseAmbiguities(Transformer):
""" """
def _ambig(self, options): def _ambig(self, options):
return sum(options, []) return sum(options, [])

def __default__(self, data, children_lists, meta): def __default__(self, data, children_lists, meta):
return [Tree(data, children, meta) for children in combine_alternatives(children_lists)] return [Tree(data, children, meta) for children in combine_alternatives(children_lists)]

def __default_token__(self, t): def __default_token__(self, t):
return [t] return [t]

+ 2
- 2
setup.py View File

@@ -29,8 +29,8 @@ setup(
description = "a modern parsing library", description = "a modern parsing library",
license = "MIT", license = "MIT",
keywords = "Earley LALR parser parsing ast", keywords = "Earley LALR parser parsing ast",
url = "https://github.com/erezsh/lark",
download_url = "https://github.com/erezsh/lark/tarball/master",
url = "https://github.com/lark-parser/lark",
download_url = "https://github.com/lark-parser/lark/tarball/master",
long_description=''' long_description='''
Lark is a modern general-purpose parsing library for Python. Lark is a modern general-purpose parsing library for Python.




+ 2
- 14
tests/__main__.py View File

@@ -9,6 +9,7 @@ from .test_tools import TestStandalone
from .test_cache import TestCache from .test_cache import TestCache
from .test_grammar import TestGrammar from .test_grammar import TestGrammar
from .test_reconstructor import TestReconstructor from .test_reconstructor import TestReconstructor
from .test_tree_forest_transformer import TestTreeForestTransformer


try: try:
from .test_nearley.test_nearley import TestNearley from .test_nearley.test_nearley import TestNearley
@@ -20,20 +21,7 @@ except ImportError:


from .test_logger import Testlogger from .test_logger import Testlogger


from .test_parser import (
TestLalrStandard,
TestEarleyStandard,
TestCykStandard,
TestLalrContextual,
TestEarleyDynamic,
TestLalrCustom,

# TestFullEarleyStandard,
TestFullEarleyDynamic,
TestFullEarleyDynamic_complete,

TestParsers,
)
from .test_parser import * # We define __all__ to list which TestSuites to run


logger.setLevel(logging.INFO) logger.setLevel(logging.INFO)




+ 229
- 24
tests/test_parser.py View File

@@ -11,6 +11,7 @@ from copy import copy, deepcopy
from lark.utils import Py36, isascii from lark.utils import Py36, isascii


from lark import Token from lark import Token
from lark.load_grammar import FromPackageLoader


try: try:
from cStringIO import StringIO as cStringIO from cStringIO import StringIO as cStringIO
@@ -29,6 +30,7 @@ try:
except ImportError: except ImportError:
regex = None regex = None


import lark
from lark import logger from lark import logger
from lark.lark import Lark from lark.lark import Lark
from lark.exceptions import GrammarError, ParseError, UnexpectedToken, UnexpectedInput, UnexpectedCharacters from lark.exceptions import GrammarError, ParseError, UnexpectedToken, UnexpectedInput, UnexpectedCharacters
@@ -36,9 +38,9 @@ from lark.tree import Tree
from lark.visitors import Transformer, Transformer_InPlace, v_args from lark.visitors import Transformer, Transformer_InPlace, v_args
from lark.grammar import Rule from lark.grammar import Rule
from lark.lexer import TerminalDef, Lexer, TraditionalLexer from lark.lexer import TerminalDef, Lexer, TraditionalLexer
from lark.indenter import Indenter


logger.setLevel(logging.INFO)

__all__ = ['TestParsers']


__path__ = os.path.dirname(__file__) __path__ = os.path.dirname(__file__)
def _read(n, *args): def _read(n, *args):
@@ -322,6 +324,22 @@ class TestParsers(unittest.TestCase):
def test_alias(self): def test_alias(self):
Lark("""start: ["a"] "b" ["c"] "e" ["f"] ["g"] ["h"] "x" -> d """) Lark("""start: ["a"] "b" ["c"] "e" ["f"] ["g"] ["h"] "x" -> d """)


def test_backwards_custom_lexer(self):
class OldCustomLexer(Lexer):
def __init__(self, lexer_conf):
pass

def lex(self, text):
yield Token('A', 'A')

p = Lark("""
start: A
%declare A
""", parser='lalr', lexer=OldCustomLexer)

r = p.parse('')
self.assertEqual(r, Tree('start', [Token('A', 'A')]))





def _make_full_earley_test(LEXER): def _make_full_earley_test(LEXER):
@@ -745,6 +763,76 @@ def _make_full_earley_test(LEXER):
tree = parser.parse(text) tree = parser.parse(text)
self.assertEqual(tree.children, ['foo', 'bar']) self.assertEqual(tree.children, ['foo', 'bar'])


def test_cycle(self):
grammar = """
start: start?
"""

l = Lark(grammar, ambiguity='resolve', lexer=LEXER)
tree = l.parse('')
self.assertEqual(tree, Tree('start', []))

l = Lark(grammar, ambiguity='explicit', lexer=LEXER)
tree = l.parse('')
self.assertEqual(tree, Tree('start', []))

def test_cycles(self):
grammar = """
a: b
b: c*
c: a
"""

l = Lark(grammar, start='a', ambiguity='resolve', lexer=LEXER)
tree = l.parse('')
self.assertEqual(tree, Tree('a', [Tree('b', [])]))

l = Lark(grammar, start='a', ambiguity='explicit', lexer=LEXER)
tree = l.parse('')
self.assertEqual(tree, Tree('a', [Tree('b', [])]))

def test_many_cycles(self):
grammar = """
start: a? | start start
!a: "a"
"""

l = Lark(grammar, ambiguity='resolve', lexer=LEXER)
tree = l.parse('a')
self.assertEqual(tree, Tree('start', [Tree('a', ['a'])]))

l = Lark(grammar, ambiguity='explicit', lexer=LEXER)
tree = l.parse('a')
self.assertEqual(tree, Tree('start', [Tree('a', ['a'])]))

def test_cycles_with_child_filter(self):
grammar = """
a: _x
_x: _x? b
b:
"""

grammar2 = """
a: x
x: x? b
b:
"""

l = Lark(grammar, start='a', ambiguity='resolve', lexer=LEXER)
tree = l.parse('')
self.assertEqual(tree, Tree('a', [Tree('b', [])]))

l = Lark(grammar, start='a', ambiguity='explicit', lexer=LEXER)
tree = l.parse('');
self.assertEqual(tree, Tree('a', [Tree('b', [])]))

l = Lark(grammar2, start='a', ambiguity='resolve', lexer=LEXER)
tree = l.parse('');
self.assertEqual(tree, Tree('a', [Tree('x', [Tree('b', [])])]))

l = Lark(grammar2, start='a', ambiguity='explicit', lexer=LEXER)
tree = l.parse('');
self.assertEqual(tree, Tree('a', [Tree('x', [Tree('b', [])])]))






@@ -768,16 +856,32 @@ def _make_full_earley_test(LEXER):
_NAME = "TestFullEarley" + LEXER.capitalize() _NAME = "TestFullEarley" + LEXER.capitalize()
_TestFullEarley.__name__ = _NAME _TestFullEarley.__name__ = _NAME
globals()[_NAME] = _TestFullEarley globals()[_NAME] = _TestFullEarley
__all__.append(_NAME)


class CustomLexer(Lexer):
class CustomLexerNew(Lexer):
""" """
Purpose of this custom lexer is to test the integration, Purpose of this custom lexer is to test the integration,
so it uses the traditionalparser as implementation without custom lexing behaviour. so it uses the traditionalparser as implementation without custom lexing behaviour.
""" """
def __init__(self, lexer_conf): def __init__(self, lexer_conf):
self.lexer = TraditionalLexer(copy(lexer_conf)) self.lexer = TraditionalLexer(copy(lexer_conf))
def lex(self, *args, **kwargs):
return self.lexer.lex(*args, **kwargs)
def lex(self, lexer_state, parser_state):
return self.lexer.lex(lexer_state, parser_state)

__future_interface__ = True

class CustomLexerOld(Lexer):
"""
Purpose of this custom lexer is to test the integration,
so it uses the traditionalparser as implementation without custom lexing behaviour.
"""
def __init__(self, lexer_conf):
self.lexer = TraditionalLexer(copy(lexer_conf))
def lex(self, text):
ls = self.lexer.make_lexer_state(text)
return self.lexer.lex(ls, None)

__future_interface__ = False


def _tree_structure_check(a, b): def _tree_structure_check(a, b):
""" """
@@ -851,12 +955,31 @@ class DualBytesLark:
self.bytes_lark.load(f) self.bytes_lark.load(f)


def _make_parser_test(LEXER, PARSER): def _make_parser_test(LEXER, PARSER):
lexer_class_or_name = CustomLexer if LEXER == 'custom' else LEXER
lexer_class_or_name = {
'custom_new': CustomLexerNew,
'custom_old': CustomLexerOld,
}.get(LEXER, LEXER)

def _Lark(grammar, **kwargs): def _Lark(grammar, **kwargs):
return Lark(grammar, lexer=lexer_class_or_name, parser=PARSER, propagate_positions=True, **kwargs) return Lark(grammar, lexer=lexer_class_or_name, parser=PARSER, propagate_positions=True, **kwargs)
def _Lark_open(gfilename, **kwargs): def _Lark_open(gfilename, **kwargs):
return Lark.open(gfilename, lexer=lexer_class_or_name, parser=PARSER, propagate_positions=True, **kwargs) return Lark.open(gfilename, lexer=lexer_class_or_name, parser=PARSER, propagate_positions=True, **kwargs)


if (LEXER, PARSER) == ('standard', 'earley'):
# Check that the `lark.lark` grammar represents can parse every example used in these tests.
# Standard-Earley was an arbitrary choice, to make sure it only ran once.
lalr_parser = Lark.open(os.path.join(os.path.dirname(lark.__file__), 'grammars/lark.lark'), parser='lalr')
def wrap_with_test_grammar(f):
def _f(x, **kwargs):
inst = f(x, **kwargs)
lalr_parser.parse(inst.source_grammar) # Test after instance creation. When the grammar should fail, don't test it.
return inst
return _f

_Lark = wrap_with_test_grammar(_Lark)
_Lark_open = wrap_with_test_grammar(_Lark_open)


class _TestParser(unittest.TestCase): class _TestParser(unittest.TestCase):
def test_basic1(self): def test_basic1(self):
g = _Lark("""start: a+ b a* "b" a* g = _Lark("""start: a+ b a* "b" a*
@@ -1412,7 +1535,7 @@ def _make_parser_test(LEXER, PARSER):
%s""" % (' '.join(tokens), '\n'.join("%s: %s"%x for x in tokens.items()))) %s""" % (' '.join(tokens), '\n'.join("%s: %s"%x for x in tokens.items())))


def test_float_without_lexer(self): def test_float_without_lexer(self):
expected_error = UnexpectedCharacters if LEXER.startswith('dynamic') else UnexpectedToken
expected_error = UnexpectedCharacters if 'dynamic' in LEXER else UnexpectedToken
if PARSER == 'cyk': if PARSER == 'cyk':
expected_error = ParseError expected_error = ParseError


@@ -1545,13 +1668,13 @@ def _make_parser_test(LEXER, PARSER):
self.assertEqual(d.line, 2) self.assertEqual(d.line, 2)
self.assertEqual(d.column, 2) self.assertEqual(d.column, 2)


if LEXER != 'dynamic':
self.assertEqual(a.end_line, 1)
self.assertEqual(a.end_column, 2)
self.assertEqual(bc.end_line, 2)
self.assertEqual(bc.end_column, 2)
self.assertEqual(d.end_line, 2)
self.assertEqual(d.end_column, 3)
# if LEXER != 'dynamic':
self.assertEqual(a.end_line, 1)
self.assertEqual(a.end_column, 2)
self.assertEqual(bc.end_line, 2)
self.assertEqual(bc.end_column, 2)
self.assertEqual(d.end_line, 2)
self.assertEqual(d.end_column, 3)






@@ -1782,7 +1905,7 @@ def _make_parser_test(LEXER, PARSER):
""" """
self.assertRaises(IOError, _Lark, grammar) self.assertRaises(IOError, _Lark, grammar)


@unittest.skipIf(LEXER=='dynamic', "%declare/postlex doesn't work with dynamic")
@unittest.skipIf('dynamic' in LEXER, "%declare/postlex doesn't work with dynamic")
def test_postlex_declare(self): # Note: this test does a lot. maybe split it up? def test_postlex_declare(self): # Note: this test does a lot. maybe split it up?
class TestPostLexer: class TestPostLexer:
def process(self, stream): def process(self, stream):
@@ -1805,6 +1928,59 @@ def _make_parser_test(LEXER, PARSER):
tree = parser.parse(test_file) tree = parser.parse(test_file)
self.assertEqual(tree.children, [Token('B', 'A')]) self.assertEqual(tree.children, [Token('B', 'A')])


@unittest.skipIf('dynamic' in LEXER, "%declare/postlex doesn't work with dynamic")
def test_postlex_indenter(self):
class CustomIndenter(Indenter):
NL_type = 'NEWLINE'
OPEN_PAREN_types = []
CLOSE_PAREN_types = []
INDENT_type = 'INDENT'
DEDENT_type = 'DEDENT'
tab_len = 8

grammar = r"""
start: "a" NEWLINE INDENT "b" NEWLINE DEDENT

NEWLINE: ( /\r?\n */ )+

%ignore " "+
%declare INDENT DEDENT
"""

parser = _Lark(grammar, postlex=CustomIndenter())
parser.parse("a\n b\n")

def test_import_custom_sources(self):
custom_loader = FromPackageLoader('tests', ('grammars', ))

grammar = """
start: startab

%import ab.startab
"""

p = _Lark(grammar, import_paths=[custom_loader])
self.assertEqual(p.parse('ab'),
Tree('start', [Tree('startab', [Tree('ab__expr', [Token('ab__A', 'a'), Token('ab__B', 'b')])])]))

grammar = """
start: rule_to_import

%import test_relative_import_of_nested_grammar__grammar_to_import.rule_to_import
"""
p = _Lark(grammar, import_paths=[custom_loader])
x = p.parse('N')
self.assertEqual(next(x.find_data('rule_to_import')).children, ['N'])

custom_loader2 = FromPackageLoader('tests')
grammar = """
%import .test_relative_import (start, WS)
%ignore WS
"""
p = _Lark(grammar, import_paths=[custom_loader2], source_path=__file__) # import relative to current file
x = p.parse('12 capybaras')
self.assertEqual(x.children, ['12', 'capybaras'])

@unittest.skipIf(PARSER == 'cyk', "Doesn't work for CYK") @unittest.skipIf(PARSER == 'cyk', "Doesn't work for CYK")
def test_prioritization(self): def test_prioritization(self):
"Tests effect of priority on result" "Tests effect of priority on result"
@@ -1849,7 +2025,7 @@ def _make_parser_test(LEXER, PARSER):






@unittest.skipIf(PARSER != 'earley' or LEXER == 'standard', "Currently only Earley supports priority sum in rules")
@unittest.skipIf(PARSER != 'earley' or 'dynamic' not in LEXER, "Currently only Earley supports priority sum in rules")
def test_prioritization_sum(self): def test_prioritization_sum(self):
"Tests effect of priority on result" "Tests effect of priority on result"


@@ -2060,9 +2236,9 @@ def _make_parser_test(LEXER, PARSER):
self.assertEqual(tok, text) self.assertEqual(tok, text)
self.assertEqual(tok.line, 1) self.assertEqual(tok.line, 1)
self.assertEqual(tok.column, 1) self.assertEqual(tok.column, 1)
if _LEXER != 'dynamic':
self.assertEqual(tok.end_line, 2)
self.assertEqual(tok.end_column, 6)
# if _LEXER != 'dynamic':
self.assertEqual(tok.end_line, 2)
self.assertEqual(tok.end_column, 6)


@unittest.skipIf(PARSER=='cyk', "Empty rules") @unittest.skipIf(PARSER=='cyk', "Empty rules")
def test_empty_end(self): def test_empty_end(self):
@@ -2153,7 +2329,7 @@ def _make_parser_test(LEXER, PARSER):
parser = _Lark(grammar) parser = _Lark(grammar)




@unittest.skipIf(PARSER!='lalr' or LEXER=='custom', "Serialize currently only works for LALR parsers without custom lexers (though it should be easy to extend)")
@unittest.skipIf(PARSER!='lalr' or 'custom' in LEXER, "Serialize currently only works for LALR parsers without custom lexers (though it should be easy to extend)")
def test_serialize(self): def test_serialize(self):
grammar = """ grammar = """
start: _ANY b "C" start: _ANY b "C"
@@ -2199,6 +2375,31 @@ def _make_parser_test(LEXER, PARSER):
self.assertEqual(a.line, 1) self.assertEqual(a.line, 1)
self.assertEqual(b.line, 2) self.assertEqual(b.line, 2)


@unittest.skipIf(PARSER=='cyk' or LEXER=='custom_old', "match_examples() not supported for CYK/old custom lexer")
def test_match_examples(self):
p = _Lark(r"""
start: "a" "b" "c"
""")

def match_error(s):
try:
_ = p.parse(s)
except UnexpectedInput as u:
return u.match_examples(p.parse, {
0: ['abe'],
1: ['ab'],
2: ['cbc', 'dbc'],
})
assert False

assert match_error("abe") == 0
assert match_error("ab") == 1
assert match_error("bbc") == 2
assert match_error("cbc") == 2
self.assertEqual( match_error("dbc"), 2 )
self.assertEqual( match_error("ebc"), 2 )


@unittest.skipIf(not regex or sys.version_info[0] == 2, 'Unicode and Python 2 do not place nicely together.') @unittest.skipIf(not regex or sys.version_info[0] == 2, 'Unicode and Python 2 do not place nicely together.')
def test_unicode_class(self): def test_unicode_class(self):
"Tests that character classes from the `regex` module work correctly." "Tests that character classes from the `regex` module work correctly."
@@ -2257,17 +2458,21 @@ def _make_parser_test(LEXER, PARSER):
_TestParser.__name__ = _NAME _TestParser.__name__ = _NAME
_TestParser.__qualname__ = "tests.test_parser." + _NAME _TestParser.__qualname__ = "tests.test_parser." + _NAME
globals()[_NAME] = _TestParser globals()[_NAME] = _TestParser
__all__.append(_NAME)


# Note: You still have to import them in __main__ for the tests to run
_TO_TEST = [ _TO_TEST = [
('standard', 'earley'), ('standard', 'earley'),
('standard', 'cyk'), ('standard', 'cyk'),
('standard', 'lalr'),

('dynamic', 'earley'), ('dynamic', 'earley'),
('dynamic_complete', 'earley'), ('dynamic_complete', 'earley'),
('standard', 'lalr'),
('contextual', 'lalr'), ('contextual', 'lalr'),
('custom', 'lalr'),
# (None, 'earley'),

('custom_new', 'lalr'),
('custom_new', 'cyk'),
('custom_old', 'earley'),
] ]


for _LEXER, _PARSER in _TO_TEST: for _LEXER, _PARSER in _TO_TEST:


Loading…
Cancel
Save