syntactes
A simpler Python parser generator.
The name is derived from Greek συντάκτης (/sin'daktis/) meaning editor/composer.
Features
- Parsing table creation (LR0, SLR and LR1)
- Token parsing, with callbacks that compute values on each reduction
Installation
> pip install syntactes
Quick start
Creating a parsing table
from syntactes import Grammar, Rule, SLRGenerator, Token
EOF = Token.eof()
S = Token("S", is_terminal=False)
E = Token("E", False)
T = Token("T", False)
x = Token("x", True)
PLUS = Token("+", True)
tokens = {EOF, S, E, T, x, PLUS}
# 0. S -> E $
# 1. E -> T + E
# 2. E -> T
# 3. T -> x
rule_1 = Rule(0, S, E, EOF)
rule_2 = Rule(1, E, T, PLUS, E)
rule_3 = Rule(2, E, T)
rule_4 = Rule(3, T, x)
rules = (rule_1, rule_2, rule_3, rule_4)
grammar = Grammar(rule_1, rules, tokens)
generator = SLRGenerator(grammar)
parsing_table = generator.generate()
print(parsing_table.pretty_str())
Running the above example produces this output:
GRAMMAR RULES
-------------
0. S -> E $
1. E -> T + E
2. E -> T
3. T -> x
-------------
SLR PARSING TABLE
-------------------------------------------------
| | $ | + | E | S | T | x |
-------------------------------------------------
| 1 | -- | -- | s2 | -- | s3 | s4 |
-------------------------------------------------
| 2 | a | -- | -- | -- | -- | -- |
-------------------------------------------------
| 3 | r2 | s5 | -- | -- | -- | -- |
-------------------------------------------------
| 4 | r3 | r3 | -- | -- | -- | -- |
-------------------------------------------------
| 5 | -- | -- | s6 | -- | s3 | s4 |
-------------------------------------------------
| 6 | r1 | -- | -- | -- | -- | -- |
-------------------------------------------------
Parsing
from syntactes import Grammar, Rule, Token
from syntactes.parser import ParserError, SLRParser
EOF = Token.eof()
S = Token("S", is_terminal=False)
E = Token("E", False)
T = Token("T", False)
x = Token("x", True)
PLUS = Token("+", True)
tokens = {EOF, S, E, T, x, PLUS}
# 0. S -> E $
# 1. E -> T + E
# 2. E -> T
# 3. T -> x
rule_1 = Rule(0, S, E, EOF)
rule_2 = Rule(1, E, T, PLUS, E)
rule_3 = Rule(2, E, T)
rule_4 = Rule(3, T, x)
rules = (rule_1, rule_2, rule_3, rule_4)
grammar = Grammar(rule_1, rules, tokens)
parser = SLRParser.from_grammar(grammar)
@parser.execute_on(rule_1)
def result(e):
# The starting rule's callback gets every right-hand side token except $.
# Its return value is returned by parse().
return e.value
@parser.execute_on(rule_2)
def add(t, plus, e):
# One token per right-hand side symbol, in order. A non-terminal's value is
# whatever the callback of the rule that produced it returned.
print(f"reducing by {rule_2}: {t.value} {plus} {e.value}")
return t.value + e.value
@parser.execute_on(rule_3)
def term(t):
return t.value
@parser.execute_on(rule_4)
def number(x_token):
# Terminals carry the value they were created with.
return x_token.value
def num(value):
return Token("x", True, value)
print("Parsing stream: 1 + 2 + 3 $\n")
print("result:", parser.parse([num(1), PLUS, num(2), PLUS, num(3), EOF]))
print("\nParsing stream: 1 + $\n")
try:
parser.parse([num(1), PLUS, EOF])
except ParserError as e:
print("ParserError:", e)
Running the above example produces this output:
Parsing stream: 1 + 2 + 3 $
reducing by E -> T + E: 2 + 3
reducing by E -> T + E: 1 + 5
result: 6
Parsing stream: 1 + $
ParserError: Received token: $; expected one of: ['x']
Callbacks
Callbacks are registered on a parser with @parser.execute_on(rule). Registering
a rule that is not in the parser's grammar raises ValueError.
- On each reduction, the rule's callback is called with one token per right-hand side symbol, in order.
- The callback's return value becomes the
valueof the token pushed for the left-hand side, so the callback of an enclosing rule receives it. A rule without a callback pushes a token whose value isNone. - When the parser accepts, the starting rule's callback is called with its
right-hand side tokens, except the trailing
$. Its return value is returned byparse(). Without a callback for the starting rule,parse()returnsNone. - Exceptions raised in callbacks propagate out of
parse()unchanged.
Errors
parse() raises a syntactes.parser.ParserError:
UnexpectedTokenErrorif a token has no action in the current state, or if tokens follow the$that completed the parse. Itsexpected_tokensholds the terminals that would have been accepted, sorted.NotAcceptedErrorif the stream ends before the parser accepts.
Grammars
Grammar raises syntactes.GrammarError (a ValueError) if:
- the starting rule is not in the rules, or doesn't end with
$; $appears anywhere other than at the end of the starting rule;- a rule's left-hand side is a terminal;
- a rule uses a symbol that is not in the tokens (
εis always allowed); - a non-terminal has no rules;
- two rules have the same number.
An empty rule can be written as Rule(n, A) or Rule(n, A, Token.null()). Its
callback is called with no arguments.
Conflicts
table.conflicts() lists the cells of a parsing table that hold more than one
action. When parsing, conflicts are resolved the way yacc resolves them: shift
wins over reduce, and between reduces the rule with the lowest number wins. For
example, with E -> E + E, x + x + x is parsed as x + (x + x).
Metadata
Release files for syntactes 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| syntactes-0.5.0.tar.gz | 17.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| syntactes-0.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 36.7 kB
Release files / syntactes-0.5.0.tar.gz
| Download URL | syntactes-0.5.0.tar.gz |
|---|---|
| Size | 17.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
56ea0a10218857899e27f554a20bbec1d30995bf03bcef0fbce5c330ae169953
|
|
BLAKE2b-256 checksum How to use checksums |
060578387a1a7b88745f80f428bcd461420a182fdfeece1f67eccfc7bde6e052
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Linux Mint","version":"21.3","id":"virginia","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / syntactes-0.5.0-py3-none-any.whl
| Download URL | syntactes-0.5.0-py3-none-any.whl |
|---|---|
| Size | 19.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ed968be5f201005d9c6de07b23ace57787cf3ed71af7c1419ae0be9a120ee303
|
|
BLAKE2b-256 checksum How to use checksums |
8b9f737b2fb6a61f5af163a6269087edb25c4fc1bf5bd08ac1949c13f5c779f3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Linux Mint","version":"21.3","id":"virginia","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|