Quickstart#
Installation#
You can install grap with pip:
python -m pip install grap
Write a Basic Grammar#
Let’s write a basic grammar for a mathematical expression parser.
from grap.core.rules import rule
from grap.core.common import AsciiDigit, OnceOrMore, Optional, RuleUnion
@rule
def main():
yield OnceOrMore(AsciiDigit())
yield Optional(whitespace())
yield operator()
yield Optional(whitespace())
yield OnceOrMore(AsciiDigit())
@rule
def whitespace():
yield OnceOrMore(" ")
@rule
def operator():
yield RuleUnion(("+", "-", "*", "/"))
Save this file as math_expr.py.
Parse Text#
It is recommended to use rich for
debugging. Install it with python -m pip install rich. Highlighting
is automatically enabled when rich is installed.
After you have saved the gramar file you are able to parse text with it. Use the python REPL in the same directory as your grammar file.
>>> from grap.core import parse
>>> from rich import print
>>>
>>> from math_expr import main
>>>
>>>
>>> print(parse(main(), "42 - 7"))
ParsedRule(
name='main',
rule=<grap.core.rules.rule.<locals>.decorator.<locals>.R object at
0x7f54d204ae30>,
match='42 - 7',
span=(0, 6),
parent=None,
inner=[
ParsedRule(
name='ASCII Digit+',
rule=OnceOrMore(
rule=<grap.core.rules.rule.<locals>.decorator.<locals>.R object
at 0x7f54d1673760>,
name='ASCII Digit+'
),
match='42',
span=(0, 2),
parent=OnceOrMore(
rule=<grap.core.rules.rule.<locals>.decorator.<locals>.R object
at 0x7f54d1673760>,
name='ASCII Digit+'
),
inner=[
ParsedRule(
name='ASCII Digit',
rule=<grap.core.rules.rule.<locals>.decorator.<locals>.R
object at 0x7f54d1673760>,
match='4',
span=(0, 1),
parent=<grap.core.rules.rule.<locals>.decorator.<locals>.R
object at 0x7f54d1673760>,
inner=[
ParsedRule(
name="('0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9')",
rule=RuleUnion(
rules=[
String(
string='0',
case_sensitive=True,
name="'0'"
),
String(
string='1',
case_sensitive=True,
name="'1'"
),
String(
string='2',
case_sensitive=True,
name="'2'"
),
String(
string='3',
case_sensitive=True,
name="'3'"
),
String(
string='4',
case_sensitive=True,
name="'4'"
),
String(
string='5',
case_sensitive=True,
name="'5'"
),
String(
string='6',
case_sensitive=True,
name="'6'"
),
String(
string='7',
case_sensitive=True,
name="'7'"
),
String(
string='8',
case_sensitive=True,
name="'8'"
),
String(
string='9',
case_sensitive=True,
name="'9'"
)
],
name="('0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9')"
),
match='4',
span=(0, 1),
parent=RuleUnion(
rules=[
String(
string='0',
case_sensitive=True,
name="'0'"
),
String(
string='1',
case_sensitive=True,
name="'1'"
),
String(
string='2',
case_sensitive=True,
name="'2'"
),
String(
string='3',
case_sensitive=True,
name="'3'"
),
String(
string='4',
case_sensitive=True,
name="'4'"
),
String(
string='5',
case_sensitive=True,
name="'5'"
),
String(
string='6',
case_sensitive=True,
name="'6'"
),
String(
string='7',
case_sensitive=True,
name="'7'"
),
String(
string='8',
case_sensitive=True,
name="'8'"
),
String(
string='9',
case_sensitive=True,
name="'9'"
)
],
name="('0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9')"
),
inner=[
ParsedRule(
name="'4'",
rule=String(
string='4',
case_sensitive=True,
name="'4'"
),
match='4',
span=(0, 1),
parent=String(
string='4',
case_sensitive=True,
name="'4'"
),
inner=[]
)
]
)
]
),
ParsedRule(
name='ASCII Digit',
rule=<grap.core.rules.rule.<locals>.decorator.<locals>.R
object at 0x7f54d1673760>,
match='2',
span=(1, 2),
parent=<grap.core.rules.rule.<locals>.decorator.<locals>.R
object at 0x7f54d1673760>,
inner=[
ParsedRule(
name="('0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9')",
rule=RuleUnion(
rules=[
String(
string='0',
case_sensitive=True,
name="'0'"
),
String(
string='1',
case_sensitive=True,
name="'1'"
),
String(
string='2',
case_sensitive=True,
name="'2'"
),
String(
string='3',
case_sensitive=True,
name="'3'"
),
String(
string='4',
case_sensitive=True,
name="'4'"
),
String(
string='5',
case_sensitive=True,
name="'5'"
),
String(
string='6',
case_sensitive=True,
name="'6'"
),
String(
string='7',
case_sensitive=True,
name="'7'"
),
String(
string='8',
case_sensitive=True,
name="'8'"
),
String(
string='9',
case_sensitive=True,
name="'9'"
)
],
name="('0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9')"
),
match='2',
span=(1, 2),
parent=RuleUnion(
rules=[
String(
string='0',
case_sensitive=True,
name="'0'"
),
String(
string='1',
case_sensitive=True,
name="'1'"
),
String(
string='2',
case_sensitive=True,
name="'2'"
),
String(
string='3',
case_sensitive=True,
name="'3'"
),
String(
string='4',
case_sensitive=True,
name="'4'"
),
String(
string='5',
case_sensitive=True,
name="'5'"
),
String(
string='6',
case_sensitive=True,
name="'6'"
),
String(
string='7',
case_sensitive=True,
name="'7'"
),
String(
string='8',
case_sensitive=True,
name="'8'"
),
String(
string='9',
case_sensitive=True,
name="'9'"
)
],
name="('0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9')"
),
inner=[
ParsedRule(
name="'2'",
rule=String(
string='2',
case_sensitive=True,
name="'2'"
),
match='2',
span=(1, 2),
parent=String(
string='2',
case_sensitive=True,
name="'2'"
),
inner=[]
)
]
)
]
)
]
),
ParsedRule(
name='whitespace?',
rule=Optional(
rule=<grap.core.rules.rule.<locals>.decorator.<locals>.R object
at 0x7f54d1673820>,
name='whitespace?'
),
match=' ',
span=(2, 3),
parent=Optional(
rule=<grap.core.rules.rule.<locals>.decorator.<locals>.R object
at 0x7f54d1673820>,
name='whitespace?'
),
inner=[
ParsedRule(
name='whitespace',
rule=<grap.core.rules.rule.<locals>.decorator.<locals>.R
object at 0x7f54d1673820>,
match=' ',
span=(2, 3),
parent=<grap.core.rules.rule.<locals>.decorator.<locals>.R
object at 0x7f54d1673820>,
inner=[
ParsedRule(
name=' +',
rule=OnceOrMore(rule=' ', name=' +'),
match=' ',
span=(2, 3),
parent=OnceOrMore(rule=' ', name=' +'),
inner=[]
)
]
)
]
),
ParsedRule(
name='operator',
rule=<grap.core.rules.rule.<locals>.decorator.<locals>.R object at
0x7f54d1673910>,
match='-',
span=(3, 4),
parent=<grap.core.rules.rule.<locals>.decorator.<locals>.R object at
0x7f54d1673910>,
inner=[
ParsedRule(
name='(+|-|*|/)',
rule=RuleUnion(
rules=('+', '-', '*', '/'),
name='(+|-|*|/)'
),
match='-',
span=(3, 4),
parent=RuleUnion(
rules=('+', '-', '*', '/'),
name='(+|-|*|/)'
),
inner=[]
)
]
),
ParsedRule(
name='whitespace?',
rule=Optional(
rule=<grap.core.rules.rule.<locals>.decorator.<locals>.R object
at 0x7f54d16738e0>,
name='whitespace?'
),
match=' ',
span=(4, 5),
parent=Optional(
rule=<grap.core.rules.rule.<locals>.decorator.<locals>.R object
at 0x7f54d16738e0>,
name='whitespace?'
),
inner=[
ParsedRule(
name='whitespace',
rule=<grap.core.rules.rule.<locals>.decorator.<locals>.R
object at 0x7f54d16738e0>,
match=' ',
span=(4, 5),
parent=<grap.core.rules.rule.<locals>.decorator.<locals>.R
object at 0x7f54d16738e0>,
inner=[
ParsedRule(
name=' +',
rule=OnceOrMore(rule=' ', name=' +'),
match=' ',
span=(4, 5),
parent=OnceOrMore(rule=' ', name=' +'),
inner=[]
)
]
)
]
),
ParsedRule(
name='ASCII Digit+',
rule=OnceOrMore(
rule=<grap.core.rules.rule.<locals>.decorator.<locals>.R object
at 0x7f54d1673d30>,
name='ASCII Digit+'
),
match='7',
span=(5, 6),
parent=OnceOrMore(
rule=<grap.core.rules.rule.<locals>.decorator.<locals>.R object
at 0x7f54d1673d30>,
name='ASCII Digit+'
),
inner=[
ParsedRule(
name='ASCII Digit',
rule=<grap.core.rules.rule.<locals>.decorator.<locals>.R
object at 0x7f54d1673d30>,
match='7',
span=(5, 6),
parent=<grap.core.rules.rule.<locals>.decorator.<locals>.R
object at 0x7f54d1673d30>,
inner=[
ParsedRule(
name="('0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9')",
rule=RuleUnion(
rules=[
String(
string='0',
case_sensitive=True,
name="'0'"
),
String(
string='1',
case_sensitive=True,
name="'1'"
),
String(
string='2',
case_sensitive=True,
name="'2'"
),
String(
string='3',
case_sensitive=True,
name="'3'"
),
String(
string='4',
case_sensitive=True,
name="'4'"
),
String(
string='5',
case_sensitive=True,
name="'5'"
),
String(
string='6',
case_sensitive=True,
name="'6'"
),
String(
string='7',
case_sensitive=True,
name="'7'"
),
String(
string='8',
case_sensitive=True,
name="'8'"
),
String(
string='9',
case_sensitive=True,
name="'9'"
)
],
name="('0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9')"
),
match='7',
span=(5, 6),
parent=RuleUnion(
rules=[
String(
string='0',
case_sensitive=True,
name="'0'"
),
String(
string='1',
case_sensitive=True,
name="'1'"
),
String(
string='2',
case_sensitive=True,
name="'2'"
),
String(
string='3',
case_sensitive=True,
name="'3'"
),
String(
string='4',
case_sensitive=True,
name="'4'"
),
String(
string='5',
case_sensitive=True,
name="'5'"
),
String(
string='6',
case_sensitive=True,
name="'6'"
),
String(
string='7',
case_sensitive=True,
name="'7'"
),
String(
string='8',
case_sensitive=True,
name="'8'"
),
String(
string='9',
case_sensitive=True,
name="'9'"
)
],
name="('0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9')"
),
inner=[
ParsedRule(
name="'7'",
rule=String(
string='7',
case_sensitive=True,
name="'7'"
),
match='7',
span=(5, 6),
parent=String(
string='7',
case_sensitive=True,
name="'7'"
),
inner=[]
)
]
)
]
)
]
)
]
)
This will print a large parse tree.