@@ -0,0 +1,12 @@ | |||
# These are supported funding model platforms | |||
github: lark-parser | |||
patreon: # Replace with a single Patreon username | |||
open_collective: # Replace with a single Open Collective username | |||
ko_fi: # Replace with a single Ko-fi username | |||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel | |||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry | |||
liberapay: # Replace with a single Liberapay username | |||
issuehunt: # Replace with a single IssueHunt username | |||
otechie: # Replace with a single Otechie username | |||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] |
@@ -0,0 +1,18 @@ | |||
--- | |||
name: Bug report | |||
about: Create a report to help us improve | |||
title: '' | |||
labels: '' | |||
assignees: '' | |||
--- | |||
**Describe the bug** | |||
A clear and concise description of what the bug is, and what you expected to happen. | |||
**To Reproduce** | |||
Provide a short script that reproduces the erroneous behavior. | |||
If that is impossible, provide clear steps to reproduce the behavior. |
@@ -0,0 +1,17 @@ | |||
--- | |||
name: Feature request | |||
about: Suggest an idea for this project | |||
title: '' | |||
labels: enhancement | |||
assignees: '' | |||
--- | |||
**Suggestion** | |||
Provide a clear and concise description of what the problem is, and what you would like to happen. | |||
**Describe alternatives you've considered** | |||
A clear and concise description of any alternative solutions or features you've considered. | |||
**Additional context** | |||
Add any other context or screenshots about the feature request here. |
@@ -0,0 +1,10 @@ | |||
--- | |||
name: Other | |||
about: For any discussion that doesn't fit the templates | |||
title: '' | |||
labels: '' | |||
assignees: '' | |||
--- | |||
@@ -0,0 +1,18 @@ | |||
--- | |||
name: Question | |||
about: Ask a question about Lark or request help | |||
title: '' | |||
labels: question | |||
assignees: '' | |||
--- | |||
**What is your question?** | |||
Try to be accurate and concise. | |||
**If you're having trouble with your code or grammar** | |||
Provide a small script that encapsulates your issue. | |||
Explain what you're trying to do, and what is obstructing your progress. |
@@ -0,0 +1,42 @@ | |||
name: Compute coverage and push to Codecov | |||
on: [push] | |||
jobs: | |||
run: | |||
runs-on: ${{ matrix.os }} | |||
strategy: | |||
matrix: | |||
os: [ubuntu-latest, macos-latest, windows-latest] | |||
env: | |||
OS: ${{ matrix.os }} | |||
PYTHON: '3.7' | |||
steps: | |||
- uses: actions/checkout@master | |||
- name: Download submodules | |||
run: | | |||
git submodule update --init --recursive | |||
git submodule sync -q | |||
git submodule update --init | |||
- name: Setup Python | |||
uses: actions/setup-python@master | |||
with: | |||
python-version: 3.7 | |||
- name: Install dependencies | |||
run: | | |||
python -m pip install --upgrade pip | |||
pip install -r test-requirements.txt | |||
- name: Generate coverage report | |||
run: | | |||
pip install pytest | |||
pip install pytest-cov | |||
pytest --cov=./ --cov-report=xml | |||
- name: Upload coverage to Codecov | |||
uses: codecov/codecov-action@v1 | |||
with: | |||
token: ${{ secrets.CODECOV_TOKEN }} | |||
files: ./coverage.xml | |||
flags: unittests | |||
env_vars: OS,PYTHON | |||
name: codecov-umbrella | |||
fail_ci_if_error: true | |||
path_to_write_report: ./coverage/codecov_report.txt | |||
verbose: true |
@@ -0,0 +1,19 @@ | |||
name: Python type check | |||
on: [push, pull_request] | |||
jobs: | |||
build: | |||
runs-on: ubuntu-latest | |||
steps: | |||
- uses: actions/checkout@v1 | |||
- name: Download submodules | |||
run: git submodule update --init --recursive | |||
- name: Set up Python | |||
uses: actions/setup-python@v1 | |||
with: | |||
python-version: 3.8 | |||
- name: Install dependencies | |||
run: | | |||
python -m pip install --upgrade pip | |||
pip install mypy | |||
- name: Lint with mypy | |||
run: mypy -p lark || true |
@@ -0,0 +1,28 @@ | |||
name: Tests | |||
on: [push, pull_request] | |||
jobs: | |||
build: | |||
runs-on: ubuntu-latest | |||
strategy: | |||
matrix: | |||
python-version: [3.6, 3.7, 3.8, 3.9, 3.10.0-rc - 3.10, pypy3] | |||
steps: | |||
- uses: actions/checkout@v2 | |||
- name: Download submodules | |||
run: | | |||
git submodule update --init --recursive | |||
git submodule sync -q | |||
git submodule update --init | |||
- name: Set up Python ${{ matrix.python-version }} | |||
uses: actions/setup-python@v2 | |||
with: | |||
python-version: ${{ matrix.python-version }} | |||
- name: Install dependencies | |||
run: | | |||
python -m pip install --upgrade pip | |||
pip install -r test-requirements.txt | |||
- name: Run tests | |||
run: | | |||
python -m tests |
@@ -1 +1,14 @@ | |||
__pycache__ | |||
*.pyc | |||
*.pyo | |||
/.tox | |||
/lark_parser.egg-info/** | |||
tags | |||
.vscode | |||
.idea | |||
.ropeproject | |||
.cache | |||
.mypy_cache | |||
/dist | |||
/build | |||
docs/_build | |||
docs/examples |
@@ -0,0 +1,3 @@ | |||
[submodule "tests/test_nearley/nearley"] | |||
path = tests/test_nearley/nearley | |||
url = https://github.com/Hardmath123/nearley |
@@ -0,0 +1,11 @@ | |||
v1.0 | |||
- `maybe_placeholders` is now True by default | |||
- `use_accepts` in `UnexpectedInput.match_examples()` is now True by default | |||
- Token priority is now 0 by default | |||
- `v_args(meta=True)` now gives meta as the first argument. i.e. `(meta, children)` | |||
- Renamed TraditionalLexer to BasicLexer, and 'standard' lexer option to 'basic' |
@@ -0,0 +1,19 @@ | |||
Copyright © 2017 Erez Shinan | |||
Permission is hereby granted, free of charge, to any person obtaining a copy of | |||
this software and associated documentation files (the "Software"), to deal in | |||
the Software without restriction, including without limitation the rights to | |||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | |||
the Software, and to permit persons to whom the Software is furnished to do so, | |||
subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in all | |||
copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | |||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | |||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||
@@ -0,0 +1 @@ | |||
include README.md LICENSE docs/* examples/*.py examples/*.png examples/*.lark tests/*.py tests/*.lark tests/grammars/* tests/test_nearley/*.py tests/test_nearley/grammars/* |
@@ -1,31 +1,188 @@ | |||
GITMIRROR | |||
========= | |||
# Lark - a parsing toolkit for Python | |||
This repo is a mirror of various repositories that I want to keep track of. | |||
I realized that git, w/ it's inherently dedupability, and the ability to | |||
store many trees in a single repo, that it'd be easy to create a repo that | |||
regularly clones/mirrors other source repos. Not only this, but the | |||
state of the tags and branches can be archived on a daily basis, | |||
consuming very little space. | |||
Lark is a parsing toolkit for Python, built with a focus on ergonomics, performance and modularity. | |||
The main reason that I want this is from a supply chain availability | |||
perspective. As a consumer of source, it isn't always guaranteed that | |||
the project you depend upon will continue to exist in the future. It | |||
could also be that older version are removed, etc. | |||
Lark can parse all context-free languages. To put it simply, it means that it is capable of parsing almost any programming language out there, and to some degree most natural languages too. | |||
**Who is it for?** | |||
Quick start | |||
----------- | |||
- **Beginners**: Lark is very friendly for experimentation. It can parse any grammar you throw at it, no matter how complicated or ambiguous, and do so efficiently. It also constructs an annotated parse-tree for you, using only the grammar and an input, and it gives you convienient and flexible tools to process that parse-tree. | |||
1. Update the file `repos.txt` with a list of urls that you want to mirror. | |||
2. Run the script `doupdate.sh` to mirror all the repos. | |||
3. Optionally run `git push --mirror origin` to store the data on the server. | |||
- **Experts**: Lark implements both Earley(SPPF) and LALR(1), and several different lexers, so you can trade-off power and speed, according to your requirements. It also provides a variety of sophisticated features and utilities. | |||
**What can it do?** | |||
Process | |||
------- | |||
- Parse all context-free grammars, and handle any ambiguity gracefully | |||
- Build an annotated parse-tree automagically, no construction code required. | |||
- Provide first-rate performance in terms of both Big-O complexity and measured run-time (considering that this is Python ;) | |||
- Run on every Python interpreter (it's pure-python) | |||
- Generate a stand-alone parser (for LALR(1) grammars) | |||
1. Repo will self update main to get latest repos/code to mirror. | |||
2. Fetch the repos to mirror into their respective date tagged tags/branches. | |||
3. Push the tags/branches to the parent. | |||
4. Repeat | |||
And many more features. Read ahead and find out! | |||
Most importantly, Lark will save you time and prevent you from getting parsing headaches. | |||
### Quick links | |||
- [Documentation @readthedocs](https://lark-parser.readthedocs.io/) | |||
- [Cheatsheet (PDF)](/docs/_static/lark_cheatsheet.pdf) | |||
- [Online IDE](https://lark-parser.github.io/ide) | |||
- [Tutorial](/docs/json_tutorial.md) for writing a JSON parser. | |||
- Blog post: [How to write a DSL with Lark](http://blog.erezsh.com/how-to-write-a-dsl-in-python-with-lark/) | |||
- [Gitter chat](https://gitter.im/lark-parser/Lobby) | |||
### Install Lark | |||
$ pip install lark --upgrade | |||
Lark has no dependencies. | |||
[](https://github.com/lark-parser/lark/actions/workflows/tests.yml) | |||
### Syntax Highlighting | |||
Lark provides syntax highlighting for its grammar files (\*.lark): | |||
- [Sublime Text & TextMate](https://github.com/lark-parser/lark_syntax) | |||
- [vscode](https://github.com/lark-parser/vscode-lark) | |||
- [Intellij & PyCharm](https://github.com/lark-parser/intellij-syntax-highlighting) | |||
- [Vim](https://github.com/lark-parser/vim-lark-syntax) | |||
- [Atom](https://github.com/Alhadis/language-grammars) | |||
### Clones | |||
These are implementations of Lark in other languages. They accept Lark grammars, and provide similar utilities. | |||
- [Lerche (Julia)](https://github.com/jamesrhester/Lerche.jl) - an unofficial clone, written entirely in Julia. | |||
- [Lark.js (Javascript)](https://github.com/lark-parser/lark.js) - a port of the stand-alone LALR(1) parser generator to Javascsript. | |||
### Hello World | |||
Here is a little program to parse "Hello, World!" (Or any other similar phrase): | |||
```python | |||
from lark import Lark | |||
l = Lark('''start: WORD "," WORD "!" | |||
%import common.WORD // imports from terminal library | |||
%ignore " " // Disregard spaces in text | |||
''') | |||
print( l.parse("Hello, World!") ) | |||
``` | |||
And the output is: | |||
```python | |||
Tree(start, [Token(WORD, 'Hello'), Token(WORD, 'World')]) | |||
``` | |||
Notice punctuation doesn't appear in the resulting tree. It's automatically filtered away by Lark. | |||
### Fruit flies like bananas | |||
Lark is great at handling ambiguity. Here is the result of parsing the phrase "fruit flies like bananas": | |||
 | |||
[Read the code here](https://github.com/lark-parser/lark/tree/master/examples/fruitflies.py), and see [more examples here](https://lark-parser.readthedocs.io/en/latest/examples/index.html). | |||
## List of main features | |||
- Builds a parse-tree (AST) automagically, based on the structure of the grammar | |||
- **Earley** parser | |||
- Can parse all context-free grammars | |||
- Full support for ambiguous grammars | |||
- **LALR(1)** parser | |||
- Fast and light, competitive with PLY | |||
- Can generate a stand-alone parser ([read more](docs/tools.md#stand-alone-parser)) | |||
- **CYK** parser, for highly ambiguous grammars | |||
- **EBNF** grammar | |||
- **Unicode** fully supported | |||
- **Python 2 & 3** compatible | |||
- Automatic line & column tracking | |||
- Standard library of terminals (strings, numbers, names, etc.) | |||
- Import grammars from Nearley.js ([read more](/docs/tools.md#importing-grammars-from-nearleyjs)) | |||
- Extensive test suite [](https://codecov.io/gh/lark-parser/lark) | |||
- MyPy support using type stubs | |||
- And much more! | |||
See the full list of [features here](https://lark-parser.readthedocs.io/en/latest/features.html) | |||
### Comparison to other libraries | |||
#### Performance comparison | |||
Lark is the fastest and lightest (lower is better) | |||
 | |||
 | |||
Check out the [JSON tutorial](/docs/json_tutorial.md#conclusion) for more details on how the comparison was made. | |||
*Note: I really wanted to add PLY to the benchmark, but I couldn't find a working JSON parser anywhere written in PLY. If anyone can point me to one that actually works, I would be happy to add it!* | |||
*Note 2: The parsimonious code has been optimized for this specific test, unlike the other benchmarks (Lark included). Its "real-world" performance may not be as good.* | |||
#### Feature comparison | |||
| Library | Algorithm | Grammar | Builds tree? | Supports ambiguity? | Can handle every CFG? | Line/Column tracking | Generates Stand-alone | |||
|:--------|:----------|:----|:--------|:------------|:------------|:----------|:---------- | |||
| **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](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 | | |||
(\* *PEGs cannot handle non-deterministic grammars. Also, according to Wikipedia, it remains unanswered whether PEGs can really parse all deterministic CFGs*) | |||
### Projects using Lark | |||
- [Poetry](https://github.com/python-poetry/poetry-core) - A utility for dependency management and packaging | |||
- [tartiflette](https://github.com/dailymotion/tartiflette) - a GraphQL server by Dailymotion | |||
- [Hypothesis](https://github.com/HypothesisWorks/hypothesis) - Library for property-based testing | |||
- [mappyfile](https://github.com/geographika/mappyfile) - a MapFile parser for working with MapServer configuration | |||
- [synapse](https://github.com/vertexproject/synapse) - an intelligence analysis platform | |||
- [Datacube-core](https://github.com/opendatacube/datacube-core) - Open Data Cube analyses continental scale Earth Observation data through time | |||
- [SPFlow](https://github.com/SPFlow/SPFlow) - Library for Sum-Product Networks | |||
- [Torchani](https://github.com/aiqm/torchani) - Accurate Neural Network Potential on PyTorch | |||
- [Command-Block-Assembly](https://github.com/simon816/Command-Block-Assembly) - An assembly language, and C compiler, for Minecraft commands | |||
- [EQL](https://github.com/endgameinc/eql) - Event Query Language | |||
- [Fabric-SDK-Py](https://github.com/hyperledger/fabric-sdk-py) - Hyperledger fabric SDK with Python 3.x | |||
- [required](https://github.com/shezadkhan137/required) - multi-field validation using docstrings | |||
- [miniwdl](https://github.com/chanzuckerberg/miniwdl) - A static analysis toolkit for the Workflow Description Language | |||
- [pytreeview](https://gitlab.com/parmenti/pytreeview) - a lightweight tree-based grammar explorer | |||
- [harmalysis](https://github.com/napulen/harmalysis) - A language for harmonic analysis and music theory | |||
- [gersemi](https://github.com/BlankSpruce/gersemi) - A CMake code formatter | |||
Using Lark? Send me a message and I'll add your project! | |||
## License | |||
Lark uses the [MIT license](LICENSE). | |||
(The standalone tool is under MPL2) | |||
## Contribute | |||
Lark is currently accepting pull-requests. See [How to develop Lark](/docs/how_to_develop.md) | |||
## Sponsor | |||
If you like Lark, and want to see it grow, please consider [sponsoring us!](https://github.com/sponsors/lark-parser) | |||
## Contact the author | |||
Questions about code are best asked on [gitter](https://gitter.im/lark-parser/Lobby) or in the issues. | |||
For anything else, I can be reached by email at erezshin at gmail com. | |||
-- [Erez](https://github.com/erezsh) |
@@ -0,0 +1,20 @@ | |||
# Minimal makefile for Sphinx documentation | |||
# | |||
# You can set these variables from the command line. | |||
SPHINXOPTS = | |||
SPHINXBUILD = sphinx-build | |||
SPHINXPROJ = Lark | |||
SOURCEDIR = . | |||
BUILDDIR = _build | |||
# Put it first so that "make" without argument is like "make help". | |||
help: | |||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) | |||
.PHONY: help Makefile | |||
# Catch-all target: route all unknown targets to Sphinx using the new | |||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). | |||
%: Makefile | |||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) |
@@ -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">→</span></a></li> | |||
</ul> | |||
</nav> | |||
<footer class="site-footer"> | |||
<div class="container"> | |||
<p class="powered-by"> | |||
© 2016 Bram van der Sanden · | |||
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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -0,0 +1,92 @@ | |||
API Reference | |||
============= | |||
Lark | |||
---- | |||
.. autoclass:: lark.Lark | |||
:members: open, parse, parse_interactive, lex, save, load, get_terminal, open_from_package | |||
Using Unicode character classes with ``regex`` | |||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |||
Python's builtin ``re`` module has a few persistent known bugs and also won't parse | |||
advanced regex features such as character classes. | |||
With ``pip install lark[regex]``, the ``regex`` module will be | |||
installed alongside lark and can act as a drop-in replacement to ``re``. | |||
Any instance of Lark instantiated with ``regex=True`` will use the ``regex`` module instead of ``re``. | |||
For example, we can use character classes to match PEP-3131 compliant Python identifiers: | |||
:: | |||
from lark import Lark | |||
>>> g = Lark(r""" | |||
?start: NAME | |||
NAME: ID_START ID_CONTINUE* | |||
ID_START: /[\p{Lu}\p{Ll}\p{Lt}\p{Lm}\p{Lo}\p{Nl}_]+/ | |||
ID_CONTINUE: ID_START | /[\p{Mn}\p{Mc}\p{Nd}\p{Pc}·]+/ | |||
""", regex=True) | |||
>>> g.parse('வணக்கம்') | |||
'வணக்கம்' | |||
Tree | |||
---- | |||
.. autoclass:: lark.Tree | |||
:members: pretty, find_pred, find_data, iter_subtrees, scan_values, | |||
iter_subtrees_topdown | |||
Token | |||
----- | |||
.. autoclass:: lark.Token | |||
Transformer, Visitor & Interpreter | |||
---------------------------------- | |||
See :doc:`visitors`. | |||
ForestVisitor, ForestTransformer, & TreeForestTransformer | |||
----------------------------------------------------------- | |||
See :doc:`forest`. | |||
UnexpectedInput | |||
--------------- | |||
.. autoclass:: lark.exceptions.UnexpectedInput | |||
:members: get_context, match_examples | |||
.. autoclass:: lark.exceptions.UnexpectedToken | |||
.. autoclass:: lark.exceptions.UnexpectedCharacters | |||
.. autoclass:: lark.exceptions.UnexpectedEOF | |||
InteractiveParser | |||
----------------- | |||
.. autoclass:: lark.parsers.lalr_interactive_parser.InteractiveParser | |||
:members: choices, feed_token, copy, pretty, resume_parse, exhaust_lexer, accepts, as_immutable | |||
.. autoclass:: lark.parsers.lalr_interactive_parser.ImmutableInteractiveParser | |||
:members: choices, feed_token, copy, pretty, resume_parse, exhaust_lexer, accepts, as_mutable | |||
ast_utils | |||
--------- | |||
For an example of using ``ast_utils``, see `/examples/advanced/create_ast.py`_ | |||
.. autoclass:: lark.ast_utils.Ast | |||
.. autoclass:: lark.ast_utils.AsList | |||
.. autofunction:: lark.ast_utils.create_transformer | |||
.. _/examples/advanced/create_ast.py: examples/advanced/create_ast.html |
@@ -0,0 +1,185 @@ | |||
#!/usr/bin/env python3 | |||
# -*- coding: utf-8 -*- | |||
# | |||
# Lark documentation build configuration file, created by | |||
# sphinx-quickstart on Sun Aug 16 13:09:41 2020. | |||
# | |||
# This file is execfile()d with the current directory set to its | |||
# containing dir. | |||
# | |||
# Note that not all possible configuration values are present in this | |||
# autogenerated file. | |||
# | |||
# All configuration values have a default; values that are commented out | |||
# serve to show the default. | |||
# If extensions (or modules to document with autodoc) are in another directory, | |||
# add these directories to sys.path here. If the directory is relative to the | |||
# documentation root, use os.path.abspath to make it absolute, like shown here. | |||
# | |||
import os | |||
import sys | |||
sys.path.insert(0, os.path.abspath('..')) | |||
autodoc_member_order = 'bysource' | |||
# -- General configuration ------------------------------------------------ | |||
# If your documentation needs a minimal Sphinx version, state it here. | |||
# | |||
# needs_sphinx = '1.0' | |||
# Add any Sphinx extension module names here, as strings. They can be | |||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom | |||
# ones. | |||
extensions = [ | |||
'sphinx.ext.autodoc', | |||
'sphinx.ext.napoleon', | |||
'sphinx.ext.coverage', | |||
'recommonmark', | |||
'sphinx_markdown_tables', | |||
'sphinx_gallery.gen_gallery' | |||
] | |||
# Add any paths that contain templates here, relative to this directory. | |||
templates_path = ['_templates'] | |||
# The suffix(es) of source filenames. | |||
# You can specify multiple suffix as a list of string: | |||
# | |||
# source_suffix = ['.rst', '.md'] | |||
source_suffix = { | |||
'.rst': 'restructuredtext', | |||
'.md': 'markdown' | |||
} | |||
# The master toctree document. | |||
master_doc = 'index' | |||
# General information about the project. | |||
project = 'Lark' | |||
copyright = '2020, Erez Shinan' | |||
author = 'Erez Shinan' | |||
# The version info for the project you're documenting, acts as replacement for | |||
# |version| and |release|, also used in various other places throughout the | |||
# built documents. | |||
# | |||
# The short X.Y version. | |||
version = '' | |||
# The full version, including alpha/beta/rc tags. | |||
release = '' | |||
# The language for content autogenerated by Sphinx. Refer to documentation | |||
# for a list of supported languages. | |||
# | |||
# This is also used if you do content translation via gettext catalogs. | |||
# Usually you set "language" from the command line for these cases. | |||
language = None | |||
# List of patterns, relative to source directory, that match files and | |||
# directories to ignore when looking for source files. | |||
# This patterns also effect to html_static_path and html_extra_path | |||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] | |||
# The name of the Pygments (syntax highlighting) style to use. | |||
pygments_style = 'sphinx' | |||
# If true, `todo` and `todoList` produce output, else they produce nothing. | |||
todo_include_todos = False | |||
# -- Options for HTML output ---------------------------------------------- | |||
# The theme to use for HTML and HTML Help pages. See the documentation for | |||
# a list of builtin themes. | |||
# | |||
html_theme = 'sphinx_rtd_theme' | |||
# Theme options are theme-specific and customize the look and feel of a theme | |||
# further. For a list of options available for each theme, see the | |||
# documentation. | |||
# | |||
# html_theme_options = {} | |||
# Add any paths that contain custom static files (such as style sheets) here, | |||
# relative to this directory. They are copied after the builtin static files, | |||
# so a file named "default.css" will overwrite the builtin "default.css". | |||
html_static_path = ['_static'] | |||
# Custom sidebar templates, must be a dictionary that maps document names | |||
# to template names. | |||
# | |||
# This is required for the alabaster theme | |||
# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars | |||
html_sidebars = { | |||
'**': [ | |||
'relations.html', # needs 'show_related': True theme option to display | |||
'searchbox.html', | |||
] | |||
} | |||
# -- Options for HTMLHelp output ------------------------------------------ | |||
# Output file base name for HTML help builder. | |||
htmlhelp_basename = 'Larkdoc' | |||
# -- Options for LaTeX output --------------------------------------------- | |||
latex_elements = { | |||
# The paper size ('letterpaper' or 'a4paper'). | |||
# | |||
# 'papersize': 'letterpaper', | |||
# The font size ('10pt', '11pt' or '12pt'). | |||
# | |||
# 'pointsize': '10pt', | |||
# Additional stuff for the LaTeX preamble. | |||
# | |||
# 'preamble': '', | |||
# Latex figure (float) alignment | |||
# | |||
# 'figure_align': 'htbp', | |||
} | |||
# Grouping the document tree into LaTeX files. List of tuples | |||
# (source start file, target name, title, | |||
# author, documentclass [howto, manual, or own class]). | |||
latex_documents = [ | |||
(master_doc, 'Lark.tex', 'Lark Documentation', | |||
'Erez Shinan', 'manual'), | |||
] | |||
# -- Options for manual page output --------------------------------------- | |||
# One entry per manual page. List of tuples | |||
# (source start file, name, description, authors, manual section). | |||
man_pages = [ | |||
(master_doc, 'lark', 'Lark Documentation', | |||
[author], 7) | |||
] | |||
# -- Options for Texinfo output ------------------------------------------- | |||
# Grouping the document tree into Texinfo files. List of tuples | |||
# (source start file, target name, title, author, | |||
# dir menu entry, description, category) | |||
texinfo_documents = [ | |||
(master_doc, 'Lark', 'Lark Documentation', | |||
author, 'Lark', 'One line description of project.', | |||
'Miscellaneous'), | |||
] | |||
# -- Sphinx gallery config ------------------------------------------- | |||
sphinx_gallery_conf = { | |||
'examples_dirs': ['../examples'], | |||
'gallery_dirs': ['examples'], | |||
} |
@@ -0,0 +1,39 @@ | |||
# Features | |||
## Main Features | |||
- Earley parser, capable of parsing any context-free grammar | |||
- Implements SPPF, for efficient parsing and storing of ambiguous grammars. | |||
- LALR(1) parser, limited in power of expression, but very efficient in space and performance (O(n)). | |||
- Implements a parse-aware lexer that provides a better power of expression than traditional LALR implementations (such as ply). | |||
- EBNF-inspired grammar, with extra features (See: [Grammar Reference](grammar.md)) | |||
- Builds a parse-tree (AST) automagically based on the grammar | |||
- Stand-alone parser generator - create a small independent parser to embed in your project. ([read more](tools.html#stand-alone-parser)) | |||
- Flexible error handling by using an interactive parser interface (LALR only) | |||
- Automatic line & column tracking (for both tokens and matched rules) | |||
- Automatic terminal collision resolution | |||
- Standard library of terminals (strings, numbers, names, etc.) | |||
- Unicode fully supported | |||
- Extensive test suite | |||
- MyPy support using type stubs | |||
- Python 2 & Python 3 compatible | |||
- Pure-Python implementation | |||
[Read more about the parsers](parsers.md) | |||
## Extra features | |||
- Import rules and tokens from other Lark grammars, for code reuse and modularity. | |||
- Support for external regex module ([see here](classes.html#using-unicode-character-classes-with-regex)) | |||
- Import grammars from Nearley.js ([read more](tools.html#importing-grammars-from-nearleyjs)) | |||
- CYK parser | |||
- Visualize your parse trees as dot or png files ([see_example](https://github.com/lark-parser/lark/blob/master/examples/fruitflies.py)) | |||
### Experimental features | |||
- Automatic reconstruction of input from parse-tree (see examples) | |||
### Planned features (not implemented yet) | |||
- Generate code in other languages than Python | |||
- Grammar composition | |||
- LALR(k) parser | |||
- Full regexp-collision support using NFAs |
@@ -0,0 +1,65 @@ | |||
Working with the SPPF | |||
===================== | |||
When parsing with Earley, Lark provides the ``ambiguity='forest'`` option | |||
to obtain the shared packed parse forest (SPPF) produced by the parser as | |||
an alternative to it being automatically converted to a tree. | |||
Lark provides a few tools to facilitate working with the SPPF. Here are some | |||
things to consider when deciding whether or not to use the SPPF. | |||
**Pros** | |||
- Efficient storage of highly ambiguous parses | |||
- Precise handling of ambiguities | |||
- Custom rule prioritizers | |||
- Ability to handle infinite ambiguities | |||
- Directly transform forest -> object instead of forest -> tree -> object | |||
**Cons** | |||
- More complex than working with a tree | |||
- SPPF may contain nodes corresponding to rules generated internally | |||
- Loss of Lark grammar features: | |||
- Rules starting with '_' are not inlined in the SPPF | |||
- Rules starting with '?' are never inlined in the SPPF | |||
- All tokens will appear in the SPPF | |||
SymbolNode | |||
---------- | |||
.. autoclass:: lark.parsers.earley_forest.SymbolNode | |||
:members: is_ambiguous, children | |||
PackedNode | |||
---------- | |||
.. autoclass:: lark.parsers.earley_forest.PackedNode | |||
:members: children | |||
ForestVisitor | |||
------------- | |||
.. autoclass:: lark.parsers.earley_forest.ForestVisitor | |||
:members: visit, visit_symbol_node_in, visit_symbol_node_out, | |||
visit_packed_node_in, visit_packed_node_out, | |||
visit_token_node, on_cycle, get_cycle_in_path | |||
ForestTransformer | |||
----------------- | |||
.. autoclass:: lark.parsers.earley_forest.ForestTransformer | |||
:members: transform, transform_symbol_node, transform_intermediate_node, | |||
transform_packed_node, transform_token_node | |||
TreeForestTransformer | |||
--------------------- | |||
.. autoclass:: lark.parsers.earley_forest.TreeForestTransformer | |||
:members: __default__, __default_token__, __default_ambig__ | |||
handles_ambiguity | |||
----------------- | |||
.. autofunction:: lark.parsers.earley_forest.handles_ambiguity |
@@ -0,0 +1,324 @@ | |||
# Grammar Reference | |||
## Definitions | |||
A **grammar** is a list of rules and terminals, that together define a language. | |||
Terminals define the alphabet of the language, while rules define its structure. | |||
In Lark, a terminal may be a string, a regular expression, or a concatenation of these and other terminals. | |||
Each rule is a list of terminals and rules, whose location and nesting define the structure of the resulting parse-tree. | |||
A **parsing algorithm** is an algorithm that takes a grammar definition and a sequence of symbols (members of the alphabet), and matches the entirety of the sequence by searching for a structure that is allowed by the grammar. | |||
### General Syntax and notes | |||
Grammars in Lark are based on [EBNF](https://en.wikipedia.org/wiki/Extended_Backus–Naur_form) syntax, with several enhancements. | |||
EBNF is basically a short-hand for common BNF patterns. | |||
Optionals are expanded: | |||
```ebnf | |||
a b? c -> (a c | a b c) | |||
``` | |||
Repetition is extracted into a recursion: | |||
```ebnf | |||
a: b* -> a: _b_tag | |||
_b_tag: (_b_tag b)? | |||
``` | |||
And so on. | |||
Lark grammars are composed of a list of definitions and directives, each on its own line. A definition is either a named rule, or a named terminal, with the following syntax, respectively: | |||
```c | |||
rule: <EBNF EXPRESSION> | |||
| etc. | |||
TERM: <EBNF EXPRESSION> // Rules aren't allowed | |||
``` | |||
**Comments** start with `//` and last to the end of the line (C++ style) | |||
Lark begins the parse with the rule 'start', unless specified otherwise in the options. | |||
Names of rules are always in lowercase, while names of terminals are always in uppercase. This distinction has practical effects, for the shape of the generated parse-tree, and the automatic construction of the lexer (aka tokenizer, or scanner). | |||
## Terminals | |||
Terminals are used to match text into symbols. They can be defined as a combination of literals and other terminals. | |||
**Syntax:** | |||
```html | |||
<NAME> [. <priority>] : <literals-and-or-terminals> | |||
``` | |||
Terminal names must be uppercase. | |||
Literals can be one of: | |||
* `"string"` | |||
* `/regular expression+/` | |||
* `"case-insensitive string"i` | |||
* `/re with flags/imulx` | |||
* Literal range: `"a".."z"`, `"1".."9"`, etc. | |||
Terminals also support grammar operators, such as `|`, `+`, `*` and `?`. | |||
Terminals are a linear construct, and therefore may not contain themselves (recursion isn't allowed). | |||
### Templates | |||
Templates are expanded when preprocessing the grammar. | |||
Definition syntax: | |||
```ebnf | |||
my_template{param1, param2, ...}: <EBNF EXPRESSION> | |||
``` | |||
Use syntax: | |||
```ebnf | |||
some_rule: my_template{arg1, arg2, ...} | |||
``` | |||
Example: | |||
```ebnf | |||
_separated{x, sep}: x (sep x)* // Define a sequence of 'x sep x sep x ...' | |||
num_list: "[" _separated{NUMBER, ","} "]" // Will match "[1, 2, 3]" etc. | |||
``` | |||
### Priority | |||
Terminals can be assigned priority only when using a lexer (future versions may support Earley's dynamic lexing). | |||
Priority can be either positive or negative. If not specified for a terminal, it defaults to 1. | |||
Highest priority terminals are always matched first. | |||
### Regexp Flags | |||
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: `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+ | |||
#### Notes for when using a lexer: | |||
When using a lexer (basic or contextual), it is the grammar-author's responsibility to make sure the literals don't collide, or that if they do, they are matched in the desired order. Literals are matched according to the following precedence: | |||
1. Highest priority first (priority is specified as: TERM.number: ...) | |||
2. Length of match (for regexps, the longest theoretical match is used) | |||
3. Length of literal / pattern definition | |||
4. Name | |||
**Examples:** | |||
```perl | |||
IF: "if" | |||
INTEGER : /[0-9]+/ | |||
INTEGER2 : ("0".."9")+ //# Same as INTEGER | |||
DECIMAL.2: INTEGER? "." INTEGER //# Will be matched before INTEGER | |||
WHITESPACE: (" " | /\t/ )+ | |||
SQL_SELECT: "select"i | |||
``` | |||
### Regular expressions & Ambiguity | |||
Each terminal is eventually compiled to a regular expression. All the operators and references inside it are mapped to their respective expressions. | |||
For example, in the following grammar, `A1` and `A2`, are equivalent: | |||
```perl | |||
A1: "a" | "b" | |||
A2: /a|b/ | |||
``` | |||
This means that inside terminals, Lark cannot detect or resolve ambiguity, even when using Earley. | |||
For example, for this grammar: | |||
```perl | |||
start : (A | B)+ | |||
A : "a" | "ab" | |||
B : "b" | |||
``` | |||
We get only one possible derivation, instead of two: | |||
```bash | |||
>>> p = Lark(g, ambiguity="explicit") | |||
>>> p.parse("ab") | |||
Tree('start', [Token('A', 'ab')]) | |||
``` | |||
This is happening because Python's regex engine always returns the best matching option. There is no way to access the alternatives. | |||
If you find yourself in this situation, the recommended solution is to use rules instead. | |||
Example: | |||
```python | |||
>>> p = Lark("""start: (a | b)+ | |||
... !a: "a" | "ab" | |||
... !b: "b" | |||
... """, ambiguity="explicit") | |||
>>> print(p.parse("ab").pretty()) | |||
_ambig | |||
start | |||
a ab | |||
start | |||
a a | |||
b b | |||
``` | |||
## Rules | |||
**Syntax:** | |||
```html | |||
<name> : <items-to-match> [-> <alias> ] | |||
| ... | |||
``` | |||
Names of rules and aliases are always in lowercase. | |||
Rule definitions can be extended to the next line by using the OR operator (signified by a pipe: `|` ). | |||
An alias is a name for the specific rule alternative. It affects tree construction. | |||
Each item is one of: | |||
* `rule` | |||
* `TERMINAL` | |||
* `"string literal"` or `/regexp literal/` | |||
* `(item item ..)` - Group items | |||
* `[item item ..]` - Maybe. Same as `(item item ..)?`, but when `maybe_placeholders=True`, generates `None` if there is no match. | |||
* `item?` - Zero or one instances of item ("maybe") | |||
* `item*` - Zero or more instances of item | |||
* `item+` - One or more instances of item | |||
* `item ~ n` - Exactly *n* instances of item | |||
* `item ~ n..m` - Between *n* to *m* instances of item (not recommended for wide ranges, due to performance issues) | |||
**Examples:** | |||
```perl | |||
hello_world: "hello" "world" | |||
mul: (mul "*")? number //# Left-recursion is allowed and encouraged! | |||
expr: expr operator expr | |||
| value //# Multi-line, belongs to expr | |||
four_words: word ~ 4 | |||
``` | |||
### Priority | |||
Rules can be assigned priority only when using Earley (future versions may support LALR as well). | |||
Priority can be either positive or negative. In not specified for a terminal, it's assumed to be 1 (i.e. the default). | |||
<a name="dirs"></a> | |||
## Directives | |||
### %ignore | |||
All occurrences of the terminal will be ignored, and won't be part of the parse. | |||
Using the `%ignore` directive results in a cleaner grammar. | |||
It's especially important for the LALR(1) algorithm, because adding whitespace (or comments, or other extraneous elements) explicitly in the grammar, harms its predictive abilities, which are based on a lookahead of 1. | |||
**Syntax:** | |||
```html | |||
%ignore <TERMINAL> | |||
``` | |||
**Examples:** | |||
```perl | |||
%ignore " " | |||
COMMENT: "#" /[^\n]/* | |||
%ignore COMMENT | |||
``` | |||
### %import | |||
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). | |||
**Syntax:** | |||
```html | |||
%import <module>.<TERMINAL> | |||
%import <module>.<rule> | |||
%import <module>.<TERMINAL> -> <NEWTERMINAL> | |||
%import <module>.<rule> -> <newrule> | |||
%import <module> (<TERM1>, <TERM2>, <rule1>, <rule2>) | |||
``` | |||
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. | |||
The rule or terminal can be imported under another name with the `->` syntax. | |||
**Example:** | |||
```perl | |||
%import common.NUMBER | |||
%import .terminals_file (A, B, C) | |||
%import .rules_file.rulea -> ruleb | |||
``` | |||
Note that `%ignore` directives cannot be imported. Imported rules will abide by the `%ignore` directives declared in the main grammar. | |||
### %declare | |||
Declare a terminal without defining it. Useful for plugins. | |||
### %override | |||
Override a rule or terminals, affecting all references to it, even in imported grammars. | |||
Useful for implementing an inheritance pattern when importing grammars. | |||
**Example:** | |||
```perl | |||
%import my_grammar (start, number, NUMBER) | |||
// Add hex support to my_grammar | |||
%override number: NUMBER | /0x\w+/ | |||
``` | |||
### %extend | |||
Extend the definition of a rule or terminal, e.g. add a new option on what it can match, like when separated with `|`. | |||
Useful for splitting up a definition of a complex rule with many different options over multiple files. | |||
Can also be used to implement a plugin system where a core grammar is extended by others. | |||
**Example:** | |||
```perl | |||
%import my_grammar (start, NUMBER) | |||
// Add hex support to my_grammar | |||
%extend NUMBER: /0x\w+/ | |||
``` | |||
For both `%extend` and `%override`, there is not requirement for a rule/terminal to come from another file, but that is probably the most common usecase |
@@ -0,0 +1,63 @@ | |||
# How to develop Lark - Guide | |||
There are many ways you can help the project: | |||
* Help solve issues | |||
* Improve the documentation | |||
* Write new grammars for Lark's library | |||
* Write a blog post introducing Lark to your audience | |||
* Port Lark to another language | |||
* Help me with code development | |||
If you're interested in taking one of these on, let me know and I will provide more details and assist you in the process. | |||
## Unit Tests | |||
Lark comes with an extensive set of tests. Many of the tests will run several times, once for each parser configuration. | |||
To run the tests, just go to the lark project root, and run the command: | |||
```bash | |||
python -m tests | |||
``` | |||
or | |||
```bash | |||
pypy -m tests | |||
``` | |||
For a list of supported interpreters, you can consult the `tox.ini` file. | |||
You can also run a single unittest using its class and method name, for example: | |||
```bash | |||
## test_package test_class_name.test_function_name | |||
python -m tests TestLalrBasic.test_keep_all_tokens | |||
``` | |||
### tox | |||
To run all Unit Tests with tox, | |||
install tox and Python 2.7 up to the latest python interpreter supported (consult the file tox.ini). | |||
Then, | |||
run the command `tox` on the root of this project (where the main setup.py file is on). | |||
And, for example, | |||
if you would like to only run the Unit Tests for Python version 2.7, | |||
you can run the command `tox -e py27` | |||
### pytest | |||
You can also run the tests using pytest: | |||
```bash | |||
pytest tests | |||
``` | |||
### Using setup.py | |||
Another way to run the tests is using setup.py: | |||
```bash | |||
python setup.py test | |||
``` |
@@ -0,0 +1,83 @@ | |||
# How To Use Lark - Guide | |||
## Work process | |||
This is the recommended process for working with Lark: | |||
1. Collect or create input samples, that demonstrate key features or behaviors in the language you're trying to parse. | |||
2. Write a grammar. Try to aim for a structure that is intuitive, and in a way that imitates how you would explain your language to a fellow human. | |||
3. Try your grammar in Lark against each input sample. Make sure the resulting parse-trees make sense. | |||
4. Use Lark's grammar features to [shape the tree](tree_construction.md): Get rid of superfluous rules by inlining them, and use aliases when specific cases need clarification. | |||
- You can perform steps 1-4 repeatedly, gradually growing your grammar to include more sentences. | |||
5. Create a transformer to evaluate the parse-tree into a structure you'll be comfortable to work with. This may include evaluating literals, merging branches, or even converting the entire tree into your own set of AST classes. | |||
Of course, some specific use-cases may deviate from this process. Feel free to suggest these cases, and I'll add them to this page. | |||
## Getting started | |||
Browse the [Examples](https://github.com/lark-parser/lark/tree/master/examples) to find a template that suits your purposes. | |||
Read the tutorials to get a better understanding of how everything works. (links in the [main page](/index)) | |||
Use the [Cheatsheet (PDF)](https://lark-parser.readthedocs.io/en/latest/_static/lark_cheatsheet.pdf) for quick reference. | |||
Use the reference pages for more in-depth explanations. (links in the [main page](/index)) | |||
## Debug | |||
Grammars may contain non-obvious bugs, usually caused by rules or terminals interfering with each other in subtle ways. | |||
When trying to debug a misbehaving grammar, the following methodology is recommended: | |||
1. Create a copy of the grammar, so you can change the parser/grammar without any worries | |||
2. Find the minimal input that creates the error | |||
3. Slowly remove rules from the grammar, while making sure the error still occurs. | |||
Usually, by the time you get to a minimal grammar, the problem becomes clear. | |||
But if it doesn't, feel free to ask us on gitter, or even open an issue. Post a reproducing code, with the minimal grammar and input, and we'll do our best to help. | |||
### LALR | |||
By default Lark silently resolves Shift/Reduce conflicts as Shift. To enable warnings pass `debug=True`. To get the messages printed you have to configure the `logger` beforehand. For example: | |||
```python | |||
import logging | |||
from lark import Lark, logger | |||
logger.setLevel(logging.DEBUG) | |||
collision_grammar = ''' | |||
start: as as | |||
as: a* | |||
a: "a" | |||
''' | |||
p = Lark(collision_grammar, parser='lalr', debug=True) | |||
``` | |||
## Tools | |||
### Stand-alone parser | |||
Lark can generate a stand-alone LALR(1) parser from a grammar. | |||
The resulting module provides the same interface as Lark, but with a fixed grammar, and reduced functionality. | |||
Run using: | |||
```bash | |||
python -m lark.tools.standalone | |||
``` | |||
For a play-by-play, read the [tutorial](http://blog.erezsh.com/create-a-stand-alone-lalr1-parser-in-python/) | |||
### Import Nearley.js grammars | |||
It is possible to import Nearley grammars into Lark. The Javascript code is translated using Js2Py. | |||
See the [tools page](tools.md) for more information. |
@@ -0,0 +1,101 @@ | |||
<!doctype html> | |||
<html> | |||
<head> | |||
<meta charset="UTF-8"> | |||
<!-- flip comment below to use local pyodide --> | |||
<script src="https://pyodide-cdn2.iodide.io/v0.15.0/full/pyodide.js"></script> | |||
<!-- <script src="./pyodide/pyodide.js"></script> --> | |||
<link rel="stylesheet" href="https://unpkg.com/purecss@1.0.1/build/base-min.css"> | |||
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@500&display=swap" rel="stylesheet"> | |||
<script src="app.js"></script> | |||
<style> | |||
.is-loading:after { | |||
background-image: url(is-loading.gif); | |||
background-position: center 35%; | |||
background-repeat: no-repeat; | |||
background-color: hsla(0, 0%, 100%, .6); | |||
position: absolute; | |||
z-index: 700; | |||
content: " "; | |||
width: 100%; | |||
height: 100%; | |||
display: block; | |||
left: 0; | |||
right: 0; | |||
top: 0; | |||
bottom: 0 | |||
} | |||
h1 { | |||
text-align: center; | |||
} | |||
textarea, select, body > div > ul { | |||
/* display: block; | |||
margin: 15px auto; | |||
width: 90%; | |||
font-weight: bold; | |||
color: #052569; */ | |||
font-family: 'Inconsolata', monospace; | |||
} | |||
textarea { | |||
margin: 10px; | |||
width: 90%; | |||
padding: 10px; | |||
font-size: 1.4em; | |||
} | |||
#grammar { | |||
min-height: 300px; | |||
} | |||
#input { | |||
min-height: 100px; | |||
} | |||
ul ul { | |||
border-left: 1px dotted silver; | |||
margin-left: -16px; | |||
} | |||
li { | |||
list-style: circle; | |||
margin-left: 10px; | |||
} | |||
select { | |||
padding: 5px; | |||
} | |||
#inputs { | |||
text-align: center; | |||
} | |||
#result { | |||
display: flex; | |||
justify-content: center; | |||
} | |||
#result > ul { | |||
margin: 10px; | |||
width: 90%; | |||
padding: 10px; | |||
font-size: 1.2em; | |||
} | |||
menu { | |||
display: flex; | |||
} | |||
main { | |||
margin: auto; | |||
} | |||
</style> | |||
</head> | |||
<body class="is-loading"> | |||
</body> | |||
</html> |
@@ -0,0 +1,105 @@ | |||
class app { | |||
constructor(modules, invocation){ | |||
languagePluginLoader.then(() => { | |||
// If you don't require for pre-loaded Python packages, remove this promise below. | |||
window.pyodide.runPythonAsync("import setuptools, micropip").then(()=>{ | |||
window.pyodide.runPythonAsync("micropip.install('lark-parser')").then(()=>{ | |||
this.fetchSources(modules).then(() => { | |||
window.pyodide.runPythonAsync("import " + Object.keys(modules).join("\nimport ") + "\n" + invocation + "\n").then(() => this.initializingComplete()); | |||
}); | |||
}); | |||
}); | |||
}); | |||
} | |||
loadSources(module, baseURL, files) { | |||
let promises = []; | |||
for (let f in files) { | |||
promises.push( | |||
new Promise((resolve, reject) => { | |||
let file = files[f]; | |||
let url = (baseURL ? baseURL + "/" : "") + file; | |||
fetch(url, {}).then((response) => { | |||
if (response.status === 200) | |||
return response.text().then((code) => { | |||
let path = ("/lib/python3.7/site-packages/" + module + "/" + file).split("/"); | |||
let lookup = ""; | |||
for (let i in path) { | |||
if (!path[i]) { | |||
continue; | |||
} | |||
lookup += (lookup ? "/" : "") + path[i]; | |||
if (parseInt(i) === path.length - 1) { | |||
window.pyodide._module.FS.writeFile(lookup, code); | |||
console.debug(`fetched ${lookup}`); | |||
} else { | |||
try { | |||
window.pyodide._module.FS.lookupPath(lookup); | |||
} catch { | |||
window.pyodide._module.FS.mkdir(lookup); | |||
console.debug(`created ${lookup}`); | |||
} | |||
} | |||
} | |||
resolve(); | |||
}); | |||
else | |||
reject(); | |||
}); | |||
}) | |||
); | |||
} | |||
return Promise.all(promises); | |||
} | |||
fetchSources(modules) { | |||
let promises = []; | |||
for( let module of Object.keys(modules) ) | |||
{ | |||
promises.push( | |||
new Promise((resolve, reject) => { | |||
fetch(`${modules[module]}/files.json`, {}).then((response) => { | |||
if (response.status === 200) { | |||
response.text().then((list) => { | |||
let files = JSON.parse(list); | |||
this.loadSources(module, modules[module], files).then(() => { | |||
resolve(); | |||
}) | |||
}) | |||
} else { | |||
reject(); | |||
} | |||
}) | |||
})); | |||
} | |||
return Promise.all(promises).then(() => { | |||
for( let module of Object.keys(modules) ) { | |||
window.pyodide.loadedPackages[module] = "default channel"; | |||
} | |||
window.pyodide.runPython( | |||
'import importlib as _importlib\n' + | |||
'_importlib.invalidate_caches()\n' | |||
); | |||
}); | |||
} | |||
initializingComplete() { | |||
document.body.classList.remove("is-loading") | |||
} | |||
} | |||
(function () { | |||
window.top.app = new app({"app": "app"}, "import app.app; app.app.start()"); | |||
})(); |
@@ -0,0 +1,83 @@ | |||
from . import html5 | |||
from .examples import examples | |||
from lark import Lark | |||
from lark.tree import Tree | |||
class App(html5.Div): | |||
def __init__(self): | |||
super().__init__(""" | |||
<h1> | |||
<img src="lark-logo.png"> IDE | |||
</h1> | |||
<main> | |||
<menu> | |||
<select [name]="examples"> | |||
<option disabled selected>Examples</option> | |||
</select> | |||
<select [name]="parser"> | |||
<option value="earley" selected>Earley (default)</option> | |||
<option value="lalr">LALR</option> | |||
<option value="cyk">CYK</option> | |||
</select> | |||
</menu> | |||
<div id="inputs"> | |||
<div> | |||
<div>Grammar:</div> | |||
<textarea [name]="grammar" id="grammar" placeholder="Lark Grammar..."></textarea> | |||
</div> | |||
<div> | |||
<div>Input:</div> | |||
<textarea [name]="input" id="input" placeholder="Parser input..."></textarea> | |||
</div> | |||
</div> | |||
<div id="result"> | |||
<ul [name]="ast" /> | |||
</div> | |||
</main> | |||
""") | |||
self.sinkEvent("onKeyUp", "onChange") | |||
self.parser = "earley" | |||
# Pre-load examples | |||
for name, (grammar, input) in examples.items(): | |||
option = html5.Option(name) | |||
option.grammar = grammar | |||
option.input = input | |||
self.examples.appendChild(option) | |||
def onChange(self, e): | |||
if html5.utils.doesEventHitWidgetOrChildren(e, self.examples): | |||
example = self.examples.children(self.examples["selectedIndex"]) | |||
self.grammar["value"] = example.grammar.strip() | |||
self.input["value"] = example.input.strip() | |||
self.onKeyUp() | |||
elif html5.utils.doesEventHitWidgetOrChildren(e, self.parser): | |||
self.parser = self.parser.children(self.parser["selectedIndex"])["value"] | |||
self.onKeyUp() | |||
def onKeyUp(self, e=None): | |||
l = Lark(self.grammar["value"], parser=self.parser) | |||
try: | |||
ast = l.parse(self.input["value"]) | |||
except Exception as e: | |||
self.ast.appendChild( | |||
html5.Li(str(e)), replace=True | |||
) | |||
print(ast) | |||
traverse = lambda node: html5.Li([node.data, html5.Ul([traverse(c) for c in node.children])] if isinstance(node, Tree) else node) | |||
self.ast.appendChild(traverse(ast), replace=True) | |||
def start(): | |||
html5.Body().appendChild( | |||
App() | |||
) | |||
@@ -0,0 +1,150 @@ | |||
# Examples formattet this way: | |||
# "name": ("grammar", "demo-input") | |||
examples = { | |||
# --- hello.lark --- | |||
"hello.lark": (""" | |||
start: WORD "," WORD "!" | |||
%import common.WORD // imports from terminal library | |||
%ignore " " // Disregard spaces in text | |||
""", "Hello, World!"), | |||
# --- calc.lark --- | |||
"calc.lark": (""" | |||
?start: sum | |||
| NAME "=" sum -> assign_var | |||
?sum: product | |||
| sum "+" product -> add | |||
| sum "-" product -> sub | |||
?product: atom | |||
| product "*" atom -> mul | |||
| product "/" atom -> div | |||
?atom: NUMBER -> number | |||
| "-" atom -> neg | |||
| NAME -> var | |||
| "(" sum ")" | |||
%import common.CNAME -> NAME | |||
%import common.NUMBER | |||
%import common.WS_INLINE | |||
%ignore WS_INLINE""", | |||
"1 + 2 * 3 + 4"), | |||
# --- json.lark --- | |||
"json.lark": (""" | |||
?start: value | |||
?value: object | |||
| array | |||
| string | |||
| SIGNED_NUMBER -> number | |||
| "true" -> true | |||
| "false" -> false | |||
| "null" -> null | |||
array : "[" [value ("," value)*] "]" | |||
object : "{" [pair ("," pair)*] "}" | |||
pair : string ":" value | |||
string : ESCAPED_STRING | |||
%import common.ESCAPED_STRING | |||
%import common.SIGNED_NUMBER | |||
%import common.WS | |||
%ignore WS""", | |||
""" | |||
[ | |||
{ | |||
"_id": "5edb875cf3d764da55602437", | |||
"index": 0, | |||
"guid": "3dae2206-5d4d-41fe-b81d-dc8cdba7acaa", | |||
"isActive": false, | |||
"balance": "$2,872.54", | |||
"picture": "http://placehold.it/32x32", | |||
"age": 24, | |||
"eyeColor": "blue", | |||
"name": "Theresa Vargas", | |||
"gender": "female", | |||
"company": "GEEKOL", | |||
"email": "theresavargas@geekol.com", | |||
"phone": "+1 (930) 450-3445", | |||
"address": "418 Herbert Street, Sexton, Florida, 1375", | |||
"about": "Id minim deserunt laborum enim. Veniam commodo incididunt amet aute esse duis veniam occaecat nulla esse aute et deserunt eiusmod. Anim elit ullamco minim magna sint laboris. Est consequat quis deserunt excepteur in magna pariatur laborum quis eu. Ex quis tempor elit qui qui et culpa sunt sit esse mollit cupidatat. Fugiat cillum deserunt enim minim irure reprehenderit est. Voluptate nisi quis amet quis incididunt pariatur nostrud Lorem consectetur adipisicing voluptate.\\r\\n", | |||
"registered": "2016-11-19T01:02:42 -01:00", | |||
"latitude": -25.65267, | |||
"longitude": 104.19531, | |||
"tags": [ | |||
"eiusmod", | |||
"reprehenderit", | |||
"anim", | |||
"sunt", | |||
"esse", | |||
"proident", | |||
"esse" | |||
], | |||
"friends": [ | |||
{ | |||
"id": 0, | |||
"name": "Roth Herrera" | |||
}, | |||
{ | |||
"id": 1, | |||
"name": "Callie Christian" | |||
}, | |||
{ | |||
"id": 2, | |||
"name": "Gracie Whitfield" | |||
} | |||
], | |||
"greeting": "Hello, Theresa Vargas! You have 6 unread messages.", | |||
"favoriteFruit": "banana" | |||
}, | |||
{ | |||
"_id": "5edb875c845eb08161a83e64", | |||
"index": 1, | |||
"guid": "a8ada2c1-e2c7-40d3-96b4-52c93baff7f0", | |||
"isActive": false, | |||
"balance": "$2,717.04", | |||
"picture": "http://placehold.it/32x32", | |||
"age": 23, | |||
"eyeColor": "green", | |||
"name": "Lily Ross", | |||
"gender": "female", | |||
"company": "RODEOMAD", | |||
"email": "lilyross@rodeomad.com", | |||
"phone": "+1 (941) 465-3561", | |||
"address": "525 Beekman Place, Blodgett, Marshall Islands, 3173", | |||
"about": "Aliquip duis proident excepteur eiusmod in quis officia consequat culpa eu et ut. Occaecat reprehenderit tempor mollit do eu magna qui et magna exercitation aliqua. Incididunt exercitation dolor proident eiusmod minim occaecat. Sunt et minim mollit et veniam sint ex. Duis ullamco elit aute eu excepteur reprehenderit officia.\\r\\n", | |||
"registered": "2019-11-02T04:06:42 -01:00", | |||
"latitude": 17.031701, | |||
"longitude": -42.657106, | |||
"tags": [ | |||
"id", | |||
"non", | |||
"culpa", | |||
"reprehenderit", | |||
"esse", | |||
"elit", | |||
"sit" | |||
], | |||
"friends": [ | |||
{ | |||
"id": 0, | |||
"name": "Ursula Maldonado" | |||
}, | |||
{ | |||
"id": 1, | |||
"name": "Traci Huff" | |||
}, | |||
{ | |||
"id": 2, | |||
"name": "Taylor Holt" | |||
} | |||
], | |||
"greeting": "Hello, Lily Ross! You have 3 unread messages.", | |||
"favoriteFruit": "strawberry" | |||
} | |||
]""") | |||
} |
@@ -0,0 +1,475 @@ | |||
# -*- coding: utf-8 -*- | |||
from . import core as html5 | |||
from . import utils | |||
class Button(html5.Button): | |||
def __init__(self, txt=None, callback=None, className=None, *args, **kwargs): | |||
super().__init__(*args, **kwargs) | |||
self["class"] = "btn" | |||
if className: | |||
self.addClass(className) | |||
self["type"] = "button" | |||
if txt is not None: | |||
self.setText(txt) | |||
self.callback = callback | |||
self.sinkEvent("onClick") | |||
def setText(self, txt): | |||
if txt is not None: | |||
self.element.innerHTML = txt | |||
self["title"] = txt | |||
else: | |||
self.element.innerHTML = "" | |||
self["title"] = "" | |||
def onClick(self, event): | |||
event.stopPropagation() | |||
event.preventDefault() | |||
if self.callback is not None: | |||
self.callback(self) | |||
class Input(html5.Input): | |||
def __init__(self, type="text", placeholder=None, callback=None, id=None, focusCallback=None, *args, **kwargs): | |||
""" | |||
:param type: Input type. Default: "text | |||
:param placeholder: Placeholder text. Default: None | |||
:param callback: Function to be called onChanged: callback(id, value) | |||
:param id: Optional id of the input element. Will be passed to callback | |||
:return: | |||
""" | |||
super().__init__(*args, **kwargs) | |||
self["class"] = "input" | |||
self["type"] = type | |||
if placeholder is not None: | |||
self["placeholder"] = placeholder | |||
self.callback = callback | |||
if id is not None: | |||
self["id"] = id | |||
self.sinkEvent("onChange") | |||
self.focusCallback = focusCallback | |||
if focusCallback: | |||
self.sinkEvent("onFocus") | |||
def onChange(self, event): | |||
event.stopPropagation() | |||
event.preventDefault() | |||
if self.callback is not None: | |||
self.callback(self, self["id"], self["value"]) | |||
def onFocus(self, event): | |||
event.stopPropagation() | |||
event.preventDefault() | |||
if self.focusCallback is not None: | |||
self.focusCallback(self, self["id"], self["value"]) | |||
def onDetach(self): | |||
super().onDetach() | |||
self.callback = None | |||
class Popup(html5.Div): | |||
def __init__(self, title=None, id=None, className=None, icon=None, enableShortcuts=True, closeable=True, *args, **kwargs): | |||
super().__init__(""" | |||
<div class="box" [name]="popupBox"> | |||
<div class="box-head" [name]="popupHead"> | |||
<div class="item" [name]="popupHeadItem"> | |||
<div class="item-image"> | |||
<i class="i i--small" [name]="popupIcon"></i> | |||
</div> | |||
<div class="item-content"> | |||
<div class="item-headline" [name]="popupHeadline"></div> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="box-body box--content" [name]="popupBody"></div> | |||
<div class="box-foot box--content bar" [name]="popupFoot"></div> | |||
</div> | |||
""") | |||
self.appendChild = self.popupBody.appendChild | |||
self.fromHTML = lambda *args, **kwargs: self.popupBody.fromHTML(*args, **kwargs) if kwargs.get("bindTo") else self.popupBody.fromHTML(bindTo=self, *args, **kwargs) | |||
self["class"] = "popup popup--center is-active" | |||
if className: | |||
self.addClass(className) | |||
if closeable: | |||
closeBtn = Button("×", self.close, className="item-action") | |||
closeBtn.removeClass("btn") | |||
self.popupHeadItem.appendChild(closeBtn) | |||
if title: | |||
self.popupHeadline.appendChild(title) | |||
if icon: | |||
self.popupIcon.appendChild(icon[0]) | |||
elif title: | |||
self.popupIcon.appendChild(title[0]) | |||
else: | |||
self.popupIcon.appendChild("Vi") #fixme!!! this _LIBRARY_ is not only used in the Vi... | |||
# id can be used to pass information to callbacks | |||
self.id = id | |||
#FIXME: Implement a global overlay! One popupOverlay next to a list of popups. | |||
self.popupOverlay = html5.Div() | |||
self.popupOverlay["class"] = "popup-overlay is-active" | |||
self.enableShortcuts = enableShortcuts | |||
self.onDocumentKeyDownMethod = None | |||
self.popupOverlay.appendChild(self) | |||
html5.Body().appendChild(self.popupOverlay) | |||
#FIXME: Close/Cancel every popup with click on popupCloseBtn without removing the global overlay. | |||
def onAttach(self): | |||
super(Popup, self).onAttach() | |||
if self.enableShortcuts: | |||
self.onDocumentKeyDownMethod = self.onDocumentKeyDown # safe reference to method | |||
html5.document.addEventListener("keydown", self.onDocumentKeyDownMethod) | |||
def onDetach(self): | |||
super(Popup, self).onDetach() | |||
if self.enableShortcuts: | |||
html5.document.removeEventListener("keydown", self.onDocumentKeyDownMethod) | |||
def onDocumentKeyDown(self, event): | |||
if html5.isEscape(event): | |||
self.close() | |||
def close(self, *args, **kwargs): | |||
html5.Body().removeChild(self.popupOverlay) | |||
self.popupOverlay = None | |||
class InputDialog(Popup): | |||
def __init__(self, text, value="", successHandler=None, abortHandler=None, | |||
successLbl="OK", abortLbl="Cancel", placeholder="", *args, **kwargs): | |||
super().__init__(*args, **kwargs) | |||
self.addClass("popup--inputdialog") | |||
self.sinkEvent("onKeyDown", "onKeyUp") | |||
self.successHandler = successHandler | |||
self.abortHandler = abortHandler | |||
self.fromHTML( | |||
""" | |||
<div class="input-group"> | |||
<label class="label"> | |||
{{text}} | |||
</label> | |||
<input class="input" [name]="inputElem" value="{{value}}" placeholder="{{placeholder}}" /> | |||
</div> | |||
""", | |||
vars={ | |||
"text": text, | |||
"value": value, | |||
"placeholder": placeholder | |||
} | |||
) | |||
# Cancel | |||
self.popupFoot.appendChild(Button(abortLbl, self.onCancel, className="btn--cancel btn--danger")) | |||
# Okay | |||
self.okayBtn = Button(successLbl, self.onOkay, className="btn--okay btn--primary") | |||
if not value: | |||
self.okayBtn.disable() | |||
self.popupFoot.appendChild(self.okayBtn) | |||
self.inputElem.focus() | |||
def onKeyDown(self, event): | |||
if html5.isReturn(event) and self.inputElem["value"]: | |||
event.stopPropagation() | |||
event.preventDefault() | |||
self.onOkay() | |||
def onKeyUp(self, event): | |||
if self.inputElem["value"]: | |||
self.okayBtn.enable() | |||
else: | |||
self.okayBtn.disable() | |||
def onDocumentKeyDown(self, event): | |||
if html5.isEscape(event): | |||
event.stopPropagation() | |||
event.preventDefault() | |||
self.onCancel() | |||
def onOkay(self, *args, **kwargs): | |||
if self.successHandler: | |||
self.successHandler(self, self.inputElem["value"]) | |||
self.close() | |||
def onCancel(self, *args, **kwargs): | |||
if self.abortHandler: | |||
self.abortHandler(self, self.inputElem["value"]) | |||
self.close() | |||
class Alert(Popup): | |||
""" | |||
Just displaying an alerting message box with OK-button. | |||
""" | |||
def __init__(self, msg, title=None, className=None, okCallback=None, okLabel="OK", icon="!", closeable=True, *args, **kwargs): | |||
super().__init__(title, className=None, icon=icon, closeable=closeable, *args, **kwargs) | |||
self.addClass("popup--alert") | |||
if className: | |||
self.addClass(className) | |||
self.okCallback = okCallback | |||
message = html5.Span() | |||
message.addClass("alert-msg") | |||
self.popupBody.appendChild(message) | |||
if isinstance(msg, str): | |||
msg = msg.replace("\n", "<br>") | |||
message.appendChild(msg, bindTo=False) | |||
self.sinkEvent("onKeyDown") | |||
if closeable: | |||
okBtn = Button(okLabel, callback=self.onOkBtnClick) | |||
okBtn.addClass("btn--okay btn--primary") | |||
self.popupFoot.appendChild(okBtn) | |||
okBtn.focus() | |||
def drop(self): | |||
self.okCallback = None | |||
self.close() | |||
def onOkBtnClick(self, sender=None): | |||
if self.okCallback: | |||
self.okCallback(self) | |||
self.drop() | |||
def onKeyDown(self, event): | |||
if html5.isReturn(event): | |||
event.stopPropagation() | |||
event.preventDefault() | |||
self.onOkBtnClick() | |||
class YesNoDialog(Popup): | |||
def __init__(self, question, title=None, yesCallback=None, noCallback=None, | |||
yesLabel="Yes", noLabel="No", icon="?", | |||
closeable=False, *args, **kwargs): | |||
super().__init__(title, closeable=closeable, icon=icon, *args, **kwargs) | |||
self.addClass("popup--yesnodialog") | |||
self.yesCallback = yesCallback | |||
self.noCallback = noCallback | |||
lbl = html5.Span() | |||
lbl["class"].append("question") | |||
self.popupBody.appendChild(lbl) | |||
if isinstance(question, html5.Widget): | |||
lbl.appendChild(question) | |||
else: | |||
utils.textToHtml(lbl, question) | |||
if len(noLabel): | |||
btnNo = Button(noLabel, className="btn--no", callback=self.onNoClicked) | |||
#btnNo["class"].append("btn--no") | |||
self.popupFoot.appendChild(btnNo) | |||
btnYes = Button(yesLabel, callback=self.onYesClicked) | |||
btnYes["class"].append("btn--yes") | |||
self.popupFoot.appendChild(btnYes) | |||
self.sinkEvent("onKeyDown") | |||
btnYes.focus() | |||
def onKeyDown(self, event): | |||
if html5.isReturn(event): | |||
event.stopPropagation() | |||
event.preventDefault() | |||
self.onYesClicked() | |||
def onDocumentKeyDown(self, event): | |||
if html5.isEscape(event): | |||
event.stopPropagation() | |||
event.preventDefault() | |||
self.onNoClicked() | |||
def drop(self): | |||
self.yesCallback = None | |||
self.noCallback = None | |||
self.close() | |||
def onYesClicked(self, *args, **kwargs): | |||
if self.yesCallback: | |||
self.yesCallback(self) | |||
self.drop() | |||
def onNoClicked(self, *args, **kwargs): | |||
if self.noCallback: | |||
self.noCallback(self) | |||
self.drop() | |||
class SelectDialog(Popup): | |||
def __init__(self, prompt, items=None, title=None, okBtn="OK", cancelBtn="Cancel", forceSelect=False, | |||
callback=None, *args, **kwargs): | |||
super().__init__(title, *args, **kwargs) | |||
self["class"].append("popup--selectdialog") | |||
self.callback = callback | |||
self.items = items | |||
assert isinstance(self.items, list) | |||
# Prompt | |||
if prompt: | |||
lbl = html5.Span() | |||
lbl["class"].append("prompt") | |||
if isinstance(prompt, html5.Widget): | |||
lbl.appendChild(prompt) | |||
else: | |||
utils.textToHtml(lbl, prompt) | |||
self.popupBody.appendChild(lbl) | |||
# Items | |||
if not forceSelect and len(items) <= 3: | |||
for idx, item in enumerate(items): | |||
if isinstance(item, dict): | |||
title = item.get("title") | |||
cssc = item.get("class") | |||
elif isinstance(item, tuple): | |||
title = item[1] | |||
cssc = None | |||
else: | |||
title = item | |||
btn = Button(title, callback=self.onAnyBtnClick) | |||
btn.idx = idx | |||
if cssc: | |||
btn.addClass(cssc) | |||
self.popupBody.appendChild(btn) | |||
else: | |||
self.select = html5.Select() | |||
self.popupBody.appendChild(self.select) | |||
for idx, item in enumerate(items): | |||
if isinstance(item, dict): | |||
title = item.get("title") | |||
elif isinstance(item, tuple): | |||
title = item[1] | |||
else: | |||
title = item | |||
opt = html5.Option(title) | |||
opt["value"] = str(idx) | |||
self.select.appendChild(opt) | |||
if okBtn: | |||
self.popupFoot.appendChild(Button(okBtn, callback=self.onOkClick)) | |||
if cancelBtn: | |||
self.popupFoot.appendChild(Button(cancelBtn, callback=self.onCancelClick)) | |||
def onAnyBtnClick(self, sender): | |||
item = self.items[sender.idx] | |||
if isinstance(item, dict) and item.get("callback") and callable(item["callback"]): | |||
item["callback"](item) | |||
if self.callback: | |||
self.callback(item) | |||
self.items = None | |||
self.close() | |||
def onCancelClick(self, sender=None): | |||
self.close() | |||
def onOkClick(self, sender=None): | |||
assert self.select["selectedIndex"] >= 0 | |||
item = self.items[int(self.select.children(self.select["selectedIndex"])["value"])] | |||
if isinstance(item, dict) and item.get("callback") and callable(item["callback"]): | |||
item["callback"](item) | |||
if self.callback: | |||
self.callback(item) | |||
self.items = None | |||
self.select = None | |||
self.close() | |||
class TextareaDialog(Popup): | |||
def __init__(self, text, value="", successHandler=None, abortHandler=None, successLbl="OK", abortLbl="Cancel", | |||
*args, **kwargs): | |||
super().__init__(*args, **kwargs) | |||
self["class"].append("popup--textareadialog") | |||
self.successHandler = successHandler | |||
self.abortHandler = abortHandler | |||
span = html5.Span() | |||
span.element.innerHTML = text | |||
self.popupBody.appendChild(span) | |||
self.inputElem = html5.Textarea() | |||
self.inputElem["value"] = value | |||
self.popupBody.appendChild(self.inputElem) | |||
okayBtn = Button(successLbl, self.onOkay) | |||
okayBtn["class"].append("btn--okay") | |||
self.popupFoot.appendChild(okayBtn) | |||
cancelBtn = Button(abortLbl, self.onCancel) | |||
cancelBtn["class"].append("btn--cancel") | |||
self.popupFoot.appendChild(cancelBtn) | |||
self.sinkEvent("onKeyDown") | |||
self.inputElem.focus() | |||
def onDocumentKeyDown(self, event): | |||
if html5.isEscape(event): | |||
event.stopPropagation() | |||
event.preventDefault() | |||
self.onCancel() | |||
def onOkay(self, *args, **kwargs): | |||
if self.successHandler: | |||
self.successHandler(self, self.inputElem["value"]) | |||
self.close() | |||
def onCancel(self, *args, **kwargs): | |||
if self.abortHandler: | |||
self.abortHandler(self, self.inputElem["value"]) | |||
self.close() |
@@ -0,0 +1,9 @@ | |||
[ | |||
"app.py", | |||
"examples.py", | |||
"html5.py", | |||
"core.py", | |||
"ext.py", | |||
"ignite.py", | |||
"utils.py" | |||
] |
@@ -0,0 +1,6 @@ | |||
#-*- coding: utf-8 -*- | |||
from .core import * | |||
from . import ext, utils, ignite | |||
@@ -0,0 +1,186 @@ | |||
# -*- coding: utf-8 -*- | |||
from . import core as html5 | |||
@html5.tag | |||
class Label(html5.Label): | |||
_parserTagName = "ignite-label" | |||
def __init__(self, *args, **kwargs): | |||
super(Label, self).__init__(style="label ignt-label", *args, **kwargs) | |||
@html5.tag | |||
class Input(html5.Input): | |||
_parserTagName = "ignite-input" | |||
def __init__(self, *args, **kwargs): | |||
super(Input, self).__init__(style="input ignt-input", *args, **kwargs) | |||
@html5.tag | |||
class Switch(html5.Div): | |||
_parserTagName = "ignite-switch" | |||
def __init__(self, *args, **kwargs): | |||
super(Switch, self).__init__(style="switch ignt-switch", *args, **kwargs) | |||
self.input = html5.Input(style="switch-input") | |||
self.appendChild(self.input) | |||
self.input["type"] = "checkbox" | |||
switchLabel = html5.Label(forElem=self.input) | |||
switchLabel.addClass("switch-label") | |||
self.appendChild(switchLabel) | |||
def _setChecked(self, value): | |||
self.input["checked"] = bool(value) | |||
def _getChecked(self): | |||
return self.input["checked"] | |||
@html5.tag | |||
class Check(html5.Input): | |||
_parserTagName = "ignite-check" | |||
def __init__(self, *args, **kwargs): | |||
super(Check, self).__init__(style="check ignt-check", *args, **kwargs) | |||
checkInput = html5.Input() | |||
checkInput.addClass("check-input") | |||
checkInput["type"] = "checkbox" | |||
self.appendChild(checkInput) | |||
checkLabel = html5.Label(forElem=checkInput) | |||
checkLabel.addClass("check-label") | |||
self.appendChild(checkLabel) | |||
@html5.tag | |||
class Radio(html5.Div): | |||
_parserTagName = "ignite-radio" | |||
def __init__(self, *args, **kwargs): | |||
super(Radio, self).__init__(style="radio ignt-radio", *args, **kwargs) | |||
radioInput = html5.Input() | |||
radioInput.addClass("radio-input") | |||
radioInput["type"] = "radio" | |||
self.appendChild(radioInput) | |||
radioLabel = html5.Label(forElem=radioInput) | |||
radioLabel.addClass("radio-label") | |||
self.appendChild(radioLabel) | |||
@html5.tag | |||
class Select(html5.Select): | |||
_parserTagName = "ignite-select" | |||
def __init__(self, *args, **kwargs): | |||
super(Select, self).__init__(style="select ignt-select", *args, **kwargs) | |||
defaultOpt = html5.Option() | |||
defaultOpt["selected"] = True | |||
defaultOpt["disabled"] = True | |||
defaultOpt.element.innerHTML = "" | |||
self.appendChild(defaultOpt) | |||
@html5.tag | |||
class Textarea(html5.Textarea): | |||
_parserTagName = "ignite-textarea" | |||
def __init__(self, *args, **kwargs): | |||
super(Textarea, self).__init__(style="textarea ignt-textarea", *args, **kwargs) | |||
@html5.tag | |||
class Progress(html5.Progress): | |||
_parserTagName = "ignite-progress" | |||
def __init__(self, *args, **kwargs): | |||
super(Progress, self).__init__(style="progress ignt-progress", *args, **kwargs) | |||
@html5.tag | |||
class Item(html5.Div): | |||
_parserTagName = "ignite-item" | |||
def __init__(self, title=None, descr=None, className=None, *args, **kwargs): | |||
super(Item, self).__init__(style="item ignt-item", *args, **kwargs) | |||
if className: | |||
self.addClass(className) | |||
self.fromHTML(""" | |||
<div class="item-image ignt-item-image" [name]="itemImage"> | |||
</div> | |||
<div class="item-content ignt-item-content" [name]="itemContent"> | |||
<div class="item-headline ignt-item-headline" [name]="itemHeadline"> | |||
</div> | |||
</div> | |||
""") | |||
if title: | |||
self.itemHeadline.appendChild(html5.TextNode(title)) | |||
if descr: | |||
self.itemSubline = html5.Div() | |||
self.addClass("item-subline ignt-item-subline") | |||
self.itemSubline.appendChild(html5.TextNode(descr)) | |||
self.appendChild(self.itemSubline) | |||
@html5.tag | |||
class Table(html5.Table): | |||
_parserTagName = "ignite-table" | |||
def __init__(self, *args, **kwargs): | |||
super(Table, self).__init__(*args, **kwargs) | |||
self.head.addClass("ignt-table-head") | |||
self.body.addClass("ignt-table-body") | |||
def prepareRow(self, row): | |||
assert row >= 0, "Cannot create rows with negative index" | |||
for child in self.body._children: | |||
row -= child["rowspan"] | |||
if row < 0: | |||
return | |||
while row >= 0: | |||
tableRow = html5.Tr() | |||
tableRow.addClass("ignt-table-body-row") | |||
self.body.appendChild(tableRow) | |||
row -= 1 | |||
def prepareCol(self, row, col): | |||
assert col >= 0, "Cannot create cols with negative index" | |||
self.prepareRow(row) | |||
for rowChild in self.body._children: | |||
row -= rowChild["rowspan"] | |||
if row < 0: | |||
for colChild in rowChild._children: | |||
col -= colChild["colspan"] | |||
if col < 0: | |||
return | |||
while col >= 0: | |||
tableCell = html5.Td() | |||
tableCell.addClass("ignt-table-body-cell") | |||
rowChild.appendChild(tableCell) | |||
col -= 1 | |||
return | |||
def fastGrid( self, rows, cols, createHidden=False ): | |||
colsstr = "".join(['<td class="ignt-table-body-cell"></td>' for i in range(0, cols)]) | |||
tblstr = '<tbody [name]="body" class="ignt-table-body" >' | |||
for r in range(0, rows): | |||
tblstr += '<tr class="ignt-table-body-row %s">%s</tr>' %("is-hidden" if createHidden else "",colsstr) | |||
tblstr +="</tbody>" | |||
self.fromHTML(tblstr) |
@@ -0,0 +1,101 @@ | |||
# -*- coding: utf-8 -*- | |||
from . import core as html5 | |||
def unescape(val, maxLength = 0): | |||
""" | |||
Unquotes several HTML-quoted characters in a string. | |||
:param val: The value to be unescaped. | |||
:type val: str | |||
:param maxLength: Cut-off after maxLength characters. | |||
A value of 0 means "unlimited". (default) | |||
:type maxLength: int | |||
:returns: The unquoted string. | |||
:rtype: str | |||
""" | |||
val = val \ | |||
.replace("<", "<") \ | |||
.replace(">", ">") \ | |||
.replace(""", "\"") \ | |||
.replace("'", "'") | |||
if maxLength > 0: | |||
return val[0:maxLength] | |||
return val | |||
def doesEventHitWidgetOrParents(event, widget): | |||
""" | |||
Test if event 'event' hits widget 'widget' (or *any* of its parents) | |||
""" | |||
while widget: | |||
if event.target == widget.element: | |||
return widget | |||
widget = widget.parent() | |||
return None | |||
def doesEventHitWidgetOrChildren(event, widget): | |||
""" | |||
Test if event 'event' hits widget 'widget' (or *any* of its children) | |||
""" | |||
if event.target == widget.element: | |||
return widget | |||
for child in widget.children(): | |||
if doesEventHitWidgetOrChildren(event, child): | |||
return child | |||
return None | |||
def textToHtml(node, text): | |||
""" | |||
Generates html nodes from text by splitting text into content and into | |||
line breaks html5.Br. | |||
:param node: The node where the nodes are appended to. | |||
:param text: The text to be inserted. | |||
""" | |||
for (i, part) in enumerate(text.split("\n")): | |||
if i > 0: | |||
node.appendChild(html5.Br()) | |||
node.appendChild(html5.TextNode(part)) | |||
def parseInt(s, ret = 0): | |||
""" | |||
Parses a value as int | |||
""" | |||
if not isinstance(s, str): | |||
return int(s) | |||
elif s: | |||
if s[0] in "+-": | |||
ts = s[1:] | |||
else: | |||
ts = s | |||
if ts and all([_ in "0123456789" for _ in ts]): | |||
return int(s) | |||
return ret | |||
def parseFloat(s, ret = 0.0): | |||
""" | |||
Parses a value as float. | |||
""" | |||
if not isinstance(s, str): | |||
return float(s) | |||
elif s: | |||
if s[0] in "+-": | |||
ts = s[1:] | |||
else: | |||
ts = s | |||
if ts and ts.count(".") <= 1 and all([_ in ".0123456789" for _ in ts]): | |||
return float(s) | |||
return ret |
@@ -0,0 +1,121 @@ | |||
.. Lark documentation master file, created by | |||
sphinx-quickstart on Sun Aug 16 13:09:41 2020. | |||
You can adapt this file completely to your liking, but it should at least | |||
contain the root `toctree` directive. | |||
Welcome to Lark's documentation! | |||
================================ | |||
.. toctree:: | |||
:maxdepth: 2 | |||
:caption: Overview | |||
:hidden: | |||
philosophy | |||
features | |||
parsers | |||
.. toctree:: | |||
:maxdepth: 2 | |||
:caption: Tutorials & Guides | |||
:hidden: | |||
json_tutorial | |||
how_to_use | |||
how_to_develop | |||
recipes | |||
examples/index | |||
.. toctree:: | |||
:maxdepth: 2 | |||
:caption: Reference | |||
:hidden: | |||
grammar | |||
tree_construction | |||
classes | |||
visitors | |||
forest | |||
tools | |||
Lark is a modern parsing library for Python. Lark can parse any context-free grammar. | |||
Lark provides: | |||
- Advanced grammar language, based on EBNF | |||
- Three parsing algorithms to choose from: Earley, LALR(1) and CYK | |||
- Automatic tree construction, inferred from your grammar | |||
- Fast unicode lexer with regexp support, and automatic line-counting | |||
Install Lark | |||
-------------- | |||
.. code:: bash | |||
$ pip install lark | |||
Syntax Highlighting | |||
------------------- | |||
- `Sublime Text & TextMate`_ | |||
- `Visual Studio Code`_ (Or install through the vscode plugin system) | |||
- `Intellij & PyCharm`_ | |||
- `Vim`_ | |||
- `Atom`_ | |||
.. _Sublime Text & TextMate: https://github.com/lark-parser/lark_syntax | |||
.. _Visual Studio Code: https://github.com/lark-parser/vscode-lark | |||
.. _Intellij & PyCharm: https://github.com/lark-parser/intellij-syntax-highlighting | |||
.. _Vim: https://github.com/lark-parser/vim-lark-syntax | |||
.. _Atom: https://github.com/Alhadis/language-grammars | |||
Resources | |||
--------- | |||
- :doc:`philosophy` | |||
- :doc:`features` | |||
- `Examples`_ | |||
- `Third-party examples`_ | |||
- `Online IDE`_ | |||
- Tutorials | |||
- `How to write a DSL`_ - Implements a toy LOGO-like language with | |||
an interpreter | |||
- :doc:`json_tutorial` - Teaches you how to use Lark | |||
- Unofficial | |||
- `Program Synthesis is Possible`_ - Creates a DSL for Z3 | |||
- Guides | |||
- :doc:`how_to_use` | |||
- :doc:`how_to_develop` | |||
- Reference | |||
- :doc:`grammar` | |||
- :doc:`tree_construction` | |||
- :doc:`visitors` | |||
- :doc:`forest` | |||
- :doc:`classes` | |||
- :doc:`tools` | |||
- `Cheatsheet (PDF)`_ | |||
- Discussion | |||
- `Gitter`_ | |||
- `Forum (Google Groups)`_ | |||
.. _Examples: https://github.com/lark-parser/lark/tree/master/examples | |||
.. _Third-party examples: https://github.com/ligurio/lark-grammars | |||
.. _Online IDE: https://lark-parser.github.io/ide | |||
.. _How to write a DSL: http://blog.erezsh.com/how-to-write-a-dsl-in-python-with-lark/ | |||
.. _Program Synthesis is Possible: https://www.cs.cornell.edu/~asampson/blog/minisynth.html | |||
.. _Cheatsheet (PDF): _static/lark_cheatsheet.pdf | |||
.. _Gitter: https://gitter.im/lark-parser/Lobby | |||
.. _Forum (Google Groups): https://groups.google.com/forum/#!forum/lark-parser |
@@ -0,0 +1,448 @@ | |||
# JSON parser - Tutorial | |||
Lark is a parser - a program that accepts a grammar and text, and produces a structured tree that represents that text. | |||
In this tutorial we will write a JSON parser in Lark, and explore Lark's various features in the process. | |||
It has 5 parts. | |||
1. Writing the grammar | |||
2. Creating the parser | |||
3. Shaping the tree | |||
4. Evaluating the tree | |||
5. Optimizing | |||
Knowledge assumed: | |||
- Using Python | |||
- A basic understanding of how to use regular expressions | |||
## Part 1 - The Grammar | |||
Lark accepts its grammars in a format called [EBNF](https://www.wikiwand.com/en/Extended_Backus%E2%80%93Naur_form). It basically looks like this: | |||
rule_name : list of rules and TERMINALS to match | |||
| another possible list of items | |||
| etc. | |||
TERMINAL: "some text to match" | |||
(*a terminal is a string or a regular expression*) | |||
The parser will try to match each rule (left-part) by matching its items (right-part) sequentially, trying each alternative (In practice, the parser is predictive so we don't have to try every alternative). | |||
How to structure those rules is beyond the scope of this tutorial, but often it's enough to follow one's intuition. | |||
In the case of JSON, the structure is simple: A json document is either a list, or a dictionary, or a string/number/etc. | |||
The dictionaries and lists are recursive, and contain other json documents (or "values"). | |||
Let's write this structure in EBNF form: | |||
value: dict | |||
| list | |||
| STRING | |||
| NUMBER | |||
| "true" | "false" | "null" | |||
list : "[" [value ("," value)*] "]" | |||
dict : "{" [pair ("," pair)*] "}" | |||
pair : STRING ":" value | |||
A quick explanation of the syntax: | |||
- Parenthesis let us group rules together. | |||
- rule\* means *any amount*. That means, zero or more instances of that rule. | |||
- [rule] means *optional*. That means zero or one instance of that rule. | |||
Lark also supports the rule+ operator, meaning one or more instances. It also supports the rule? operator which is another way to say *optional*. | |||
Of course, we still haven't defined "STRING" and "NUMBER". Luckily, both these literals are already defined in Lark's common library: | |||
%import common.ESCAPED_STRING -> STRING | |||
%import common.SIGNED_NUMBER -> NUMBER | |||
The arrow (->) renames the terminals. But that only adds obscurity in this case, so going forward we'll just use their original names. | |||
We'll also take care of the white-space, which is part of the text. | |||
%import common.WS | |||
%ignore WS | |||
We tell our parser to ignore whitespace. Otherwise, we'd have to fill our grammar with WS terminals. | |||
By the way, if you're curious what these terminals signify, they are roughly equivalent to this: | |||
NUMBER : /-?\d+(\.\d+)?([eE][+-]?\d+)?/ | |||
STRING : /".*?(?<!\\)"/ | |||
%ignore /[ \t\n\f\r]+/ | |||
Lark will accept this, if you really want to complicate your life :) | |||
You can find the original definitions in [common.lark](https://github.com/lark-parser/lark/blob/master/lark/grammars/common.lark). | |||
They don't strictly adhere to [json.org](https://json.org/) - but our purpose here is to accept json, not validate it. | |||
Notice that terminals are written in UPPER-CASE, while rules are written in lower-case. | |||
I'll touch more on the differences between rules and terminals later. | |||
## Part 2 - Creating the Parser | |||
Once we have our grammar, creating the parser is very simple. | |||
We simply instantiate Lark, and tell it to accept a "value": | |||
```python | |||
from lark import Lark | |||
json_parser = Lark(r""" | |||
value: dict | |||
| list | |||
| ESCAPED_STRING | |||
| SIGNED_NUMBER | |||
| "true" | "false" | "null" | |||
list : "[" [value ("," value)*] "]" | |||
dict : "{" [pair ("," pair)*] "}" | |||
pair : ESCAPED_STRING ":" value | |||
%import common.ESCAPED_STRING | |||
%import common.SIGNED_NUMBER | |||
%import common.WS | |||
%ignore WS | |||
""", start='value') | |||
``` | |||
It's that simple! Let's test it out: | |||
```python | |||
>>> text = '{"key": ["item0", "item1", 3.14]}' | |||
>>> json_parser.parse(text) | |||
Tree(value, [Tree(dict, [Tree(pair, [Token(STRING, "key"), Tree(value, [Tree(list, [Tree(value, [Token(STRING, "item0")]), Tree(value, [Token(STRING, "item1")]), Tree(value, [Token(NUMBER, 3.14)])])])])])]) | |||
>>> print( _.pretty() ) | |||
value | |||
dict | |||
pair | |||
"key" | |||
value | |||
list | |||
value "item0" | |||
value "item1" | |||
value 3.14 | |||
``` | |||
As promised, Lark automagically creates a tree that represents the parsed text. | |||
But something is suspiciously missing from the tree. Where are the curly braces, the commas and all the other punctuation literals? | |||
Lark automatically filters out literals from the tree, based on the following criteria: | |||
- Filter out string literals without a name, or with a name that starts with an underscore. | |||
- Keep regexps, even unnamed ones, unless their name starts with an underscore. | |||
Unfortunately, this means that it will also filter out literals like "true" and "false", and we will lose that information. The next section, "Shaping the tree" deals with this issue, and others. | |||
## Part 3 - Shaping the Tree | |||
We now have a parser that can create a parse tree (or: AST), but the tree has some issues: | |||
1. "true", "false" and "null" are filtered out (test it out yourself!) | |||
2. Is has useless branches, like *value*, that clutter-up our view. | |||
I'll present the solution, and then explain it: | |||
?value: dict | |||
| list | |||
| string | |||
| SIGNED_NUMBER -> number | |||
| "true" -> true | |||
| "false" -> false | |||
| "null" -> null | |||
... | |||
string : ESCAPED_STRING | |||
1. Those little arrows signify *aliases*. An alias is a name for a specific part of the rule. In this case, we will name the *true/false/null* matches, and this way we won't lose the information. We also alias *SIGNED_NUMBER* to mark it for later processing. | |||
2. The question-mark prefixing *value* ("?value") tells the tree-builder to inline this branch if it has only one member. In this case, *value* will always have only one member, and will always be inlined. | |||
3. We turned the *ESCAPED_STRING* terminal into a rule. This way it will appear in the tree as a branch. This is equivalent to aliasing (like we did for the number), but now *string* can also be used elsewhere in the grammar (namely, in the *pair* rule). | |||
Here is the new grammar: | |||
```python | |||
from lark import Lark | |||
json_parser = Lark(r""" | |||
?value: dict | |||
| list | |||
| string | |||
| SIGNED_NUMBER -> number | |||
| "true" -> true | |||
| "false" -> false | |||
| "null" -> null | |||
list : "[" [value ("," value)*] "]" | |||
dict : "{" [pair ("," pair)*] "}" | |||
pair : string ":" value | |||
string : ESCAPED_STRING | |||
%import common.ESCAPED_STRING | |||
%import common.SIGNED_NUMBER | |||
%import common.WS | |||
%ignore WS | |||
""", start='value') | |||
``` | |||
And let's test it out: | |||
```python | |||
>>> text = '{"key": ["item0", "item1", 3.14, true]}' | |||
>>> print( json_parser.parse(text).pretty() ) | |||
dict | |||
pair | |||
string "key" | |||
list | |||
string "item0" | |||
string "item1" | |||
number 3.14 | |||
true | |||
``` | |||
Ah! That is much much nicer. | |||
## Part 4 - Evaluating the tree | |||
It's nice to have a tree, but what we really want is a JSON object. | |||
The way to do it is to evaluate the tree, using a Transformer. | |||
A transformer is a class with methods corresponding to branch names. For each branch, the appropriate method will be called with the children of the branch as its argument, and its return value will replace the branch in the tree. | |||
So let's write a partial transformer, that handles lists and dictionaries: | |||
```python | |||
from lark import Transformer | |||
class MyTransformer(Transformer): | |||
def list(self, items): | |||
return list(items) | |||
def pair(self, key_value): | |||
k, v = key_value | |||
return k, v | |||
def dict(self, items): | |||
return dict(items) | |||
``` | |||
And when we run it, we get this: | |||
```python | |||
>>> tree = json_parser.parse(text) | |||
>>> MyTransformer().transform(tree) | |||
{Tree(string, [Token(ANONRE_1, "key")]): [Tree(string, [Token(ANONRE_1, "item0")]), Tree(string, [Token(ANONRE_1, "item1")]), Tree(number, [Token(ANONRE_0, 3.14)]), Tree(true, [])]} | |||
``` | |||
This is pretty close. Let's write a full transformer that can handle the terminals too. | |||
Also, our definitions of list and dict are a bit verbose. We can do better: | |||
```python | |||
from lark import Transformer | |||
class TreeToJson(Transformer): | |||
def string(self, s): | |||
(s,) = s | |||
return s[1:-1] | |||
def number(self, n): | |||
(n,) = n | |||
return float(n) | |||
list = list | |||
pair = tuple | |||
dict = dict | |||
null = lambda self, _: None | |||
true = lambda self, _: True | |||
false = lambda self, _: False | |||
``` | |||
And when we run it: | |||
```python | |||
>>> tree = json_parser.parse(text) | |||
>>> TreeToJson().transform(tree) | |||
{u'key': [u'item0', u'item1', 3.14, True]} | |||
``` | |||
Magic! | |||
## Part 5 - Optimizing | |||
### Step 1 - Benchmark | |||
By now, we have a fully working JSON parser, that can accept a string of JSON, and return its Pythonic representation. | |||
But how fast is it? | |||
Now, of course there are JSON libraries for Python written in C, and we can never compete with them. But since this is applicable to any parser you would write in Lark, let's see how far we can take this. | |||
The first step for optimizing is to have a benchmark. For this benchmark I'm going to take data from [json-generator.com/](http://www.json-generator.com/). I took their default suggestion and changed it to 5000 objects. The result is a 6.6MB sparse JSON file. | |||
Our first program is going to be just a concatenation of everything we've done so far: | |||
```python | |||
import sys | |||
from lark import Lark, Transformer | |||
json_grammar = r""" | |||
?value: dict | |||
| list | |||
| string | |||
| SIGNED_NUMBER -> number | |||
| "true" -> true | |||
| "false" -> false | |||
| "null" -> null | |||
list : "[" [value ("," value)*] "]" | |||
dict : "{" [pair ("," pair)*] "}" | |||
pair : string ":" value | |||
string : ESCAPED_STRING | |||
%import common.ESCAPED_STRING | |||
%import common.SIGNED_NUMBER | |||
%import common.WS | |||
%ignore WS | |||
""" | |||
class TreeToJson(Transformer): | |||
def string(self, s): | |||
(s,) = s | |||
return s[1:-1] | |||
def number(self, n): | |||
(n,) = n | |||
return float(n) | |||
list = list | |||
pair = tuple | |||
dict = dict | |||
null = lambda self, _: None | |||
true = lambda self, _: True | |||
false = lambda self, _: False | |||
json_parser = Lark(json_grammar, start='value', lexer='basic') | |||
if __name__ == '__main__': | |||
with open(sys.argv[1]) as f: | |||
tree = json_parser.parse(f.read()) | |||
print(TreeToJson().transform(tree)) | |||
``` | |||
We run it and get this: | |||
$ time python tutorial_json.py json_data > /dev/null | |||
real 0m36.257s | |||
user 0m34.735s | |||
sys 0m1.361s | |||
That's unsatisfactory time for a 6MB file. Maybe if we were parsing configuration or a small DSL, but we're trying to handle large amount of data here. | |||
Well, turns out there's quite a bit we can do about it! | |||
### Step 2 - LALR(1) | |||
So far we've been using the Earley algorithm, which is the default in Lark. Earley is powerful but slow. But it just so happens that our grammar is LR-compatible, and specifically LALR(1) compatible. | |||
So let's switch to LALR(1) and see what happens: | |||
```python | |||
json_parser = Lark(json_grammar, start='value', parser='lalr') | |||
``` | |||
$ time python tutorial_json.py json_data > /dev/null | |||
real 0m7.554s | |||
user 0m7.352s | |||
sys 0m0.148s | |||
Ah, that's much better. The resulting JSON is of course exactly the same. You can run it for yourself and see. | |||
It's important to note that not all grammars are LR-compatible, and so you can't always switch to LALR(1). But there's no harm in trying! If Lark lets you build the grammar, it means you're good to go. | |||
### Step 3 - Tree-less LALR(1) | |||
So far, we've built a full parse tree for our JSON, and then transformed it. It's a convenient method, but it's not the most efficient in terms of speed and memory. Luckily, Lark lets us avoid building the tree when parsing with LALR(1). | |||
Here's the way to do it: | |||
```python | |||
json_parser = Lark(json_grammar, start='value', parser='lalr', transformer=TreeToJson()) | |||
if __name__ == '__main__': | |||
with open(sys.argv[1]) as f: | |||
print( json_parser.parse(f.read()) ) | |||
``` | |||
We've used the transformer we've already written, but this time we plug it straight into the parser. Now it can avoid building the parse tree, and just send the data straight into our transformer. The *parse()* method now returns the transformed JSON, instead of a tree. | |||
Let's benchmark it: | |||
real 0m4.866s | |||
user 0m4.722s | |||
sys 0m0.121s | |||
That's a measurable improvement! Also, this way is more memory efficient. Check out the benchmark table at the end to see just how much. | |||
As a general practice, it's recommended to work with parse trees, and only skip the tree-builder when your transformer is already working. | |||
### Step 4 - PyPy | |||
PyPy is a JIT engine for running Python, and it's designed to be a drop-in replacement. | |||
Lark is written purely in Python, which makes it very suitable for PyPy. | |||
Let's get some free performance: | |||
$ time pypy tutorial_json.py json_data > /dev/null | |||
real 0m1.397s | |||
user 0m1.296s | |||
sys 0m0.083s | |||
PyPy is awesome! | |||
### Conclusion | |||
We've brought the run-time down from 36 seconds to 1.1 seconds, in a series of small and simple steps. | |||
Now let's compare the benchmarks in a nicely organized table. | |||
I measured memory consumption using a little script called [memusg](https://gist.github.com/netj/526585) | |||
| Code | CPython Time | PyPy Time | CPython Mem | PyPy Mem | |||
|:-----|:-------------|:------------|:----------|:--------- | |||
| Lark - Earley *(with lexer)* | 42s | 4s | 1167M | 608M | | |||
| Lark - LALR(1) | 8s | 1.53s | 453M | 266M | | |||
| Lark - LALR(1) tree-less | 4.76s | 1.23s | 70M | 134M | | |||
| PyParsing ([Parser](https://github.com/pyparsing/pyparsing/blob/master/examples/jsonParser.py)) | 32s | 3.53s | 443M | 225M | | |||
| funcparserlib ([Parser](https://github.com/vlasovskikh/funcparserlib/blob/master/tests/json.py)) | 8.5s | 1.3s | 483M | 293M | | |||
| Parsimonious ([Parser](https://gist.github.com/reclosedev/5222560)) | ? | 5.7s | ? | 1545M | | |||
I added a few other parsers for comparison. PyParsing and funcparselib fair pretty well in their memory usage (they don't build a tree), but they can't compete with the run-time speed of LALR(1). | |||
These benchmarks are for Lark's alpha version. I already have several optimizations planned that will significantly improve run-time speed. | |||
Once again, shout-out to PyPy for being so effective. | |||
## Afterword | |||
This is the end of the tutorial. I hoped you liked it and learned a little about Lark. | |||
To see what else you can do with Lark, check out the [examples](/examples). | |||
For questions or any other subject, feel free to email me at erezshin at gmail dot com. | |||
@@ -0,0 +1,36 @@ | |||
@ECHO OFF | |||
pushd %~dp0 | |||
REM Command file for Sphinx documentation | |||
if "%SPHINXBUILD%" == "" ( | |||
set SPHINXBUILD=sphinx-build | |||
) | |||
set SOURCEDIR=. | |||
set BUILDDIR=_build | |||
set SPHINXPROJ=Lark | |||
if "%1" == "" goto help | |||
%SPHINXBUILD% >NUL 2>NUL | |||
if errorlevel 9009 ( | |||
echo. | |||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx | |||
echo.installed, then set the SPHINXBUILD environment variable to point | |||
echo.to the full path of the 'sphinx-build' executable. Alternatively you | |||
echo.may add the Sphinx directory to PATH. | |||
echo. | |||
echo.If you don't have Sphinx installed, grab it from | |||
echo.http://sphinx-doc.org/ | |||
exit /b 1 | |||
) | |||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% | |||
goto end | |||
:help | |||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% | |||
:end | |||
popd |
@@ -0,0 +1,81 @@ | |||
# Parsers | |||
Lark implements the following parsing algorithms: Earley, LALR(1), and CYK | |||
## Earley | |||
An [Earley Parser](https://www.wikiwand.com/en/Earley_parser) is a chart parser capable of parsing any context-free grammar at O(n^3), and O(n^2) when the grammar is unambiguous. It can parse most LR grammars at O(n). Most programming languages are LR, and can be parsed at a linear time. | |||
Lark's Earley implementation runs on top of a skipping chart parser, which allows it to use regular expressions, instead of matching characters one-by-one. This is a huge improvement to Earley that is unique to Lark. This feature is used by default, but can also be requested explicitly using `lexer='dynamic'`. | |||
It's possible to bypass the dynamic lexing, and use the regular Earley parser with a basic lexer, that tokenizes as an independent first step. Doing so will provide a speed benefit, but will tokenize without using Earley's ambiguity-resolution ability. So choose this only if you know why! Activate with `lexer='basic'` | |||
**SPPF & Ambiguity resolution** | |||
Lark implements the Shared Packed Parse Forest data-structure for the Earley parser, in order to reduce the space and computation required to handle ambiguous grammars. | |||
You can read more about SPPF [here](https://web.archive.org/web/20191229100607/www.bramvandersanden.com/post/2014/06/shared-packed-parse-forest) | |||
As a result, Lark can efficiently parse and store every ambiguity in the grammar, when using Earley. | |||
Lark provides the following options to combat ambiguity: | |||
1) Lark will choose the best derivation for you (default). Users can choose between different disambiguation strategies, and can prioritize (or demote) individual rules over others, using the rule-priority syntax. | |||
2) Users may choose to receive the set of all possible parse-trees (using ambiguity='explicit'), and choose the best derivation themselves. While simple and flexible, it comes at the cost of space and performance, and so it isn't recommended for highly ambiguous grammars, or very long inputs. | |||
3) As an advanced feature, users may use specialized visitors to iterate the SPPF themselves. | |||
**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 `/.*/` | |||
## LALR(1) | |||
[LALR(1)](https://www.wikiwand.com/en/LALR_parser) is a very efficient, true-and-tested parsing algorithm. It's incredibly fast and requires very little memory. It can parse most programming languages (For example: Python and Java). | |||
LALR(1) stands for: | |||
- Left-to-right parsing order | |||
- Rightmost derivation, bottom-up | |||
- Lookahead of 1 token | |||
Lark comes with an efficient implementation that outperforms every other parsing library for Python (including PLY) | |||
Lark extends the traditional YACC-based architecture with a *contextual lexer*, which processes feedback from the parser, 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 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. | |||
### Grammar constraints in LALR(1) | |||
Due to having only a lookahead of one token, LALR is limited in its ability to choose between rules, when they both match the input. | |||
Tips for writing a conforming grammar: | |||
- Try to avoid writing different rules that can match the same sequence of characters. | |||
- For the best performance, prefer left-recursion over right-recursion. | |||
- Consider setting terminal priority only as a last resort. | |||
For a better understanding of these constraints, it's recommended to learn how a SLR parser works. SLR is very similar to LALR but much simpler. | |||
## CYK Parser | |||
A [CYK parser](https://www.wikiwand.com/en/CYK_algorithm) can parse any context-free grammar at O(n^3*|G|). | |||
Its too slow to be practical for simple grammars, but it offers good performance for highly ambiguous grammars. |
@@ -0,0 +1,65 @@ | |||
# Philosophy | |||
Parsers are innately complicated and confusing. They're difficult to understand, difficult to write, and difficult to use. Even experts on the subject can become baffled by the nuances of these complicated state-machines. | |||
Lark's mission is to make the process of writing them as simple and abstract as possible, by following these design principles: | |||
## Design Principles | |||
1. Readability matters | |||
2. Keep the grammar clean and simple | |||
2. Don't force the user to decide on things that the parser can figure out on its own | |||
4. Usability is more important than performance | |||
5. Performance is still very important | |||
6. Follow the Zen of Python, whenever possible and applicable | |||
In accordance with these principles, I arrived at the following design choices: | |||
----------- | |||
## Design Choices | |||
### 1. Separation of code and grammar | |||
Grammars are the de-facto reference for your language, and for the structure of your parse-tree. For any non-trivial language, the conflation of code and grammar always turns out convoluted and difficult to read. | |||
The grammars in Lark are EBNF-inspired, so they are especially easy to read & work with. | |||
### 2. Always build a parse-tree (unless told not to) | |||
Trees are always simpler to work with than state-machines. | |||
1. Trees allow you to see the "state-machine" visually | |||
2. Trees allow your computation to be aware of previous and future states | |||
3. Trees allow you to process the parse in steps, instead of forcing you to do it all at once. | |||
And anyway, every parse-tree can be replayed as a state-machine, so there is no loss of information. | |||
See this answer in more detail [here](https://github.com/erezsh/lark/issues/4). | |||
To improve performance, you can skip building the tree for LALR(1), by providing Lark with a transformer (see the [JSON example](https://github.com/erezsh/lark/blob/master/examples/json_parser.py)). | |||
### 3. Earley is the default | |||
The Earley algorithm can accept *any* context-free grammar you throw at it (i.e. any grammar you can write in EBNF, it can parse). That makes it extremely friendly to beginners, who are not aware of the strange and arbitrary restrictions that LALR(1) places on its grammars. | |||
As the users grow to understand the structure of their grammar, the scope of their target language, and their performance requirements, they may choose to switch over to LALR(1) to gain a huge performance boost, possibly at the cost of some language features. | |||
Both Earley and LALR(1) can use the same grammar, as long as all constraints are satisfied. | |||
In short, "Premature optimization is the root of all evil." | |||
### Other design features | |||
- Automatically resolve terminal collisions whenever possible | |||
- Automatically keep track of line & column numbers | |||
@@ -0,0 +1,174 @@ | |||
# Recipes | |||
A collection of recipes to use Lark and its various features | |||
## Use a transformer to parse integer tokens | |||
Transformers are the common interface for processing matched rules and tokens. | |||
They can be used during parsing for better performance. | |||
```python | |||
from lark import Lark, Transformer | |||
class T(Transformer): | |||
def INT(self, tok): | |||
"Convert the value of `tok` from string to int, while maintaining line number & column." | |||
return tok.update(value=int(tok)) | |||
parser = Lark(""" | |||
start: INT* | |||
%import common.INT | |||
%ignore " " | |||
""", parser="lalr", transformer=T()) | |||
print(parser.parse('3 14 159')) | |||
``` | |||
Prints out: | |||
```python | |||
Tree(start, [Token(INT, 3), Token(INT, 14), Token(INT, 159)]) | |||
``` | |||
## Collect all comments with lexer_callbacks | |||
`lexer_callbacks` can be used to interface with the lexer as it generates tokens. | |||
It accepts a dictionary of the form | |||
{TOKEN_TYPE: callback} | |||
Where callback is of type `f(Token) -> Token` | |||
It only works with the basic and contextual lexers. | |||
This has the same effect of using a transformer, but can also process ignored tokens. | |||
```python | |||
from lark import Lark | |||
comments = [] | |||
parser = Lark(""" | |||
start: INT* | |||
COMMENT: /#.*/ | |||
%import common (INT, WS) | |||
%ignore COMMENT | |||
%ignore WS | |||
""", parser="lalr", lexer_callbacks={'COMMENT': comments.append}) | |||
parser.parse(""" | |||
1 2 3 # hello | |||
# world | |||
4 5 6 | |||
""") | |||
print(comments) | |||
``` | |||
Prints out: | |||
```python | |||
[Token(COMMENT, '# hello'), Token(COMMENT, '# world')] | |||
``` | |||
*Note: We don't have to return a token, because comments are ignored* | |||
## CollapseAmbiguities | |||
Parsing ambiguous texts with earley and `ambiguity='explicit'` produces a single tree with `_ambig` nodes to mark where the ambiguity occurred. | |||
However, it's sometimes more convenient instead to work with a list of all possible unambiguous trees. | |||
Lark provides a utility transformer for that purpose: | |||
```python | |||
from lark import Lark, Tree, Transformer | |||
from lark.visitors import CollapseAmbiguities | |||
grammar = """ | |||
!start: x y | |||
!x: "a" "b" | |||
| "ab" | |||
| "abc" | |||
!y: "c" "d" | |||
| "cd" | |||
| "d" | |||
""" | |||
parser = Lark(grammar, ambiguity='explicit') | |||
t = parser.parse('abcd') | |||
for x in CollapseAmbiguities().transform(t): | |||
print(x.pretty()) | |||
``` | |||
This prints out: | |||
start | |||
x | |||
a | |||
b | |||
y | |||
c | |||
d | |||
start | |||
x ab | |||
y cd | |||
start | |||
x abc | |||
y d | |||
While convenient, this should be used carefully, as highly ambiguous trees will soon create an exponential explosion of such unambiguous derivations. | |||
## Keeping track of parents when visiting | |||
The following visitor assigns a `parent` attribute for every node in the tree. | |||
If your tree nodes aren't unique (if there is a shared Tree instance), the assert will fail. | |||
```python | |||
class Parent(Visitor): | |||
def __default__(self, tree): | |||
for subtree in tree.children: | |||
if isinstance(subtree, Tree): | |||
assert not hasattr(subtree, 'parent') | |||
subtree.parent = tree | |||
``` | |||
## Unwinding VisitError after a transformer/visitor exception | |||
Errors that happen inside visitors and transformers get wrapped inside a `VisitError` exception. | |||
This can often be inconvenient, if you wish the actual error to propagate upwards, or if you want to catch it. | |||
But, it's easy to unwrap it at the point of calling the transformer, by catching it and raising the `VisitError.orig_exc` attribute. | |||
For example: | |||
```python | |||
from lark import Lark, Transformer | |||
from lark.visitors import VisitError | |||
tree = Lark('start: "a"').parse('a') | |||
class T(Transformer): | |||
def start(self, x): | |||
raise KeyError("Original Exception") | |||
t = T() | |||
try: | |||
print( t.transform(tree)) | |||
except VisitError as e: | |||
raise e.orig_exc | |||
``` |
@@ -0,0 +1,3 @@ | |||
# https://docs.readthedocs.io/en/stable/guides/specifying-dependencies.html#specifying-a-requirements-file | |||
sphinx-gallery | |||
sphinx_markdown_tables |
@@ -0,0 +1,71 @@ | |||
# Tools (Stand-alone, Nearley) | |||
## Stand-alone parser | |||
Lark can generate a stand-alone LALR(1) parser from a grammar. | |||
The resulting module provides the same interface as Lark, but with a fixed grammar, and reduced functionality. | |||
Run using: | |||
```bash | |||
python -m lark.tools.standalone | |||
``` | |||
For a play-by-play, read the [tutorial](http://blog.erezsh.com/create-a-stand-alone-lalr1-parser-in-python/) | |||
## Importing grammars from Nearley.js | |||
Lark comes with a tool to convert grammars from [Nearley](https://github.com/Hardmath123/nearley), a popular Earley library for Javascript. It uses [Js2Py](https://github.com/PiotrDabkowski/Js2Py) to convert and run the Javascript postprocessing code segments. | |||
#### Requirements | |||
1. Install Lark with the `nearley` component: | |||
```bash | |||
pip install lark[nearley] | |||
``` | |||
2. Acquire a copy of the Nearley codebase. This can be done using: | |||
```bash | |||
git clone https://github.com/Hardmath123/nearley | |||
``` | |||
#### Usage | |||
The tool can be run using: | |||
```bash | |||
python -m lark.tools.nearley <grammar.ne> <start_rule> <path_to_nearley_repo> | |||
``` | |||
Here's an example of how to import nearley's calculator example into Lark: | |||
```bash | |||
git clone https://github.com/Hardmath123/nearley | |||
python -m lark.tools.nearley nearley/examples/calculator/arithmetic.ne main ./nearley > ncalc.py | |||
``` | |||
You can use the output as a regular python module: | |||
```python | |||
>>> import ncalc | |||
>>> ncalc.parse('sin(pi/4) ^ e') | |||
0.38981434460254655 | |||
``` | |||
The Nearley converter also supports an experimental converter for newer JavaScript (ES6+), using the `--es6` flag: | |||
```bash | |||
git clone https://github.com/Hardmath123/nearley | |||
python -m lark.tools.nearley nearley/examples/calculator/arithmetic.ne main nearley --es6 > ncalc.py | |||
``` | |||
#### Notes | |||
- Lark currently cannot import templates from Nearley | |||
- Lark currently cannot export grammars to Nearley | |||
These might get added in the future, if enough users ask for them. | |||
@@ -0,0 +1,153 @@ | |||
# Tree Construction Reference | |||
Lark builds a tree automatically based on the structure of the grammar, where each rule that is matched becomes a branch (node) in the tree, and its children are its matches, in the order of matching. | |||
For example, the rule `node: child1 child2` will create a tree node with two children. If it is matched as part of another rule (i.e. if it isn't the root), the new rule's tree node will become its parent. | |||
Using `item+` or `item*` will result in a list of items, equivalent to writing `item item item ..`. | |||
Using `item?` will return the item if it matched, or nothing. | |||
If `maybe_placeholders=False` (the default), then `[]` behaves like `()?`. | |||
If `maybe_placeholders=True`, then using `[item]` will return the item if it matched, or the value `None`, if it didn't. | |||
## Terminals | |||
Terminals are always values in the tree, never branches. | |||
Lark filters out certain types of terminals by default, considering them punctuation: | |||
- Terminals that won't appear in the tree are: | |||
- Unnamed literals (like `"keyword"` or `"+"`) | |||
- Terminals whose name starts with an underscore (like `_DIGIT`) | |||
- Terminals that *will* appear in the tree are: | |||
- Unnamed regular expressions (like `/[0-9]/`) | |||
- Named terminals whose name starts with a letter (like `DIGIT`) | |||
Note: Terminals composed of literals and other terminals always include the entire match without filtering any part. | |||
**Example:** | |||
``` | |||
start: PNAME pname | |||
PNAME: "(" NAME ")" | |||
pname: "(" NAME ")" | |||
NAME: /\w+/ | |||
%ignore /\s+/ | |||
``` | |||
Lark will parse "(Hello) (World)" as: | |||
start | |||
(Hello) | |||
pname World | |||
Rules prefixed with `!` will retain all their literals regardless. | |||
**Example:** | |||
```perl | |||
expr: "(" expr ")" | |||
| NAME+ | |||
NAME: /\w+/ | |||
%ignore " " | |||
``` | |||
Lark will parse "((hello world))" as: | |||
expr | |||
expr | |||
expr | |||
"hello" | |||
"world" | |||
The brackets do not appear in the tree by design. The words appear because they are matched by a named terminal. | |||
## Shaping the tree | |||
Users can alter the automatic construction of the tree using a collection of grammar features. | |||
* Rules whose name begins with an underscore will be inlined into their containing rule. | |||
**Example:** | |||
```perl | |||
start: "(" _greet ")" | |||
_greet: /\w+/ /\w+/ | |||
``` | |||
Lark will parse "(hello world)" as: | |||
start | |||
"hello" | |||
"world" | |||
* Rules that receive a question mark (?) at the beginning of their definition, will be inlined if they have a single child, after filtering. | |||
**Example:** | |||
```ruby | |||
start: greet greet | |||
?greet: "(" /\w+/ ")" | |||
| /\w+/ /\w+/ | |||
``` | |||
Lark will parse "hello world (planet)" as: | |||
start | |||
greet | |||
"hello" | |||
"world" | |||
"planet" | |||
* Rules that begin with an exclamation mark will keep all their terminals (they won't get filtered). | |||
```perl | |||
!expr: "(" expr ")" | |||
| NAME+ | |||
NAME: /\w+/ | |||
%ignore " " | |||
``` | |||
Will parse "((hello world))" as: | |||
expr | |||
( | |||
expr | |||
( | |||
expr | |||
hello | |||
world | |||
) | |||
) | |||
Using the `!` prefix is usually a "code smell", and may point to a flaw in your grammar design. | |||
* Aliases - options in a rule can receive an alias. It will be then used as the branch name for the option, instead of the rule name. | |||
**Example:** | |||
```ruby | |||
start: greet greet | |||
greet: "hello" | |||
| "world" -> planet | |||
``` | |||
Lark will parse "hello world" as: | |||
start | |||
greet | |||
planet |
@@ -0,0 +1,119 @@ | |||
Transformers & Visitors | |||
======================= | |||
Transformers & Visitors provide a convenient interface to process the | |||
parse-trees that Lark returns. | |||
They are used by inheriting from the correct class (visitor or transformer), | |||
and implementing methods corresponding to the rule you wish to process. Each | |||
method accepts the children as an argument. That can be modified using the | |||
``v_args`` decorator, which allows one to inline the arguments (akin to ``*args``), | |||
or add the tree ``meta`` property as an argument. | |||
See: `visitors.py`_ | |||
.. _visitors.py: https://github.com/lark-parser/lark/blob/master/lark/visitors.py | |||
Visitor | |||
------- | |||
Visitors visit each node of the tree, and run the appropriate method on it according to the node's data. | |||
They work bottom-up, starting with the leaves and ending at the root of the tree. | |||
There are two classes that implement the visitor interface: | |||
- ``Visitor``: Visit every node (without recursion) | |||
- ``Visitor_Recursive``: Visit every node using recursion. Slightly faster. | |||
Example: | |||
:: | |||
class IncreaseAllNumbers(Visitor): | |||
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 | |||
----------- | |||
.. autoclass:: lark.visitors.Interpreter | |||
Example: | |||
:: | |||
class IncreaseSomeOfTheNumbers(Interpreter): | |||
def number(self, tree): | |||
tree.children[0] += 1 | |||
def skip(self, tree): | |||
# skip this subtree. don't change any number node inside it. | |||
pass | |||
IncreaseSomeOfTheNumbers().visit(parse_tree) | |||
Transformer | |||
----------- | |||
.. autoclass:: lark.visitors.Transformer | |||
:members: transform, __default__, __default_token__, __mul__ | |||
Example: | |||
:: | |||
from lark import Tree, Transformer | |||
class EvalExpressions(Transformer): | |||
def expr(self, args): | |||
return eval(args[0]) | |||
t = Tree('a', [Tree('expr', ['1+2'])]) | |||
print(EvalExpressions().transform( t )) | |||
# Prints: Tree(a, [3]) | |||
Example: | |||
:: | |||
class T(Transformer): | |||
INT = int | |||
NUMBER = float | |||
def NAME(self, name): | |||
return lookup_dict.get(name, name) | |||
T(visit_tokens=True).transform(tree) | |||
.. autoclass:: lark.visitors.Transformer_NonRecursive | |||
.. autoclass:: lark.visitors.Transformer_InPlace | |||
.. autoclass:: lark.visitors.Transformer_InPlaceRecursive | |||
v_args | |||
------ | |||
.. autofunction:: lark.visitors.v_args | |||
merge_transformers | |||
------------------ | |||
.. autofunction:: lark.visitors.merge_transformers | |||
Discard | |||
------- | |||
.. autoclass:: lark.visitors.Discard | |||
VisitError | |||
---------- | |||
.. autoclass:: lark.exceptions.VisitError |
@@ -1,13 +0,0 @@ | |||
#!/bin/sh | |||
runtime=$(TZ=UTC date +'%Y-%m-%dT%HZ') | |||
while read repourl name c; do | |||
baseref="gm/$runtime/$name" | |||
mkdir -p "gm/$name" | |||
git ls-remote "$repourl" > "gm/$name/${runtime}.refs.txt" | |||
#dr="--dry-run" | |||
git fetch $dr --no-tags "$repourl" +refs/tags/*:refs/tags/"$baseref/*" +refs/heads/*:refs/heads/"$baseref/*" | |||
done <<EOF | |||
$(python3 reponames.py < repos.txt) | |||
EOF |
@@ -0,0 +1,21 @@ | |||
Examples for Lark | |||
================= | |||
**How to run the examples**: | |||
After cloning the repo, open the terminal into the root directory of the | |||
project, and run the following: | |||
.. code:: bash | |||
[lark]$ python -m examples.<name_of_example> | |||
For example, the following will parse all the Python files in the | |||
standard library of your local installation: | |||
.. code:: bash | |||
[lark]$ python -m examples.advanced.python_parser | |||
Beginner Examples | |||
~~~~~~~~~~~~~~~~~ |
@@ -0,0 +1,2 @@ | |||
Advanced Examples | |||
~~~~~~~~~~~~~~~~~ |
@@ -0,0 +1,64 @@ | |||
""" | |||
Simple JSON Parser | |||
================== | |||
The code is short and clear, and outperforms every other parser (that's written in Python). | |||
For an explanation, check out the JSON parser tutorial at /docs/json_tutorial.md | |||
(this is here for use by the other examples) | |||
""" | |||
import sys | |||
from lark import Lark, Transformer, v_args | |||
json_grammar = r""" | |||
?start: value | |||
?value: object | |||
| array | |||
| string | |||
| SIGNED_NUMBER -> number | |||
| "true" -> true | |||
| "false" -> false | |||
| "null" -> null | |||
array : "[" [value ("," value)*] "]" | |||
object : "{" [pair ("," pair)*] "}" | |||
pair : string ":" value | |||
string : ESCAPED_STRING | |||
%import common.ESCAPED_STRING | |||
%import common.SIGNED_NUMBER | |||
%import common.WS | |||
%ignore WS | |||
""" | |||
class TreeToJson(Transformer): | |||
@v_args(inline=True) | |||
def string(self, s): | |||
return s[1:-1].replace('\\"', '"') | |||
array = list | |||
pair = tuple | |||
object = dict | |||
number = v_args(inline=True)(float) | |||
null = lambda self, _: None | |||
true = lambda self, _: True | |||
false = lambda self, _: False | |||
### Create the JSON parser with Lark, using the LALR algorithm | |||
json_parser = Lark(json_grammar, parser='lalr', | |||
# Using the basic lexer isn't required, and isn't usually recommended. | |||
# But, it's good enough for JSON, and it's slightly faster. | |||
lexer='basic', | |||
# Disabling propagate_positions and placeholders slightly improves speed | |||
propagate_positions=False, | |||
maybe_placeholders=False, | |||
# Using an internal transformer is faster and more memory efficient | |||
transformer=TreeToJson()) | |||
@@ -0,0 +1,45 @@ | |||
""" | |||
Earley’s dynamic lexer | |||
====================== | |||
Demonstrates the power of Earley’s dynamic lexer on a toy configuration language | |||
Using a lexer for configuration files is tricky, because values don't | |||
have to be surrounded by delimiters. Using a basic lexer for this just won't work. | |||
In this example we use a dynamic lexer and let the Earley parser resolve the ambiguity. | |||
Another approach is to use the contextual lexer with LALR. It is less powerful than Earley, | |||
but it can handle some ambiguity when lexing and it's much faster. | |||
See examples/conf_lalr.py for an example of that approach. | |||
""" | |||
from lark import Lark | |||
parser = Lark(r""" | |||
start: _NL? section+ | |||
section: "[" NAME "]" _NL item+ | |||
item: NAME "=" VALUE? _NL | |||
NAME: /\w/+ | |||
VALUE: /./+ | |||
%import common.NEWLINE -> _NL | |||
%import common.WS_INLINE | |||
%ignore WS_INLINE | |||
""", parser="earley") | |||
def test(): | |||
sample_conf = """ | |||
[bla] | |||
a=Hello | |||
this="that",4 | |||
empty= | |||
""" | |||
r = parser.parse(sample_conf) | |||
print (r.pretty()) | |||
if __name__ == '__main__': | |||
test() |
@@ -0,0 +1,43 @@ | |||
""" | |||
LALR’s contextual lexer | |||
======================= | |||
This example demonstrates the power of LALR's contextual lexer, | |||
by parsing a toy configuration language. | |||
The terminals `NAME` and `VALUE` overlap. They can match the same input. | |||
A basic lexer would arbitrarily choose one over the other, based on priority, | |||
which would lead to a (confusing) parse error. | |||
However, due to the unambiguous structure of the grammar, Lark's LALR(1) algorithm knows | |||
which one of them to expect at each point during the parse. | |||
The lexer then only matches the tokens that the parser expects. | |||
The result is a correct parse, something that is impossible with a regular lexer. | |||
Another approach is to use the Earley algorithm. | |||
It will handle more cases than the contextual lexer, but at the cost of performance. | |||
See examples/conf_earley.py for an example of that approach. | |||
""" | |||
from lark import Lark | |||
parser = Lark(r""" | |||
start: _NL? section+ | |||
section: "[" NAME "]" _NL item+ | |||
item: NAME "=" VALUE? _NL | |||
NAME: /\w/+ | |||
VALUE: /./+ | |||
%import common.NEWLINE -> _NL | |||
%import common.WS_INLINE | |||
%ignore WS_INLINE | |||
""", parser="lalr") | |||
sample_conf = """ | |||
[bla] | |||
a=Hello | |||
this="that",4 | |||
empty= | |||
""" | |||
print(parser.parse(sample_conf).pretty()) |
@@ -0,0 +1,121 @@ | |||
""" | |||
Creating an AST from the parse tree | |||
=================================== | |||
This example demonstrates how to transform a parse-tree into an AST using `lark.ast_utils`. | |||
create_transformer() collects every subclass of `Ast` subclass from the module, | |||
and creates a Lark transformer that builds the AST with no extra code. | |||
This example only works with Python 3. | |||
""" | |||
import sys | |||
from typing import List | |||
from dataclasses import dataclass | |||
from lark import Lark, ast_utils, Transformer, v_args | |||
from lark.tree import Meta | |||
this_module = sys.modules[__name__] | |||
# | |||
# Define AST | |||
# | |||
class _Ast(ast_utils.Ast): | |||
# This will be skipped by create_transformer(), because it starts with an underscore | |||
pass | |||
class _Statement(_Ast): | |||
# This will be skipped by create_transformer(), because it starts with an underscore | |||
pass | |||
@dataclass | |||
class Value(_Ast, ast_utils.WithMeta): | |||
"Uses WithMeta to include line-number metadata in the meta attribute" | |||
meta: Meta | |||
value: object | |||
@dataclass | |||
class Name(_Ast): | |||
name: str | |||
@dataclass | |||
class CodeBlock(_Ast, ast_utils.AsList): | |||
# Corresponds to code_block in the grammar | |||
statements: List[_Statement] | |||
@dataclass | |||
class If(_Statement): | |||
cond: Value | |||
then: CodeBlock | |||
@dataclass | |||
class SetVar(_Statement): | |||
# Corresponds to set_var in the grammar | |||
name: str | |||
value: Value | |||
@dataclass | |||
class Print(_Statement): | |||
value: Value | |||
class ToAst(Transformer): | |||
# Define extra transformation functions, for rules that don't correspond to an AST class. | |||
def STRING(self, s): | |||
# Remove quotation marks | |||
return s[1:-1] | |||
def DEC_NUMBER(self, n): | |||
return int(n) | |||
@v_args(inline=True) | |||
def start(self, x): | |||
return x | |||
# | |||
# Define Parser | |||
# | |||
parser = Lark(""" | |||
start: code_block | |||
code_block: statement+ | |||
?statement: if | set_var | print | |||
if: "if" value "{" code_block "}" | |||
set_var: NAME "=" value ";" | |||
print: "print" value ";" | |||
value: name | STRING | DEC_NUMBER | |||
name: NAME | |||
%import python (NAME, STRING, DEC_NUMBER) | |||
%import common.WS | |||
%ignore WS | |||
""", | |||
parser="lalr", | |||
) | |||
transformer = ast_utils.create_transformer(this_module, ToAst()) | |||
def parse(text): | |||
tree = parser.parse(text) | |||
return transformer.transform(tree) | |||
# | |||
# Test | |||
# | |||
if __name__ == '__main__': | |||
print(parse(""" | |||
a = 1; | |||
if a { | |||
print "a is 1"; | |||
a = 2; | |||
} | |||
""")) |
@@ -0,0 +1,57 @@ | |||
""" | |||
Custom lexer | |||
============ | |||
Demonstrates using a custom lexer to parse a non-textual stream of data | |||
You can use a custom lexer to tokenize text when the lexers offered by Lark | |||
are too slow, or not flexible enough. | |||
You can also use it (as shown in this example) to tokenize streams of objects. | |||
""" | |||
from lark import Lark, Transformer, v_args | |||
from lark.lexer import Lexer, Token | |||
class TypeLexer(Lexer): | |||
def __init__(self, lexer_conf): | |||
pass | |||
def lex(self, data): | |||
for obj in data: | |||
if isinstance(obj, int): | |||
yield Token('INT', obj) | |||
elif isinstance(obj, (type(''), type(u''))): | |||
yield Token('STR', obj) | |||
else: | |||
raise TypeError(obj) | |||
parser = Lark(""" | |||
start: data_item+ | |||
data_item: STR INT* | |||
%declare STR INT | |||
""", parser='lalr', lexer=TypeLexer) | |||
class ParseToDict(Transformer): | |||
@v_args(inline=True) | |||
def data_item(self, name, *numbers): | |||
return name.value, [n.value for n in numbers] | |||
start = dict | |||
def test(): | |||
data = ['alice', 1, 27, 3, 'bob', 4, 'carrie', 'dan', 8, 6] | |||
print(data) | |||
tree = parser.parse(data) | |||
res = ParseToDict().transform(tree) | |||
print('-->') | |||
print(res) # prints {'alice': [1, 27, 3], 'bob': [4], 'carrie': [], 'dan': [8, 6]} | |||
if __name__ == '__main__': | |||
test() |
@@ -0,0 +1,144 @@ | |||
""" | |||
Using lexer dynamic_complete | |||
============================ | |||
Demonstrates how to use ``lexer='dynamic_complete'`` and ``ambiguity='explicit'`` | |||
Sometimes you have data that is highly ambiguous or 'broken' in some sense. | |||
When using ``parser='earley'`` and ``lexer='dynamic_complete'``, Lark will be able | |||
parse just about anything as long as there is a valid way to generate it from | |||
the Grammar, including looking 'into' the Regexes. | |||
This examples shows how to parse a json input where the quotes have been | |||
replaced by underscores: ``{_foo_:{}, _bar_: [], _baz_: __}`` | |||
Notice that underscores might still appear inside strings, so a potentially | |||
valid reading of the above is: | |||
``{"foo_:{}, _bar": [], "baz": ""}`` | |||
""" | |||
from pprint import pprint | |||
from lark import Lark, Tree, Transformer, v_args | |||
from lark.visitors import Transformer_InPlace | |||
GRAMMAR = r""" | |||
%import common.SIGNED_NUMBER | |||
%import common.WS_INLINE | |||
%import common.NEWLINE | |||
%ignore WS_INLINE | |||
?start: value | |||
?value: object | |||
| array | |||
| string | |||
| SIGNED_NUMBER -> number | |||
| "true" -> true | |||
| "false" -> false | |||
| "null" -> null | |||
array : "[" [value ("," value)*] "]" | |||
object : "{" [pair ("," pair)*] "}" | |||
pair : string ":" value | |||
string: STRING | |||
STRING : ESCAPED_STRING | |||
ESCAPED_STRING: QUOTE_CHAR _STRING_ESC_INNER QUOTE_CHAR | |||
QUOTE_CHAR: "_" | |||
_STRING_INNER: /.*/ | |||
_STRING_ESC_INNER: _STRING_INNER /(?<!\\)(\\\\)*?/ | |||
""" | |||
def score(tree: Tree): | |||
""" | |||
Scores an option by how many children (and grand-children, and | |||
grand-grand-children, ...) it has. | |||
This means that the option with fewer large terminals get's selected | |||
Between | |||
object | |||
pair | |||
string _foo_ | |||
object | |||
pair | |||
string _bar_: [], _baz_ | |||
string __ | |||
and | |||
object | |||
pair | |||
string _foo_ | |||
object | |||
pair | |||
string _bar_ | |||
array | |||
pair | |||
string _baz_ | |||
string __ | |||
this will give the second a higher score. (9 vs 13) | |||
""" | |||
return sum(len(t.children) for t in tree.iter_subtrees()) | |||
class RemoveAmbiguities(Transformer_InPlace): | |||
""" | |||
Selects an option to resolve an ambiguity using the score function above. | |||
Scores each option and selects the one with the higher score, e.g. the one | |||
with more nodes. | |||
If there is a performance problem with the Tree having to many _ambig and | |||
being slow and to large, this can instead be written as a ForestVisitor. | |||
Look at the 'Custom SPPF Prioritizer' example. | |||
""" | |||
def _ambig(self, options): | |||
return max(options, key=score) | |||
class TreeToJson(Transformer): | |||
""" | |||
This is the same Transformer as the json_parser example. | |||
""" | |||
@v_args(inline=True) | |||
def string(self, s): | |||
return s[1:-1].replace('\\"', '"') | |||
array = list | |||
pair = tuple | |||
object = dict | |||
number = v_args(inline=True)(float) | |||
null = lambda self, _: None | |||
true = lambda self, _: True | |||
false = lambda self, _: False | |||
parser = Lark(GRAMMAR, parser='earley', ambiguity="explicit", lexer='dynamic_complete') | |||
EXAMPLES = [ | |||
r'{_array_:[1,2,3]}', | |||
r'{_abc_: _array must be of the following format [_1_, _2_, _3_]_}', | |||
r'{_foo_:{}, _bar_: [], _baz_: __}', | |||
r'{_error_:_invalid_client_, _error_description_:_AADSTS7000215: Invalid ' | |||
r'client secret is provided.\r\nTrace ID: ' | |||
r'a0a0aaaa-a0a0-0a00-000a-00a00aaa0a00\r\nCorrelation ID: ' | |||
r'aa0aaa00-0aaa-0000-00a0-00000aaaa0aa\r\nTimestamp: 1997-10-10 00:00:00Z_, ' | |||
r'_error_codes_:[7000215], _timestamp_:_1997-10-10 00:00:00Z_, ' | |||
r'_trace_id_:_a0a0aaaa-a0a0-0a00-000a-00a00aaa0a00_, ' | |||
r'_correlation_id_:_aa0aaa00-0aaa-0000-00a0-00000aaaa0aa_, ' | |||
r'_error_uri_:_https://example.com_}', | |||
] | |||
for example in EXAMPLES: | |||
tree = parser.parse(example) | |||
tree = RemoveAmbiguities().transform(tree) | |||
result = TreeToJson().transform(tree) | |||
print('-' * 100) | |||
pprint(result) |
@@ -0,0 +1,37 @@ | |||
""" | |||
Error handling using an interactive parser | |||
========================================== | |||
This example demonstrates error handling using an interactive parser in LALR | |||
When the parser encounters an UnexpectedToken exception, it creates a | |||
an interactive parser with the current parse-state, and lets you control how | |||
to proceed step-by-step. When you've achieved the correct parse-state, | |||
you can resume the run by returning True. | |||
""" | |||
from lark import Token | |||
from examples.advanced._json_parser import json_parser | |||
def ignore_errors(e): | |||
if e.token.type == 'COMMA': | |||
# Skip comma | |||
return True | |||
elif e.token.type == 'SIGNED_NUMBER': | |||
# Try to feed a comma and retry the number | |||
e.interactive_parser.feed_token(Token('COMMA', ',')) | |||
e.interactive_parser.feed_token(e.token) | |||
return True | |||
# Unhandled error. Will stop parse and raise exception | |||
return False | |||
def main(): | |||
s = "[0 1, 2,, 3,,, 4, 5 6 ]" | |||
res = json_parser.parse(s, on_error=ignore_errors) | |||
print(res) # prints [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0] | |||
main() | |||
@@ -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() | |||
@@ -0,0 +1,79 @@ | |||
""" | |||
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 | |||
from _json_parser import json_grammar # Using the grammar from the json_parser example | |||
json_parser = Lark(json_grammar, parser='lalr') | |||
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() | |||
@@ -0,0 +1,46 @@ | |||
""" | |||
Extend the Python Grammar | |||
============================== | |||
This example demonstrates how to use the `%extend` statement, | |||
to add new syntax to the example Python grammar. | |||
""" | |||
from lark.lark import Lark | |||
from python_parser import PythonIndenter | |||
GRAMMAR = r""" | |||
%import .python3 (compound_stmt, single_input, file_input, eval_input, test, suite, _NEWLINE, _INDENT, _DEDENT, COMMENT) | |||
%extend compound_stmt: match_stmt | |||
match_stmt: "match" test ":" cases | |||
cases: _NEWLINE _INDENT case+ _DEDENT | |||
case: "case" test ":" suite // test is not quite correct. | |||
%ignore /[\t \f]+/ // WS | |||
%ignore /\\[\t \f]*\r?\n/ // LINE_CONT | |||
%ignore COMMENT | |||
""" | |||
parser = Lark(GRAMMAR, parser='lalr', start=['single_input', 'file_input', 'eval_input'], postlex=PythonIndenter()) | |||
tree = parser.parse(r""" | |||
def name(n): | |||
match n: | |||
case 1: | |||
print("one") | |||
case 2: | |||
print("two") | |||
case _: | |||
print("number is too big") | |||
""", start='file_input') | |||
# Remove the 'python3__' prefix that was added to the implicitly imported rules. | |||
for t in tree.iter_subtrees(): | |||
t.data = t.data.rsplit('__', 1)[-1] | |||
print(tree.pretty()) |
@@ -0,0 +1,72 @@ | |||
""" | |||
Custom SPPF Prioritizer | |||
======================= | |||
This example demonstrates how to subclass ``ForestVisitor`` to make a custom | |||
SPPF node prioritizer to be used in conjunction with ``TreeForestTransformer``. | |||
Our prioritizer will count the number of descendants of a node that are tokens. | |||
By negating this count, our prioritizer will prefer nodes with fewer token | |||
descendants. Thus, we choose the more specific parse. | |||
""" | |||
from lark import Lark | |||
from lark.parsers.earley_forest import ForestVisitor, TreeForestTransformer | |||
class TokenPrioritizer(ForestVisitor): | |||
def visit_symbol_node_in(self, node): | |||
# visit the entire forest by returning node.children | |||
return node.children | |||
def visit_packed_node_in(self, node): | |||
return node.children | |||
def visit_symbol_node_out(self, node): | |||
priority = 0 | |||
for child in node.children: | |||
# Tokens do not have a priority attribute | |||
# count them as -1 | |||
priority += getattr(child, 'priority', -1) | |||
node.priority = priority | |||
def visit_packed_node_out(self, node): | |||
priority = 0 | |||
for child in node.children: | |||
priority += getattr(child, 'priority', -1) | |||
node.priority = priority | |||
def on_cycle(self, node, path): | |||
raise Exception("Oops, we encountered a cycle.") | |||
grammar = """ | |||
start: hello " " world | hello_world | |||
hello: "Hello" | |||
world: "World" | |||
hello_world: "Hello World" | |||
""" | |||
parser = Lark(grammar, parser='earley', ambiguity='forest') | |||
forest = parser.parse("Hello World") | |||
print("Default prioritizer:") | |||
tree = TreeForestTransformer(resolve_ambiguity=True).transform(forest) | |||
print(tree.pretty()) | |||
forest = parser.parse("Hello World") | |||
print("Custom prioritizer:") | |||
tree = TreeForestTransformer(resolve_ambiguity=True, prioritizer=TokenPrioritizer()).transform(forest) | |||
print(tree.pretty()) | |||
# Output: | |||
# | |||
# Default prioritizer: | |||
# start | |||
# hello Hello | |||
# | |||
# world World | |||
# | |||
# Custom prioritizer: | |||
# start | |||
# hello_world Hello World |
@@ -0,0 +1,168 @@ | |||
// Python 2 grammar for Lark | |||
// NOTE: Work in progress!!! (XXX TODO) | |||
// This grammar should parse all python 2.x code successfully, | |||
// but the resulting parse-tree is still not well-organized. | |||
// Adapted from: https://docs.python.org/2/reference/grammar.html | |||
// Adapted by: Erez Shinan | |||
// Start symbols for the grammar: | |||
// single_input is a single interactive statement; | |||
// file_input is a module or sequence of commands read from an input file; | |||
// eval_input is the input for the eval() and input() functions. | |||
// NB: compound_stmt in single_input is followed by extra _NEWLINE! | |||
single_input: _NEWLINE | simple_stmt | compound_stmt _NEWLINE | |||
?file_input: (_NEWLINE | stmt)* | |||
eval_input: testlist _NEWLINE? | |||
decorator: "@" dotted_name [ "(" [arglist] ")" ] _NEWLINE | |||
decorators: decorator+ | |||
decorated: decorators (classdef | funcdef) | |||
funcdef: "def" NAME "(" parameters ")" ":" suite | |||
parameters: [paramlist] | |||
paramlist: param ("," param)* ["," [star_params ["," kw_params] | kw_params]] | |||
| star_params ["," kw_params] | |||
| kw_params | |||
star_params: "*" NAME | |||
kw_params: "**" NAME | |||
param: fpdef ["=" test] | |||
fpdef: NAME | "(" fplist ")" | |||
fplist: fpdef ("," fpdef)* [","] | |||
?stmt: simple_stmt | compound_stmt | |||
?simple_stmt: small_stmt (";" small_stmt)* [";"] _NEWLINE | |||
?small_stmt: (expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt | |||
| import_stmt | global_stmt | exec_stmt | assert_stmt) | |||
expr_stmt: testlist augassign (yield_expr|testlist) -> augassign2 | |||
| testlist ("=" (yield_expr|testlist))+ -> assign | |||
| testlist | |||
augassign: ("+=" | "-=" | "*=" | "/=" | "%=" | "&=" | "|=" | "^=" | "<<=" | ">>=" | "**=" | "//=") | |||
// For normal assignments, additional restrictions enforced by the interpreter | |||
print_stmt: "print" ( [ test ("," test)* [","] ] | ">>" test [ ("," test)+ [","] ] ) | |||
del_stmt: "del" exprlist | |||
pass_stmt: "pass" | |||
?flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt | |||
break_stmt: "break" | |||
continue_stmt: "continue" | |||
return_stmt: "return" [testlist] | |||
yield_stmt: yield_expr | |||
raise_stmt: "raise" [test ["," test ["," test]]] | |||
import_stmt: import_name | import_from | |||
import_name: "import" dotted_as_names | |||
import_from: "from" ("."* dotted_name | "."+) "import" ("*" | "(" import_as_names ")" | import_as_names) | |||
?import_as_name: NAME ["as" NAME] | |||
?dotted_as_name: dotted_name ["as" NAME] | |||
import_as_names: import_as_name ("," import_as_name)* [","] | |||
dotted_as_names: dotted_as_name ("," dotted_as_name)* | |||
dotted_name: NAME ("." NAME)* | |||
global_stmt: "global" NAME ("," NAME)* | |||
exec_stmt: "exec" expr ["in" test ["," test]] | |||
assert_stmt: "assert" test ["," test] | |||
?compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | |||
if_stmt: "if" test ":" suite ("elif" test ":" suite)* ["else" ":" suite] | |||
while_stmt: "while" test ":" suite ["else" ":" suite] | |||
for_stmt: "for" exprlist "in" testlist ":" suite ["else" ":" suite] | |||
try_stmt: ("try" ":" suite ((except_clause ":" suite)+ ["else" ":" suite] ["finally" ":" suite] | "finally" ":" suite)) | |||
with_stmt: "with" with_item ("," with_item)* ":" suite | |||
with_item: test ["as" expr] | |||
// NB compile.c makes sure that the default except clause is last | |||
except_clause: "except" [test [("as" | ",") test]] | |||
suite: simple_stmt | _NEWLINE _INDENT _NEWLINE? stmt+ _DEDENT _NEWLINE? | |||
// Backward compatibility cruft to support: | |||
// [ x for x in lambda: True, lambda: False if x() ] | |||
// even while also allowing: | |||
// lambda x: 5 if x else 2 | |||
// (But not a mix of the two) | |||
testlist_safe: old_test [("," old_test)+ [","]] | |||
old_test: or_test | old_lambdef | |||
old_lambdef: "lambda" [paramlist] ":" old_test | |||
?test: or_test ["if" or_test "else" test] | lambdef | |||
?or_test: and_test ("or" and_test)* | |||
?and_test: not_test ("and" not_test)* | |||
?not_test: "not" not_test | comparison | |||
?comparison: expr (comp_op expr)* | |||
comp_op: "<"|">"|"=="|">="|"<="|"<>"|"!="|"in"|"not" "in"|"is"|"is" "not" | |||
?expr: xor_expr ("|" xor_expr)* | |||
?xor_expr: and_expr ("^" and_expr)* | |||
?and_expr: shift_expr ("&" shift_expr)* | |||
?shift_expr: arith_expr (("<<"|">>") arith_expr)* | |||
?arith_expr: term (("+"|"-") term)* | |||
?term: factor (("*"|"/"|"%"|"//") factor)* | |||
?factor: ("+"|"-"|"~") factor | power | |||
?power: molecule ["**" factor] | |||
// _trailer: "(" [arglist] ")" | "[" subscriptlist "]" | "." NAME | |||
?molecule: molecule "(" [arglist] ")" -> func_call | |||
| molecule "[" [subscriptlist] "]" -> getitem | |||
| molecule "." NAME -> getattr | |||
| atom | |||
?atom: "(" [yield_expr|testlist_comp] ")" -> tuple | |||
| "[" [listmaker] "]" | |||
| "{" [dictorsetmaker] "}" | |||
| "`" testlist1 "`" | |||
| "(" test ")" | |||
| NAME | number | string+ | |||
listmaker: test ( list_for | ("," test)* [","] ) | |||
?testlist_comp: test ( comp_for | ("," test)+ [","] | ",") | |||
lambdef: "lambda" [paramlist] ":" test | |||
?subscriptlist: subscript ("," subscript)* [","] | |||
subscript: "." "." "." | test | [test] ":" [test] [sliceop] | |||
sliceop: ":" [test] | |||
?exprlist: expr ("," expr)* [","] | |||
?testlist: test ("," test)* [","] | |||
dictorsetmaker: ( (test ":" test (comp_for | ("," test ":" test)* [","])) | (test (comp_for | ("," test)* [","])) ) | |||
classdef: "class" NAME ["(" [testlist] ")"] ":" suite | |||
arglist: (argument ",")* (argument [","] | |||
| star_args ["," kw_args] | |||
| kw_args) | |||
star_args: "*" test | |||
kw_args: "**" test | |||
// The reason that keywords are test nodes instead of NAME is that using NAME | |||
// results in an ambiguity. ast.c makes sure it's a NAME. | |||
argument: test [comp_for] | test "=" test | |||
list_iter: list_for | list_if | |||
list_for: "for" exprlist "in" testlist_safe [list_iter] | |||
list_if: "if" old_test [list_iter] | |||
comp_iter: comp_for | comp_if | |||
comp_for: "for" exprlist "in" or_test [comp_iter] | |||
comp_if: "if" old_test [comp_iter] | |||
testlist1: test ("," test)* | |||
yield_expr: "yield" [testlist] | |||
number: DEC_NUMBER | HEX_NUMBER | OCT_NUMBER | FLOAT | IMAG_NUMBER | |||
string: STRING | LONG_STRING | |||
// Tokens | |||
COMMENT: /#[^\n]*/ | |||
_NEWLINE: ( /\r?\n[\t ]*/ | COMMENT )+ | |||
STRING : /[ubf]?r?("(?!"").*?(?<!\\)(\\\\)*?"|'(?!'').*?(?<!\\)(\\\\)*?')/i | |||
LONG_STRING.2: /[ubf]?r?(""".*?(?<!\\)(\\\\)*?"""|'''.*?(?<!\\)(\\\\)*?''')/is | |||
DEC_NUMBER: /[1-9]\d*l?/i | |||
HEX_NUMBER: /0x[\da-f]*l?/i | |||
OCT_NUMBER: /0o?[0-7]*l?/i | |||
%import common.FLOAT -> FLOAT | |||
%import common.INT -> _INT | |||
%import common.CNAME -> NAME | |||
IMAG_NUMBER: (_INT | FLOAT) ("j"|"J") | |||
%ignore /[\t \f]+/ // WS | |||
%ignore /\\[\t \f]*\r?\n/ // LINE_CONT | |||
%ignore COMMENT | |||
%declare _INDENT _DEDENT | |||
@@ -0,0 +1,88 @@ | |||
""" | |||
Grammar-complete Python Parser | |||
============================== | |||
A fully-working Python 2 & 3 parser (but not production ready yet!) | |||
This example demonstrates usage of the included Python grammars | |||
""" | |||
import sys | |||
import os, os.path | |||
from io import open | |||
import glob, time | |||
from lark import Lark | |||
from lark.indenter import Indenter | |||
class PythonIndenter(Indenter): | |||
NL_type = '_NEWLINE' | |||
OPEN_PAREN_types = ['LPAR', 'LSQB', 'LBRACE'] | |||
CLOSE_PAREN_types = ['RPAR', 'RSQB', 'RBRACE'] | |||
INDENT_type = '_INDENT' | |||
DEDENT_type = '_DEDENT' | |||
tab_len = 8 | |||
kwargs = dict(postlex=PythonIndenter(), start='file_input') | |||
# Official Python grammar by Lark | |||
python_parser3 = Lark.open_from_package('lark', 'python.lark', ['grammars'], parser='lalr', **kwargs) | |||
# Local Python2 grammar | |||
python_parser2 = Lark.open('python2.lark', rel_to=__file__, parser='lalr', **kwargs) | |||
python_parser2_earley = Lark.open('python2.lark', rel_to=__file__, parser='earley', lexer='basic', **kwargs) | |||
try: | |||
xrange | |||
except NameError: | |||
chosen_parser = python_parser3 | |||
else: | |||
chosen_parser = python_parser2 | |||
def _read(fn, *args): | |||
kwargs = {'encoding': 'iso-8859-1'} | |||
with open(fn, *args, **kwargs) as f: | |||
return f.read() | |||
def _get_lib_path(): | |||
if os.name == 'nt': | |||
if 'PyPy' in sys.version: | |||
return os.path.join(sys.prefix, 'lib-python', sys.winver) | |||
else: | |||
return os.path.join(sys.prefix, 'Lib') | |||
else: | |||
return [x for x in sys.path if x.endswith('%s.%s' % sys.version_info[:2])][0] | |||
def test_python_lib(): | |||
path = _get_lib_path() | |||
start = time.time() | |||
files = glob.glob(path+'/*.py') | |||
total_kb = 0 | |||
for f in files: | |||
r = _read(os.path.join(path, f)) | |||
kb = len(r) / 1024 | |||
print( '%s -\t%.1f kb' % (f, kb)) | |||
chosen_parser.parse(r + '\n') | |||
total_kb += kb | |||
end = time.time() | |||
print( "test_python_lib (%d files, %.1f kb), time: %.2f secs"%(len(files), total_kb, end-start) ) | |||
def test_earley_equals_lalr(): | |||
path = _get_lib_path() | |||
files = glob.glob(path+'/*.py') | |||
for f in files: | |||
print( f ) | |||
tree1 = python_parser2.parse(_read(os.path.join(path, f)) + '\n') | |||
tree2 = python_parser2_earley.parse(_read(os.path.join(path, f)) + '\n') | |||
assert tree1 == tree2 | |||
if __name__ == '__main__': | |||
test_python_lib() | |||
# test_earley_equals_lalr() | |||
# python_parser3.parse(_read(sys.argv[1]) + '\n') | |||
@@ -0,0 +1,205 @@ | |||
""" | |||
Syntax Highlighting | |||
=================== | |||
This example shows how to write a syntax-highlighted editor with Qt and Lark | |||
Requirements: | |||
PyQt5==5.10.1 | |||
QScintilla==2.10.4 | |||
""" | |||
import sys | |||
import textwrap | |||
from PyQt5.Qt import * # noqa | |||
from PyQt5.Qsci import QsciScintilla | |||
from PyQt5.Qsci import QsciLexerCustom | |||
from lark import Lark | |||
class LexerJson(QsciLexerCustom): | |||
def __init__(self, parent=None): | |||
super().__init__(parent) | |||
self.create_parser() | |||
self.create_styles() | |||
def create_styles(self): | |||
deeppink = QColor(249, 38, 114) | |||
khaki = QColor(230, 219, 116) | |||
mediumpurple = QColor(174, 129, 255) | |||
mediumturquoise = QColor(81, 217, 205) | |||
yellowgreen = QColor(166, 226, 46) | |||
lightcyan = QColor(213, 248, 232) | |||
darkslategrey = QColor(39, 40, 34) | |||
styles = { | |||
0: mediumturquoise, | |||
1: mediumpurple, | |||
2: yellowgreen, | |||
3: deeppink, | |||
4: khaki, | |||
5: lightcyan | |||
} | |||
for style, color in styles.items(): | |||
self.setColor(color, style) | |||
self.setPaper(darkslategrey, style) | |||
self.setFont(self.parent().font(), style) | |||
self.token_styles = { | |||
"COLON": 5, | |||
"COMMA": 5, | |||
"LBRACE": 5, | |||
"LSQB": 5, | |||
"RBRACE": 5, | |||
"RSQB": 5, | |||
"FALSE": 0, | |||
"NULL": 0, | |||
"TRUE": 0, | |||
"STRING": 4, | |||
"NUMBER": 1, | |||
} | |||
def create_parser(self): | |||
grammar = ''' | |||
anons: ":" "{" "}" "," "[" "]" | |||
TRUE: "true" | |||
FALSE: "false" | |||
NULL: "NULL" | |||
%import common.ESCAPED_STRING -> STRING | |||
%import common.SIGNED_NUMBER -> NUMBER | |||
%import common.WS | |||
%ignore WS | |||
''' | |||
self.lark = Lark(grammar, parser=None, lexer='basic') | |||
# All tokens: print([t.name for t in self.lark.parser.lexer.tokens]) | |||
def defaultPaper(self, style): | |||
return QColor(39, 40, 34) | |||
def language(self): | |||
return "Json" | |||
def description(self, style): | |||
return {v: k for k, v in self.token_styles.items()}.get(style, "") | |||
def styleText(self, start, end): | |||
self.startStyling(start) | |||
text = self.parent().text()[start:end] | |||
last_pos = 0 | |||
try: | |||
for token in self.lark.lex(text): | |||
ws_len = token.pos_in_stream - last_pos | |||
if ws_len: | |||
self.setStyling(ws_len, 0) # whitespace | |||
token_len = len(bytearray(token, "utf-8")) | |||
self.setStyling( | |||
token_len, self.token_styles.get(token.type, 0)) | |||
last_pos = token.pos_in_stream + token_len | |||
except Exception as e: | |||
print(e) | |||
class EditorAll(QsciScintilla): | |||
def __init__(self, parent=None): | |||
super().__init__(parent) | |||
# Set font defaults | |||
font = QFont() | |||
font.setFamily('Consolas') | |||
font.setFixedPitch(True) | |||
font.setPointSize(8) | |||
font.setBold(True) | |||
self.setFont(font) | |||
# Set margin defaults | |||
fontmetrics = QFontMetrics(font) | |||
self.setMarginsFont(font) | |||
self.setMarginWidth(0, fontmetrics.width("000") + 6) | |||
self.setMarginLineNumbers(0, True) | |||
self.setMarginsForegroundColor(QColor(128, 128, 128)) | |||
self.setMarginsBackgroundColor(QColor(39, 40, 34)) | |||
self.setMarginType(1, self.SymbolMargin) | |||
self.setMarginWidth(1, 12) | |||
# Set indentation defaults | |||
self.setIndentationsUseTabs(False) | |||
self.setIndentationWidth(4) | |||
self.setBackspaceUnindents(True) | |||
self.setIndentationGuides(True) | |||
# self.setFolding(QsciScintilla.CircledFoldStyle) | |||
# Set caret defaults | |||
self.setCaretForegroundColor(QColor(247, 247, 241)) | |||
self.setCaretWidth(2) | |||
# Set selection color defaults | |||
self.setSelectionBackgroundColor(QColor(61, 61, 52)) | |||
self.resetSelectionForegroundColor() | |||
# Set multiselection defaults | |||
self.SendScintilla(QsciScintilla.SCI_SETMULTIPLESELECTION, True) | |||
self.SendScintilla(QsciScintilla.SCI_SETMULTIPASTE, 1) | |||
self.SendScintilla( | |||
QsciScintilla.SCI_SETADDITIONALSELECTIONTYPING, True) | |||
lexer = LexerJson(self) | |||
self.setLexer(lexer) | |||
EXAMPLE_TEXT = textwrap.dedent("""\ | |||
{ | |||
"_id": "5b05ffcbcf8e597939b3f5ca", | |||
"about": "Excepteur consequat commodo esse voluptate aute aliquip ad sint deserunt commodo eiusmod irure. Sint aliquip sit magna duis eu est culpa aliqua excepteur ut tempor nulla. Aliqua ex pariatur id labore sit. Quis sit ex aliqua veniam exercitation laboris anim adipisicing. Lorem nisi reprehenderit ullamco labore qui sit ut aliqua tempor consequat pariatur proident.", | |||
"address": "665 Malbone Street, Thornport, Louisiana, 243", | |||
"age": 23, | |||
"balance": "$3,216.91", | |||
"company": "BULLJUICE", | |||
"email": "elisekelley@bulljuice.com", | |||
"eyeColor": "brown", | |||
"gender": "female", | |||
"guid": "d3a6d865-0f64-4042-8a78-4f53de9b0707", | |||
"index": 0, | |||
"isActive": false, | |||
"isActive2": true, | |||
"latitude": -18.660714, | |||
"longitude": -85.378048, | |||
"name": "Elise Kelley", | |||
"phone": "+1 (808) 543-3966", | |||
"picture": "http://placehold.it/32x32", | |||
"registered": "2017-09-30T03:47:40 -02:00", | |||
"tags": [ | |||
"et", | |||
"nostrud", | |||
"in", | |||
"fugiat", | |||
"incididunt", | |||
"labore", | |||
"nostrud" | |||
] | |||
}\ | |||
""") | |||
def main(): | |||
app = QApplication(sys.argv) | |||
ex = EditorAll() | |||
ex.setWindowTitle(__file__) | |||
ex.setText(EXAMPLE_TEXT) | |||
ex.resize(800, 600) | |||
ex.show() | |||
sys.exit(app.exec_()) | |||
if __name__ == "__main__": | |||
main() |
@@ -0,0 +1,50 @@ | |||
""" | |||
Reconstruct a JSON | |||
================== | |||
Demonstrates the experimental text-reconstruction feature | |||
The Reconstructor takes a parse tree (already filtered from punctuation, of course), | |||
and reconstructs it into correct text, that can be parsed correctly. | |||
It can be useful for creating "hooks" to alter data before handing it to other parsers. You can also use it to generate samples from scratch. | |||
""" | |||
import json | |||
from lark import Lark | |||
from lark.reconstruct import Reconstructor | |||
from _json_parser import json_grammar | |||
test_json = ''' | |||
{ | |||
"empty_object" : {}, | |||
"empty_array" : [], | |||
"booleans" : { "YES" : true, "NO" : false }, | |||
"numbers" : [ 0, 1, -2, 3.3, 4.4e5, 6.6e-7 ], | |||
"strings" : [ "This", [ "And" , "That", "And a \\"b" ] ], | |||
"nothing" : null | |||
} | |||
''' | |||
def test_earley(): | |||
json_parser = Lark(json_grammar, maybe_placeholders=False) | |||
tree = json_parser.parse(test_json) | |||
new_json = Reconstructor(json_parser).reconstruct(tree) | |||
print (new_json) | |||
print (json.loads(new_json) == json.loads(test_json)) | |||
def test_lalr(): | |||
json_parser = Lark(json_grammar, parser='lalr', maybe_placeholders=False) | |||
tree = json_parser.parse(test_json) | |||
new_json = Reconstructor(json_parser).reconstruct(tree) | |||
print (new_json) | |||
print (json.loads(new_json) == json.loads(test_json)) | |||
test_earley() | |||
test_lalr() |
@@ -0,0 +1,72 @@ | |||
""" | |||
Reconstruct Python | |||
================== | |||
Demonstrates how Lark's experimental text-reconstruction feature can recreate | |||
functional Python code from its parse-tree, using just the correct grammar and | |||
a small formatter. | |||
""" | |||
from lark import Token | |||
from lark.reconstruct import Reconstructor | |||
from python_parser import python_parser3 | |||
SPACE_AFTER = set(',+-*/~@<>="|:') | |||
SPACE_BEFORE = (SPACE_AFTER - set(',:')) | set('\'') | |||
def special(sym): | |||
return Token('SPECIAL', sym.name) | |||
def postproc(items): | |||
stack = ['\n'] | |||
actions = [] | |||
last_was_whitespace = True | |||
for item in items: | |||
if isinstance(item, Token) and item.type == 'SPECIAL': | |||
actions.append(item.value) | |||
else: | |||
if actions: | |||
assert actions[0] == '_NEWLINE' and '_NEWLINE' not in actions[1:], actions | |||
for a in actions[1:]: | |||
if a == '_INDENT': | |||
stack.append(stack[-1] + ' ' * 4) | |||
else: | |||
assert a == '_DEDENT' | |||
stack.pop() | |||
actions.clear() | |||
yield stack[-1] | |||
last_was_whitespace = True | |||
if not last_was_whitespace: | |||
if item[0] in SPACE_BEFORE: | |||
yield ' ' | |||
yield item | |||
last_was_whitespace = item[-1].isspace() | |||
if not last_was_whitespace: | |||
if item[-1] in SPACE_AFTER: | |||
yield ' ' | |||
last_was_whitespace = True | |||
yield "\n" | |||
python_reconstruct = Reconstructor(python_parser3, {'_NEWLINE': special, '_DEDENT': special, '_INDENT': special}) | |||
def test(): | |||
self_contents = open(__file__).read() | |||
tree = python_parser3.parse(self_contents+'\n') | |||
output = python_reconstruct.reconstruct(tree, postproc) | |||
tree_new = python_parser3.parse(output) | |||
assert tree == tree_new | |||
print(output) | |||
if __name__ == '__main__': | |||
test() |
@@ -0,0 +1,56 @@ | |||
start: (_item | _NL)* | |||
_item: rule | |||
| token | |||
| statement | |||
_rule_or_token: RULE | |||
| TOKEN | |||
rule: RULE rule_params priority? ":" expansions{_rule_or_token} _NL | |||
token: TOKEN priority? ":" expansions{TOKEN} _NL | |||
rule_params: ["{" RULE ("," RULE)* "}"] | |||
priority: "." NUMBER | |||
statement: "%ignore" expansions{TOKEN} _NL -> ignore | |||
| "%import" import_path{_rule_or_token} ["->" _rule_or_token] _NL -> import | |||
| "%import" import_path{_rule_or_token} name_list{_rule_or_token} _NL -> multi_import | |||
| "%declare" TOKEN+ -> declare | |||
!import_path{name}: "."? name ("." name)* | |||
name_list{name}: "(" name ("," name)* ")" | |||
?expansions{name}: alias{name} (_VBAR alias{name})* | |||
?alias{name}: expansion{name} ["->" RULE] | |||
?expansion{name}: expr{name}* | |||
?expr{name}: atom{name} [OP | "~" NUMBER [".." NUMBER]] | |||
?atom{name}: "(" expansions{name} ")" | |||
| "[" expansions{name} "]" -> maybe | |||
| value{name} | |||
?value{name}: STRING ".." STRING -> literal_range | |||
| name | |||
| (REGEXP | STRING) -> literal | |||
| name "{" value{name} ("," value{name})* "}" -> template_usage | |||
_VBAR: _NL? "|" | |||
OP: /[+*]|[?](?![a-z])/ | |||
RULE: /!?[_?]?[a-z][_a-z0-9]*/ | |||
TOKEN: /_?[A-Z][_A-Z0-9]*/ | |||
STRING: _STRING "i"? | |||
REGEXP: /\/(?!\/)(\\\/|\\\\|[^\/\n])*?\/[imslux]*/ | |||
_NL: /(\r?\n)+\s*/ | |||
%import common.ESCAPED_STRING -> _STRING | |||
%import common.INT -> NUMBER | |||
%import common.WS_INLINE | |||
COMMENT: /\s*/ "//" /[^\n]/* | |||
%ignore WS_INLINE | |||
%ignore COMMENT |
@@ -0,0 +1,29 @@ | |||
""" | |||
Templates | |||
========= | |||
This example shows how to use Lark's templates to achieve cleaner grammars | |||
""" | |||
from lark import Lark | |||
grammar = r""" | |||
start: list | dict | |||
list: "[" _seperated{atom, ","} "]" | |||
dict: "{" _seperated{key_value, ","} "}" | |||
key_value: atom ":" atom | |||
_seperated{x, sep}: x (sep x)* // Define a sequence of 'x sep x sep x ...' | |||
atom: NUMBER | ESCAPED_STRING | |||
%import common (NUMBER, ESCAPED_STRING, WS) | |||
%ignore WS | |||
""" | |||
parser = Lark(grammar) | |||
print(parser.parse('[1, "a", 2]')) | |||
print(parser.parse('{"a": 2, "b": 6}')) |
@@ -0,0 +1,58 @@ | |||
""" | |||
Transform a Forest | |||
================== | |||
This example demonstrates how to subclass ``TreeForestTransformer`` to | |||
directly transform a SPPF. | |||
""" | |||
from lark import Lark | |||
from lark.parsers.earley_forest import TreeForestTransformer, handles_ambiguity, Discard | |||
class CustomTransformer(TreeForestTransformer): | |||
@handles_ambiguity | |||
def sentence(self, trees): | |||
return next(tree for tree in trees if tree.data == 'simple') | |||
def simple(self, children): | |||
children.append('.') | |||
return self.tree_class('simple', children) | |||
def adj(self, children): | |||
raise Discard() | |||
def __default_token__(self, token): | |||
return token.capitalize() | |||
grammar = """ | |||
sentence: noun verb noun -> simple | |||
| noun verb "like" noun -> comparative | |||
noun: adj? NOUN | |||
verb: VERB | |||
adj: ADJ | |||
NOUN: "flies" | "bananas" | "fruit" | |||
VERB: "like" | "flies" | |||
ADJ: "fruit" | |||
%import common.WS | |||
%ignore WS | |||
""" | |||
parser = Lark(grammar, start='sentence', ambiguity='forest') | |||
sentence = 'fruit flies like bananas' | |||
forest = parser.parse(sentence) | |||
tree = CustomTransformer(resolve_ambiguity=False).transform(forest) | |||
print(tree.pretty()) | |||
# Output: | |||
# | |||
# simple | |||
# noun Flies | |||
# verb Like | |||
# noun Bananas | |||
# . | |||
# |
@@ -0,0 +1,82 @@ | |||
""" | |||
Basic calculator | |||
================ | |||
A simple example of a REPL calculator | |||
This example shows how to write a basic calculator with variables. | |||
""" | |||
from lark import Lark, Transformer, v_args | |||
try: | |||
input = raw_input # For Python2 compatibility | |||
except NameError: | |||
pass | |||
calc_grammar = """ | |||
?start: sum | |||
| NAME "=" sum -> assign_var | |||
?sum: product | |||
| sum "+" product -> add | |||
| sum "-" product -> sub | |||
?product: atom | |||
| product "*" atom -> mul | |||
| product "/" atom -> div | |||
?atom: NUMBER -> number | |||
| "-" atom -> neg | |||
| NAME -> var | |||
| "(" sum ")" | |||
%import common.CNAME -> NAME | |||
%import common.NUMBER | |||
%import common.WS_INLINE | |||
%ignore WS_INLINE | |||
""" | |||
@v_args(inline=True) # Affects the signatures of the methods | |||
class CalculateTree(Transformer): | |||
from operator import add, sub, mul, truediv as div, neg | |||
number = float | |||
def __init__(self): | |||
self.vars = {} | |||
def assign_var(self, name, value): | |||
self.vars[name] = value | |||
return value | |||
def var(self, name): | |||
try: | |||
return self.vars[name] | |||
except KeyError: | |||
raise Exception("Variable not found: %s" % name) | |||
calc_parser = Lark(calc_grammar, parser='lalr', transformer=CalculateTree()) | |||
calc = calc_parser.parse | |||
def main(): | |||
while True: | |||
try: | |||
s = input('> ') | |||
except EOFError: | |||
break | |||
print(calc(s)) | |||
def test(): | |||
print(calc("a = 1+2")) | |||
print(calc("1+a*-3")) | |||
if __name__ == '__main__': | |||
# test() | |||
main() |
@@ -0,0 +1,10 @@ | |||
Grammar Composition | |||
=================== | |||
This example shows how to do grammar composition in Lark, by creating a new | |||
file format that allows both CSV and JSON to co-exist. | |||
We show how, by using namespaces, Lark grammars and their transformers can be fully reused - | |||
they don't need to care if their grammar is used directly, or being imported, or who is doing the importing. | |||
See [``main.py``](main.py) for more details. |
@@ -0,0 +1,6 @@ | |||
{"header": ["this", "is", "json", 1111]} | |||
# file lines author | |||
data.json 12 Robin | |||
data.csv 30 erezsh | |||
compiler.py 123123 Megalng | |||
{"footer": "done"} |
@@ -0,0 +1,14 @@ | |||
start: header _NL row+ | |||
header: "#" " "? (WORD _SEPARATOR?)+ | |||
row: (_anything _SEPARATOR?)+ _NL | |||
_anything: INT | WORD | NON_SEPARATOR_STRING | FLOAT | SIGNED_FLOAT | |||
NON_SEPARATOR_STRING: /[a-zA-z.;\\\/]+/ | |||
_SEPARATOR: /[ ]+/ | |||
| "\t" | |||
| "," | |||
%import common.NEWLINE -> _NL | |||
%import common.WORD | |||
%import common.INT | |||
%import common.FLOAT | |||
%import common.SIGNED_FLOAT |
@@ -0,0 +1,26 @@ | |||
"Transformer for evaluating csv.lark" | |||
from lark import Transformer | |||
class CsvTreeToPandasDict(Transformer): | |||
INT = int | |||
FLOAT = float | |||
SIGNED_FLOAT = float | |||
WORD = str | |||
NON_SEPARATOR_STRING = str | |||
def row(self, children): | |||
return children | |||
def start(self, children): | |||
data = {} | |||
header = children[0].children | |||
for heading in header: | |||
data[heading] = [] | |||
for row in children[1:]: | |||
for i, element in enumerate(row): | |||
data[header[i]].append(element) | |||
return data |
@@ -0,0 +1,17 @@ | |||
"Transformer for evaluating json.lark" | |||
from lark import Transformer, v_args | |||
class JsonTreeToJson(Transformer): | |||
@v_args(inline=True) | |||
def string(self, s): | |||
return s[1:-1].replace('\\"', '"') | |||
array = list | |||
pair = tuple | |||
object = dict | |||
number = v_args(inline=True)(float) | |||
null = lambda self, _: None | |||
true = lambda self, _: True | |||
false = lambda self, _: False |
@@ -0,0 +1,19 @@ | |||
?start: value | |||
?value: object | |||
| array | |||
| string | |||
| SIGNED_NUMBER -> number | |||
| "true" -> true | |||
| "false" -> false | |||
| "null" -> null | |||
array : "[" _WS? [value ("," _WS? value)*] "]" | |||
object : "{" _WS? [pair ("," _WS? pair)*] "}" | |||
pair : string ":" _WS value | |||
string : ESCAPED_STRING | |||
%import common.ESCAPED_STRING | |||
%import common.SIGNED_NUMBER | |||
%import common.WS -> _WS |
@@ -0,0 +1,51 @@ | |||
""" | |||
Grammar Composition | |||
=================== | |||
This example shows how to do grammar composition in Lark, by creating a new | |||
file format that allows both CSV and JSON to co-exist. | |||
1) We define ``storage.lark``, which imports both ``csv.lark`` and ``json.lark``, | |||
and allows them to be used one after the other. | |||
In the generated tree, each imported rule/terminal is automatically prefixed (with ``json__`` or ``csv__), | |||
which creates an implicit namespace and allows them to coexist without collisions. | |||
2) We merge their respective transformers (unaware of each other) into a new base transformer. | |||
The resulting transformer can evaluate both JSON and CSV in the parse tree. | |||
The methods of each transformer are renamed into their appropriate namespace, using the given prefix. | |||
This approach allows full re-use: the transformers don't need to care if their grammar is used directly, | |||
or being imported, or who is doing the importing. | |||
""" | |||
from pathlib import Path | |||
from lark import Lark | |||
from json import dumps | |||
from lark.visitors import Transformer, merge_transformers | |||
from eval_csv import CsvTreeToPandasDict | |||
from eval_json import JsonTreeToJson | |||
__dir__ = Path(__file__).parent | |||
class Storage(Transformer): | |||
def start(self, children): | |||
return children | |||
storage_transformer = merge_transformers(Storage(), csv=CsvTreeToPandasDict(), json=JsonTreeToJson()) | |||
parser = Lark.open("storage.lark", rel_to=__file__) | |||
def main(): | |||
json_tree = parser.parse(dumps({"test": "a", "dict": { "list": [1, 1.2] }})) | |||
res = storage_transformer.transform(json_tree) | |||
print("Just JSON: ", res) | |||
csv_json_tree = parser.parse(open(__dir__ / 'combined_csv_and_json.txt').read()) | |||
res = storage_transformer.transform(csv_json_tree) | |||
print("JSON + CSV: ", dumps(res, indent=2)) | |||
if __name__ == "__main__": | |||
main() |
@@ -0,0 +1,9 @@ | |||
start: (csv__start | json__start _NL?)+ | |||
// Renaming of the import variables is required, as they receive the namespace of this file. | |||
// See: https://github.com/lark-parser/lark/pull/973#issuecomment-907287565 | |||
%import .csv.start -> csv__start | |||
%import .json.start -> json__start | |||
%import .csv._NL -> _NL | |||
@@ -0,0 +1,58 @@ | |||
""" | |||
Handling Ambiguity | |||
================== | |||
A demonstration of ambiguity | |||
This example shows how to use get explicit ambiguity from Lark's Earley parser. | |||
""" | |||
import sys | |||
from lark import Lark, tree | |||
grammar = """ | |||
sentence: noun verb noun -> simple | |||
| noun verb "like" noun -> comparative | |||
noun: adj? NOUN | |||
verb: VERB | |||
adj: ADJ | |||
NOUN: "flies" | "bananas" | "fruit" | |||
VERB: "like" | "flies" | |||
ADJ: "fruit" | |||
%import common.WS | |||
%ignore WS | |||
""" | |||
parser = Lark(grammar, start='sentence', ambiguity='explicit') | |||
sentence = 'fruit flies like bananas' | |||
def make_png(filename): | |||
tree.pydot__tree_to_png( parser.parse(sentence), filename) | |||
def make_dot(filename): | |||
tree.pydot__tree_to_dot( parser.parse(sentence), filename) | |||
if __name__ == '__main__': | |||
print(parser.parse(sentence).pretty()) | |||
# make_png(sys.argv[1]) | |||
# make_dot(sys.argv[1]) | |||
# Output: | |||
# | |||
# _ambig | |||
# comparative | |||
# noun fruit | |||
# verb flies | |||
# noun bananas | |||
# simple | |||
# noun | |||
# fruit | |||
# flies | |||
# verb like | |||
# noun bananas | |||
# | |||
# (or view a nicer version at "./fruitflies.png") |
@@ -0,0 +1,55 @@ | |||
""" | |||
Parsing Indentation | |||
=================== | |||
A demonstration of parsing indentation (“whitespace significant” language) | |||
and the usage of the Indenter class. | |||
Since indentation is context-sensitive, a postlex stage is introduced to | |||
manufacture INDENT/DEDENT tokens. | |||
It is crucial for the indenter that the NL_type matches | |||
the spaces (and tabs) after the newline. | |||
""" | |||
from lark import Lark | |||
from lark.indenter import Indenter | |||
tree_grammar = r""" | |||
?start: _NL* tree | |||
tree: NAME _NL [_INDENT tree+ _DEDENT] | |||
%import common.CNAME -> NAME | |||
%import common.WS_INLINE | |||
%declare _INDENT _DEDENT | |||
%ignore WS_INLINE | |||
_NL: /(\r?\n[\t ]*)+/ | |||
""" | |||
class TreeIndenter(Indenter): | |||
NL_type = '_NL' | |||
OPEN_PAREN_types = [] | |||
CLOSE_PAREN_types = [] | |||
INDENT_type = '_INDENT' | |||
DEDENT_type = '_DEDENT' | |||
tab_len = 8 | |||
parser = Lark(tree_grammar, parser='lalr', postlex=TreeIndenter()) | |||
test_tree = """ | |||
a | |||
b | |||
c | |||
d | |||
e | |||
f | |||
g | |||
""" | |||
def test(): | |||
print(parser.parse(test_tree).pretty()) | |||
if __name__ == '__main__': | |||
test() | |||
@@ -0,0 +1,91 @@ | |||
""" | |||
Simple JSON Parser | |||
================== | |||
The code is short and clear, and outperforms every other parser (that's written in Python). | |||
For an explanation, check out the JSON parser tutorial at /docs/json_tutorial.md | |||
""" | |||
import sys | |||
from lark import Lark, Transformer, v_args | |||
json_grammar = r""" | |||
?start: value | |||
?value: object | |||
| array | |||
| string | |||
| SIGNED_NUMBER -> number | |||
| "true" -> true | |||
| "false" -> false | |||
| "null" -> null | |||
array : "[" [value ("," value)*] "]" | |||
object : "{" [pair ("," pair)*] "}" | |||
pair : string ":" value | |||
string : ESCAPED_STRING | |||
%import common.ESCAPED_STRING | |||
%import common.SIGNED_NUMBER | |||
%import common.WS | |||
%ignore WS | |||
""" | |||
class TreeToJson(Transformer): | |||
@v_args(inline=True) | |||
def string(self, s): | |||
return s[1:-1].replace('\\"', '"') | |||
array = list | |||
pair = tuple | |||
object = dict | |||
number = v_args(inline=True)(float) | |||
null = lambda self, _: None | |||
true = lambda self, _: True | |||
false = lambda self, _: False | |||
### Create the JSON parser with Lark, using the Earley algorithm | |||
# json_parser = Lark(json_grammar, parser='earley', lexer='basic') | |||
# def parse(x): | |||
# return TreeToJson().transform(json_parser.parse(x)) | |||
### Create the JSON parser with Lark, using the LALR algorithm | |||
json_parser = Lark(json_grammar, parser='lalr', | |||
# Using the basic lexer isn't required, and isn't usually recommended. | |||
# But, it's good enough for JSON, and it's slightly faster. | |||
lexer='basic', | |||
# Disabling propagate_positions and placeholders slightly improves speed | |||
propagate_positions=False, | |||
maybe_placeholders=False, | |||
# Using an internal transformer is faster and more memory efficient | |||
transformer=TreeToJson()) | |||
parse = json_parser.parse | |||
def test(): | |||
test_json = ''' | |||
{ | |||
"empty_object" : {}, | |||
"empty_array" : [], | |||
"booleans" : { "YES" : true, "NO" : false }, | |||
"numbers" : [ 0, 1, -2, 3.3, 4.4e5, 6.6e-7 ], | |||
"strings" : [ "This", [ "And" , "That", "And a \\"b" ] ], | |||
"nothing" : null | |||
} | |||
''' | |||
j = parse(test_json) | |||
print(j) | |||
import json | |||
assert j == json.loads(test_json) | |||
if __name__ == '__main__': | |||
# test() | |||
with open(sys.argv[1]) as f: | |||
print(parse(f.read())) |
@@ -0,0 +1,36 @@ | |||
""" | |||
Lark Grammar | |||
============ | |||
A reference implementation of the Lark grammar (using LALR(1)) | |||
""" | |||
import lark | |||
from pathlib import Path | |||
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 / 'advanced/python2.lark', | |||
examples_path / 'relative-imports/multiples.lark', | |||
examples_path / 'relative-imports/multiple2.lark', | |||
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(): | |||
for grammar_file in grammar_files: | |||
tree = parser.parse(open(grammar_file).read()) | |||
print("All grammars parsed successfully") | |||
if __name__ == '__main__': | |||
test() |
@@ -0,0 +1 @@ | |||
start: ("0" | "1")* "0" |
@@ -0,0 +1,5 @@ | |||
start: mod0mod0+ | |||
mod0mod0: "0" | "1" mod1mod0 | |||
mod1mod0: "1" | "0" mod2mod1 mod1mod0 | |||
mod2mod1: "0" | "1" mod2mod1 |
@@ -0,0 +1,5 @@ | |||
start: "2:" multiple2 | |||
| "3:" multiple3 | |||
%import .multiple2.start -> multiple2 | |||
%import .multiple3.start -> multiple3 |
@@ -0,0 +1,28 @@ | |||
# | |||
# This example demonstrates relative imports with rule rewrite | |||
# see multiples.lark | |||
# | |||
# | |||
# if b is a number written in binary, and m is either 2 or 3, | |||
# the grammar aims to recognise m:b iif b is a multiple of m | |||
# | |||
# for example, 3:1001 is recognised | |||
# because 9 (0b1001) is a multiple of 3 | |||
# | |||
from lark import Lark, UnexpectedInput | |||
parser = Lark.open('multiples.lark', parser='lalr') | |||
def is_in_grammar(data): | |||
try: | |||
parser.parse(data) | |||
except UnexpectedInput: | |||
return False | |||
return True | |||
for n_dec in range(100): | |||
n_bin = bin(n_dec)[2:] | |||
assert is_in_grammar('2:{}'.format(n_bin)) == (n_dec % 2 == 0) | |||
assert is_in_grammar('3:{}'.format(n_bin)) == (n_dec % 3 == 0) |
@@ -0,0 +1,20 @@ | |||
# Standalone example | |||
To initialize, cd to this folder, and run: | |||
```bash | |||
./create_standalone.sh | |||
``` | |||
Or: | |||
```bash | |||
python -m lark.tools.standalone json.lark > json_parser.py | |||
```` | |||
Then run using: | |||
```bash | |||
python json_parser_main.py <path-to.json> | |||
``` | |||
@@ -0,0 +1,2 @@ | |||
#!/bin/sh | |||
PYTHONPATH=../.. python -m lark.tools.standalone json.lark > json_parser.py |
@@ -0,0 +1,21 @@ | |||
?start: value | |||
?value: object | |||
| array | |||
| string | |||
| SIGNED_NUMBER -> number | |||
| "true" -> true | |||
| "false" -> false | |||
| "null" -> null | |||
array : "[" [value ("," value)*] "]" | |||
object : "{" [pair ("," pair)*] "}" | |||
pair : string ":" value | |||
string : ESCAPED_STRING | |||
%import common.ESCAPED_STRING | |||
%import common.SIGNED_NUMBER | |||
%import common.WS | |||
%ignore WS |
@@ -0,0 +1,37 @@ | |||
""" | |||
Standalone Parser | |||
=================================== | |||
This example demonstrates how to generate and use the standalone parser, | |||
using the JSON example. | |||
See README.md for more details. | |||
""" | |||
import sys | |||
from json_parser import Lark_StandAlone, Transformer, v_args | |||
inline_args = v_args(inline=True) | |||
class TreeToJson(Transformer): | |||
@inline_args | |||
def string(self, s): | |||
return s[1:-1].replace('\\"', '"') | |||
array = list | |||
pair = tuple | |||
object = dict | |||
number = inline_args(float) | |||
null = lambda self, _: None | |||
true = lambda self, _: True | |||
false = lambda self, _: False | |||
parser = Lark_StandAlone(transformer=TreeToJson()) | |||
if __name__ == '__main__': | |||
with open(sys.argv[1]) as f: | |||
print(parser.parse(f.read())) | |||
@@ -0,0 +1,2 @@ | |||
start: r | |||
r.-1: "a" |
@@ -0,0 +1 @@ | |||
start: "a" |
@@ -0,0 +1,90 @@ | |||
""" | |||
Turtle DSL | |||
========== | |||
Implements a LOGO-like toy language for Python’s turtle, with interpreter. | |||
""" | |||
try: | |||
input = raw_input # For Python2 compatibility | |||
except NameError: | |||
pass | |||
import turtle | |||
from lark import Lark | |||
turtle_grammar = """ | |||
start: instruction+ | |||
instruction: MOVEMENT NUMBER -> movement | |||
| "c" COLOR [COLOR] -> change_color | |||
| "fill" code_block -> fill | |||
| "repeat" NUMBER code_block -> repeat | |||
code_block: "{" instruction+ "}" | |||
MOVEMENT: "f"|"b"|"l"|"r" | |||
COLOR: LETTER+ | |||
%import common.LETTER | |||
%import common.INT -> NUMBER | |||
%import common.WS | |||
%ignore WS | |||
""" | |||
parser = Lark(turtle_grammar) | |||
def run_instruction(t): | |||
if t.data == 'change_color': | |||
turtle.color(*t.children) # We just pass the color names as-is | |||
elif t.data == 'movement': | |||
name, number = t.children | |||
{ 'f': turtle.fd, | |||
'b': turtle.bk, | |||
'l': turtle.lt, | |||
'r': turtle.rt, }[name](int(number)) | |||
elif t.data == 'repeat': | |||
count, block = t.children | |||
for i in range(int(count)): | |||
run_instruction(block) | |||
elif t.data == 'fill': | |||
turtle.begin_fill() | |||
run_instruction(t.children[0]) | |||
turtle.end_fill() | |||
elif t.data == 'code_block': | |||
for cmd in t.children: | |||
run_instruction(cmd) | |||
else: | |||
raise SyntaxError('Unknown instruction: %s' % t.data) | |||
def run_turtle(program): | |||
parse_tree = parser.parse(program) | |||
for inst in parse_tree.children: | |||
run_instruction(inst) | |||
def main(): | |||
while True: | |||
code = input('> ') | |||
try: | |||
run_turtle(code) | |||
except Exception as e: | |||
print(e) | |||
def test(): | |||
text = """ | |||
c red yellow | |||
fill { repeat 36 { | |||
f200 l170 | |||
}} | |||
""" | |||
run_turtle(text) | |||
if __name__ == '__main__': | |||
# test() | |||
main() |