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.