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 4 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
- 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
@@ -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) |
| [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 |
| [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 |


+ 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

- 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))
- CYK parser
- 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
SELECT: "select"i //# Will ignore case, and match SELECT or Select, etc.
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+

@@ -251,7 +255,7 @@ COMMENT: "#" /[^\n]/*
```
### %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).

@@ -264,7 +268,7 @@ When importing rules, all their dependencies will be imported into a namespace,
%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.



+ 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.

**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)

@@ -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.

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.



+ 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

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:


+ 11
- 4
docs/visitors.rst View File

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

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)

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

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

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

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

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

T(visit_tokens=True).transform(tree)

.. autoclass:: lark.visitors.Transformer_NonRecursive

.. autoclass:: lark.visitors.Transformer_InPlace

.. autoclass:: lark.visitors.Transformer_InPlaceRecursive

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
(See also: error_reporting_earley.py)
"""
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
funcdef: "def" NAME "(" parameters? ")" ["->" test] ":" suite

parameters: paramvalue ("," paramvalue)* ["," [ starparams | kwparams]]
parameters: paramvalue ("," paramvalue)* ["," "/"] ["," [starparams | kwparams]]
| starparams
| 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
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]*\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
from pathlib import Path

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

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

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


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

def test():


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

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


class ConfigurationError(LarkError, ValueError):
pass


class GrammarError(LarkError):
pass



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

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

from typing import (
TypeVar, Type, List, Dict, IO, Iterator, Callable, Union, Optional,
Literal, Protocol, Iterable,
Literal, Protocol, Tuple, Iterable,
)
from .visitors import Transformer
from .lexer import Token, Lexer, TerminalDef
@@ -34,11 +34,25 @@ class LarkOptions:
cache: Union[bool, str]
g_regex_flags: int
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:
source: str
grammar_source: str
source_path: str
source_grammar: str
options: LarkOptions
lexer: Lexer
terminals: List[TerminalDef]
@@ -49,7 +63,7 @@ class Lark:
*,
start: Union[None, str, List[str]] = "start",
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,
postlex: Optional[PostLex] = None,
ambiguity: Literal["explicit", "resolve"] = "resolve",
@@ -62,6 +76,8 @@ class Lark:
cache: Union[bool, str] = False,
g_regex_flags: int = ...,
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
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]:
...


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

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
from types import ModuleType
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,
)
from abc import abstractmethod, ABC
@@ -85,6 +85,9 @@ class Token(str):
end_column: 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:
...

@@ -100,10 +103,22 @@ class Lexer(ABC):
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):
terminals: Collection[TerminalDef]
ignore_types: List[str]
newline_types: List[str]
ignore_types: FrozenSet[str]
newline_types: FrozenSet[str]
user_callbacks: Dict[str, _Callback]
callback: Dict[str, _Callback]
mres: List[Tuple[REPattern, Dict[int, str]]]
@@ -111,11 +126,7 @@ class TraditionalLexer(Lexer):

def __init__(
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 next_token(self, lex_state: Any, parser_state: Any = None) -> Token:
...

class ContextualLexer(Lexer):
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 InlineTransformer, inline_args # XXX Deprecated
from .exceptions import (ParseError, LexError, GrammarError, UnexpectedToken,
UnexpectedInput, UnexpectedCharacters, LarkError)
UnexpectedInput, UnexpectedCharacters, UnexpectedEOF, LarkError)
from .lexer import Token
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


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,

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.skip_validation = skip_validation
self.use_bytes = use_bytes
self.lexer_type = None
@property
def tokens(self):
warn("LexerConf.tokens is deprecated. Use LexerConf.terminals instead", DeprecationWarning)
return self.terminals

###}

class ParserConf:

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

def __init__(self, rules, callbacks, start):
assert isinstance(start, list)
self.rules = rules
self.callbacks = callbacks
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):
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):
pass


class ParseError(LarkError):
pass


class LexError(LarkError):
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):
"""UnexpectedInput Error.
@@ -44,6 +49,7 @@ class UnexpectedInput(LarkError):
The parser doesn't hold a copy of the text it has to parse,
so you have to provide it again
"""
assert self.pos_in_stream is not None, self
pos = self.pos_in_stream
start = max(pos - span, 0)
end = pos + span
@@ -88,7 +94,7 @@ class UnexpectedInput(LarkError):
parse_fn(malformed)
except UnexpectedInput as ut:
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]" %
(self.state, self.accepts, ut.accepts, i, j))
continue
@@ -105,7 +111,7 @@ class UnexpectedInput(LarkError):

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

@@ -127,10 +133,24 @@ class UnexpectedInput(LarkError):
ts = names
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):
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.column = column
self.pos_in_stream = lex_pos
@@ -168,7 +188,8 @@ class UnexpectedToken(ParseError, UnexpectedInput):

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.column = getattr(token, 'column', '?')
self.pos_in_stream = getattr(token, 'pos_in_stream', None)
@@ -179,6 +200,7 @@ class UnexpectedToken(ParseError, UnexpectedInput):
self.considered_rules = considered_rules
self.puppet = puppet
self._all_terminals = all_terminals
self.token_history = token_history


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
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)))
if self.token_history:
message += "Previous tokens: %r\n" % self.token_history

return message


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

message = 'Error trying to process rule "%s":\n\n%s' % (rule, orig_exc)
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)



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

is_term = False



class RuleOptions(Serialize):
__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



###}

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

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


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

ESCAPED_STRING : "\"" _STRING_ESC_INNER "\""

@@ -48,3 +51,9 @@ CR : /\r/
LF : /\n/
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]*/
TOKEN: /_?[A-Z][_A-Z0-9]*/
STRING: _STRING "i"?
REGEXP: /\/(?!\/)(\\\/|\\\\|[^\/\n])*?\/[imslux]*/
REGEXP: /\/(?!\/)(\\\/|\\\\|[^\/])*?\/[imslux]*/
_NL: /(\r?\n)+\s*/

%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 lark.exceptions import UnexpectedCharacters, UnexpectedInput, UnexpectedToken
from lark.exceptions import UnexpectedCharacters, UnexpectedInput, UnexpectedToken, ConfigurationError, assert_config

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

import tempfile
from warnings import warn

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 .common import LexerConf, ParserConf

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

###{standalone


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

@@ -33,9 +35,10 @@ class LarkOptions(Serialize):
start
The start symbol. Either a string, or a list of strings for multiple possible starts (Default: "start")
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
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
Propagates (line, column, end_line, end_column) attributes into all tree branches.
maybe_placeholders
@@ -91,6 +94,10 @@ class LarkOptions(Serialize):
Accept an input of type ``bytes`` instead of ``str`` (Python 3 only).
edit_terminals
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 ===**
"""
@@ -125,6 +132,8 @@ class LarkOptions(Serialize):
'edit_terminals': None,
'g_regex_flags': 0,
'use_bytes': False,
'import_paths': [],
'source_path': None,
}

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

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:
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)')

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

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

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

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

# 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
try:
@@ -222,29 +235,29 @@ class Lark(Serialize):
grammar = read()

assert isinstance(grammar, STRING_TYPE)
self.grammar_source = grammar
self.source_grammar = grammar
if self.options.use_bytes:
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':
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.")

cache_fn = None
if self.options.cache:
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):
cache_fn = self.options.cache
else:
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')
from . import __version__
options_str = ''.join(k+str(v) for k, v in options.items() if k not in unhashable)
s = grammar + options_str + __version__
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):
logger.debug('Loading grammar from cache: %s', cache_fn)
@@ -265,27 +278,28 @@ class Lark(Serialize):
else:
assert False, self.options.parser
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.parser == 'earley':
self.options.ambiguity = 'resolve'
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':
self.options.priority = 'normal'

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"'
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:
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
# 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).
elif self.options.priority == None:
elif self.options.priority is None:
for rule in self.rules:
if rule.options.priority is not None:
rule.options.priority = None
@@ -350,7 +364,7 @@ class Lark(Serialize):
self.rules,
self.options.tree_class or Tree,
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._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}, {})
options = dict(data['options'])
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))
options.update(kwargs)
self.options = LarkOptions.deserialize(options, memo)
self.rules = [Rule.deserialize(r, memo) for r in data['rules']]
self.source = '<deserialized>'
self.source_path = '<deserialized>'
self._prepare_callbacks()
self.parser = self.parser_class.deserialize(
data['parser'],
memo,
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_dict = {t.name: t for t in self.terminals}
@@ -429,8 +443,26 @@ class Lark(Serialize):
with open(grammar_filename, encoding='utf8') as f:
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):
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):
@@ -490,5 +522,23 @@ class Lark(Serialize):
except UnexpectedCharacters as 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

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


class Pattern(Serialize):

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

def __eq__(self, other):
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)
max_width = min_width


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

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

@property
def max_width(self):
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)

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):
return 'Token(%r, %r)' % (self.type, self.value)
@@ -195,6 +199,7 @@ class UnlessCallback:
break
return t


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





def _create_unless(terminals, g_regex_flags, re_, use_bytes):
tokens_by_type = classify(terminals, lambda t: type(t.pattern))
assert len(tokens_by_type) <= 2, tokens_by_type.keys()
embedded_strs = set()
callback = {}
for retok in tokens_by_type.get(PatternRE, []):
unless = [] # {}
unless = []
for strtok in tokens_by_type.get(PatternStr, []):
if strtok.priority > retok.priority:
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.. :/
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:]
return mres


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)


def _regexp_has_newline(r):
r"""Expressions that may indicate newlines in a regexp:
- 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)


class Lexer(object):
"""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.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.user_callbacks = conf.callbacks
self.g_regex_flags = conf.g_regex_flags
@@ -311,7 +316,7 @@ class TraditionalLexer(Lexer):
self._mres = None

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())

for type_, f in self.user_callbacks.items():
@@ -338,9 +343,9 @@ class TraditionalLexer(Lexer):
def lex(self, state, parser_state):
with suppress(EOFError):
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
while line_ctr.char_pos < len(lex_state.text):
res = self.match(lex_state.text, line_ctr.char_pos)
@@ -350,7 +355,7 @@ class TraditionalLexer(Lexer):
allowed = {"<END-OF-FILE>"}
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],
_all_terminals=self.terminals)
state=parser_state, _all_terminals=self.terminals)

value, type_ = res

@@ -363,7 +368,7 @@ class TraditionalLexer(Lexer):
if t.type in self.callback:
t = self.callback[t.type](t)
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
return t
else:
@@ -375,6 +380,7 @@ class TraditionalLexer(Lexer):
# EOF
raise EOFError(self)


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

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


class ContextualLexer(Lexer):

def __init__(self, conf, states, always_accept=()):
@@ -424,17 +431,17 @@ class ContextualLexer(Lexer):
try:
while True:
lexer = self.lexers[parser_state.position]
yield lexer.next_token(lexer_state)
yield lexer.next_token(lexer_state, parser_state)
except EOFError:
pass
except UnexpectedCharacters as e:
# 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.
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:
"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):
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 sys
from copy import copy, deepcopy
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 .parse_tree_builder import ParseTreeBuilder
from .parser_frontends import LALR_TraditionalLexer
from .parser_frontends import ParsingFrontend
from .common import LexerConf, ParserConf
from .grammar import RuleOptions, Rule, Terminal, NonTerminal, Symbol
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)

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

EXT = '.lark'

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


@inline_args
class EBNF_to_BNF(Transformer_InPlace):
def __init__(self):
@@ -258,9 +261,9 @@ class SimplifyRule_Visitor(Visitor):
for i, child in enumerate(tree.children):
if isinstance(child, Tree) and child.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)
break

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

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

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


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):
self.terminals = terminals
@@ -309,7 +315,6 @@ class PrepareAnonTerminals(Transformer_InPlace):
self.i = 0
self.rule_options = None


@inline_args
def pattern(self, p):
value = p.value
@@ -328,14 +333,16 @@ class PrepareAnonTerminals(Transformer_InPlace):
try:
term_name = _TERMINAL_NAMES[value]
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:
term_name = None

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
else:
assert False, p
@@ -357,7 +364,7 @@ class PrepareAnonTerminals(Transformer_InPlace):


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

def __init__(self):
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', c, None)


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):
self.rule_defs = rule_defs
@@ -399,6 +407,30 @@ def _rfind(s, 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):
@@ -439,7 +471,7 @@ class PrepareLiterals(Transformer_InPlace):
assert start.type == end.type == 'STRING'
start = start.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)
return ST('pattern', [PatternRE(regexp)])

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

return PatternRE(regexp, flags)


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


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


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


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


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

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]

# =================
@@ -564,10 +601,10 @@ class Grammar:
ebnf_to_bnf = EBNF_to_BNF()
rules = []
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]
i += 1
if len(params) != 0: # Dont transform templates
if len(params) != 0: # Dont transform templates
continue
rule_options = RuleOptions(keep_all_tokens=True) if options and options.keep_all_tokens else None
ebnf_to_bnf.rule_options = rule_options
@@ -592,7 +629,7 @@ class Grammar:

for i, (expansion, alias) in enumerate(expansions):
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]
if any(empty_indices):
@@ -621,14 +658,13 @@ class Grammar:
# Remove duplicates
compiled_rules = list(set(compiled_rules))


# Filter out unused rules
while True:
c = len(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}
compiled_rules, unused = classify_bool(compiled_rules, lambda r: r.origin in used_rules)
for r in unused:
@@ -647,9 +683,63 @@ class Grammar:
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 = {}


def import_from_grammar_into_namespace(grammar, namespace, aliases):
"""Returns all rules and terminals of grammar, prepended
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))
return _find_used_symbols(tree) - set(params)



def get_namespace_name(name, params):
if params is not None:
try:
@@ -692,19 +780,17 @@ def import_from_grammar_into_namespace(grammar, namespace, aliases):
else:
assert symbol.type == 'RULE'
_, 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 i, c in enumerate(t.children):
if isinstance(c, Token) and c.type in ('RULE', 'TERMINAL'):
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))


return term_defs, rule_defs



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

@@ -744,7 +830,7 @@ def options_from_rule(name, params, *x):
else:
expansions ,= x
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('!')
name = name.lstrip('!')
@@ -758,10 +844,12 @@ def options_from_rule(name, params, *x):
def symbols_from_strcase(expansion):
return [Terminal(x, filter_out=x.startswith('_')) if x.isupper() else NonTerminal(x) for x in expansion]


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

def nonterminal(self, name):
return name

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


class GrammarLoader:
ERRORS = [
('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']),
('Illegal name for rules or terminals', ['Aa:\n']),
('Alias expects lowercase name', ['a: -> "a"\n']),
@@ -786,43 +875,53 @@ class GrammarLoader:
('%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()]

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()
lexer_conf = LexerConf(terminals, re_module, ['WS', 'COMMENT'])
import re
lexer_conf = LexerConf(terminals, re, ['WS', 'COMMENT'])
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.re_module = re_module
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:
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
break
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

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:
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:
context = e.get_context(grammar_text)
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

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

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)
except KeyError:
imports[dotted_path] = base_paths, aliases
imports[dotted_path] = base_path, aliases

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

# 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
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)

term_defs += new_td
@@ -972,7 +1074,7 @@ class GrammarLoader:
raise GrammarError("Template '%s' used but not defined (in rule %s)" % (sym, name))
if len(args) != rule_names[sym]:
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):
if sym.type == 'TERMINAL':
if sym not in terminal_names:
@@ -981,10 +1083,8 @@ class GrammarLoader:
if sym not in rule_names and sym not in params:
raise GrammarError("Rule '%s' used but not defined (in rule %s)" % (sym, name))


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 .lexer import Token
from .tree import Tree
from .visitors import InlineTransformer # XXX Deprecated
from .visitors import InlineTransformer # XXX Deprecated
from .visitors import Transformer_InPlace
from .visitors import _vargs_meta, _vargs_meta_inline

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


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

return self.node_builder(filtered)


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):
filtered = []
@@ -108,6 +110,7 @@ class ChildFilterLALR(ChildFilter):

return self.node_builder(filtered)


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


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


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


class AmbiguousExpander:
"""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
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):
self.node_builder = node_builder
self.tree_class = tree_class
self.to_expand = to_expand

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
# 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.
@@ -186,15 +192,17 @@ class AmbiguousExpander:
if not ambiguous:
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))])


def maybe_create_ambiguous_expander(tree_class, expansion, keep_all_tokens):
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 to_expand:
return partial(AmbiguousExpander, to_expand, tree_class)


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

return self.node_builder(children)


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


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


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

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

yield rule, wrapper_chain


def create_callback(self, transformer=None):
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 .parsers.grammar_analysis import GrammarAnalyzer
from .lexer import LexerThread, TraditionalLexer, ContextualLexer, Lexer, Token, TerminalDef
from .parsers import earley, xearley, cyk
from .parsers.lalr_parser import LALR_Parser
from .grammar import Rule
from .tree import Tree
from .common import LexerConf
from .exceptions import UnexpectedInput
from .common import LexerConf, ParserConf
try:
import regex
except ImportError:
@@ -15,63 +14,118 @@ import re

###{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:
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:
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:
start = self.start
start = self.parser_conf.start
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
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):
@@ -95,174 +149,86 @@ class PostLexConnector:
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 = {}
for t in lexer_conf.terminals:
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()
try:
width = get_regexp_width(regexp)[0]
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:
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:
regexp = regexp.encode('utf-8')

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.parser = cyk.Parser(parser_conf.rules)

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):
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:
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.

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
@@ -147,7 +146,7 @@ class Parser:
column.add(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):
if item.is_complete:
return True
@@ -246,7 +245,7 @@ class Parser:

if not next_set and not next_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

@@ -262,20 +261,24 @@ class Parser:
# 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
# step.
expects = {i.expect for i in to_scan}
i = 0
for token in stream:
for token in lexer.lex(expects):
self.predict_and_complete(i, to_scan, columns, transitives)

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

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

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

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

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

@@ -292,12 +295,16 @@ class Parser:
else:
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
# 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.
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:
from .earley_forest import ForestToPyDotVisitor
try:
@@ -308,10 +315,7 @@ class Parser:
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!'

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.
"""

class _NoData():
pass

NO_DATA = _NoData()

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

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

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, "
"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

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

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

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)
data = self._collapse_ambig(data)
return self._call_ambig_func(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)
if len(data) > 1:
children = [self.tree_class('_inter', c) for c in data]
@@ -544,36 +558,40 @@ class ForestToParseTree(ForestTransformer):

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

def visit_symbol_node_in(self, node):
self._on_cycle_retreat = False
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):
self.prioritizer.visit(node)
if self.resolve_ambiguity:
return node.children[0]
return node.children

def visit_packed_node_in(self, node):
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):
"""Decorator for methods of subclasses of ``TreeForestTransformer``.
@@ -679,7 +697,10 @@ class ForestToPyDotVisitor(ForestVisitor):

def visit(self, root, filename):
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):
graph_node_id = str(id(node))


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

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

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

###{standalone

class LALR_Parser(object):
class LALR_Parser(Serialize):
def __init__(self, parser_conf, debug=False):
analysis = LALR_Analyzer(parser_conf, debug=debug)
analysis.compute_lalr()
@@ -62,6 +63,12 @@ class ParserState(object):
def position(self):
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):
return type(self)(
self.parse_conf,
@@ -86,7 +93,7 @@ class ParserState(object):
action, arg = states[state][token.type]
except KeyError:
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

@@ -95,7 +102,7 @@ class ParserState(object):
assert not is_end
state_stack.append(arg)
value_stack.append(token)
return arg
return
else:
# reduce+shift as many times as necessary
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``.
"""
return self.parser_state.feed_token(token)
return self.parser_state.feed_token(token, token.type == '$END')

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

def copy(self):
return copy(self)

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

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):
"""Print the output of ``choices()`` in a way that's easier to read."""
@@ -78,3 +81,16 @@ class ParserPuppet(object):
def resume_parse(self):
"""Resume parsing from the current puppet 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)
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.
# 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

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



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

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

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

%import common.ESCAPED_STRING -> _STRING
%import common.WS
%ignore WS
%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)

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('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)')


+ 10
- 6
lark/tree.py View File

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

def _pretty(self, level, indent_str):
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:
if isinstance(n, Tree):
l += n._pretty(level+1, indent_str)
else:
l += [ indent_str*(level+1), '%s' % (n,), '\n' ]
l += [indent_str*(level+1), '%s' % (n,), '\n']

return l

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

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]
self.children[i:i+1] = kid.children

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

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

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

@property
def end_column(self):
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.write(filename)


def pydot__tree_to_graph(tree, rankdir="LR", **kwargs):
"""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)
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
graph.add_node(node)



+ 9
- 1
lark/tree_matcher.py View File

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



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

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

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

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

+ 10
- 39
lark/utils.py View File

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

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

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


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

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



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

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


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


try:
import regex
except ImportError:
regex = None

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

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

def get_regexp_width(expr):
if regex:
# 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
the list entries are hashable."""
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:
@@ -197,8 +195,6 @@ except ImportError:
pass




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



class Enumerator(Serialize):
def __init__(self):
self.enums = {}
@@ -229,31 +224,6 @@ class Enumerator(Serialize):
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):
"""
@@ -332,4 +302,5 @@ def _serialize(value, memo):
return list(value) # TODO reversible?
elif isinstance(value, dict):
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

+ 52
- 38
lark/visitors.py View File

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


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

# Transformers


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

@@ -47,7 +49,7 @@ class _Decoratable:
class Transformer(_Decoratable):
"""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.

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

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

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


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

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

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

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)

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



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


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
return self._call_userfunc(tree)

@@ -187,7 +190,12 @@ class Transformer_InPlace(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):
# Tree to postfix
@@ -195,7 +203,7 @@ class Transformer_NonRecursive(Transformer):
q = [tree]
while q:
t = q.pop()
rev_postfix.append( t )
rev_postfix.append(t)
if isinstance(t, Tree):
q += t.children

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



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):
tree.children = list(self._transform_children(tree.children))
return self._call_userfunc(tree)



# Visitors

class VisitorBase:
@@ -233,7 +239,10 @@ class VisitorBase:
return getattr(self, tree.data, self.__default__)(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

def __class_getitem__(cls, _):
@@ -241,18 +250,19 @@ class 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):
"Visits the tree, starting with the leaves and finally the root (bottom-up)"
for subtree in tree.iter_subtrees():
self._call_userfunc(subtree)
return 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():
self._call_userfunc(subtree)
return tree
@@ -261,11 +271,13 @@ class Visitor(VisitorBase):
class Visitor_Recursive(VisitorBase):
"""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):
"Visits the tree, starting with the leaves and finally the root (bottom-up)"
for child in tree.children:
if isinstance(child, Tree):
self.visit(child)
@@ -274,6 +286,7 @@ class Visitor_Recursive(VisitorBase):
return tree

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

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



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




# Decorators

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



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



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


def _vargs_inline(f, data, children, meta):
def _vargs_inline(f, _data, children, _meta):
return f(*children)
def _vargs_meta_inline(f, data, children, meta):
def _vargs_meta_inline(f, _data, children, meta):
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!
def _vargs_tree(f, 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,
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:
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:
::
@@ -440,7 +452,7 @@ def v_args(inline=False, meta=False, tree=False, wrapper=None):
###}


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

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

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

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

+ 2
- 2
setup.py View File

@@ -29,8 +29,8 @@ setup(
description = "a modern parsing library",
license = "MIT",
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='''
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_grammar import TestGrammar
from .test_reconstructor import TestReconstructor
from .test_tree_forest_transformer import TestTreeForestTransformer

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

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)



+ 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 import Token
from lark.load_grammar import FromPackageLoader

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

import lark
from lark import logger
from lark.lark import Lark
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.grammar import Rule
from lark.lexer import TerminalDef, Lexer, TraditionalLexer
from lark.indenter import Indenter

logger.setLevel(logging.INFO)

__all__ = ['TestParsers']

__path__ = os.path.dirname(__file__)
def _read(n, *args):
@@ -322,6 +324,22 @@ class TestParsers(unittest.TestCase):
def test_alias(self):
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):
@@ -745,6 +763,76 @@ def _make_full_earley_test(LEXER):
tree = parser.parse(text)
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()
_TestFullEarley.__name__ = _NAME
globals()[_NAME] = _TestFullEarley
__all__.append(_NAME)

class CustomLexer(Lexer):
class CustomLexerNew(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, *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):
"""
@@ -851,12 +955,31 @@ class DualBytesLark:
self.bytes_lark.load(f)

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):
return Lark(grammar, lexer=lexer_class_or_name, parser=PARSER, propagate_positions=True, **kwargs)
def _Lark_open(gfilename, **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):
def test_basic1(self):
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())))

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':
expected_error = ParseError

@@ -1545,13 +1668,13 @@ def _make_parser_test(LEXER, PARSER):
self.assertEqual(d.line, 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)

@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?
class TestPostLexer:
def process(self, stream):
@@ -1805,6 +1928,59 @@ def _make_parser_test(LEXER, PARSER):
tree = parser.parse(test_file)
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")
def test_prioritization(self):
"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):
"Tests effect of priority on result"

@@ -2060,9 +2236,9 @@ def _make_parser_test(LEXER, PARSER):
self.assertEqual(tok, text)
self.assertEqual(tok.line, 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")
def test_empty_end(self):
@@ -2153,7 +2329,7 @@ def _make_parser_test(LEXER, PARSER):
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):
grammar = """
start: _ANY b "C"
@@ -2199,6 +2375,31 @@ def _make_parser_test(LEXER, PARSER):
self.assertEqual(a.line, 1)
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.')
def test_unicode_class(self):
"Tests that character classes from the `regex` module work correctly."
@@ -2257,17 +2458,21 @@ def _make_parser_test(LEXER, PARSER):
_TestParser.__name__ = _NAME
_TestParser.__qualname__ = "tests.test_parser." + _NAME
globals()[_NAME] = _TestParser
__all__.append(_NAME)

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

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

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

for _LEXER, _PARSER in _TO_TEST:


Loading…
Cancel
Save