Пример #1
0
def test_extend_grammar():
    grammar1 = Grammar()
    grammar1.add_pattern(grammar1.add_token('A'), 'a+')
    grammar1.add_pattern(grammar1.add_token('B'), 'b+')
    grammar1.add_pattern(grammar1.add_token('C'), 'c+')
    grammar1.add_parselet('expr', kind=ParseletKind.Pratt)

    grammar2 = Grammar()
    grammar2.add_pattern(grammar2.add_token('A'), '_a+')
    grammar2.add_pattern(grammar2.add_token('B'), '_b+')
    grammar2.add_parselet('expr', kind=ParseletKind.Pratt)

    result = Grammar()
    initial_count = len(result.symbols)
    result.extend(grammar1)
    result.extend(grammar2)

    assert 'A' in result.tokens
    assert 'B' in result.tokens
    assert 'C' in result.tokens
    assert len(result.symbols) == initial_count + 4
    assert len(result.parselets) == 1
    assert len(result.patterns) == 5
    assert {pattern.pattern.pattern
            for pattern in result.patterns
            } == {'a+', '_a+', 'b+', '_b+', 'c+'}
Пример #2
0
def test_add_incorrect_pratt_parser():
    grammar = Grammar()
    stmt_id = grammar.add_parselet('stmt',
                                   kind=ParseletKind.Pratt,
                                   result_type=SyntaxToken)
    expr_id = grammar.add_parselet('expr',
                                   kind=ParseletKind.Pratt,
                                   result_type=SyntaxToken)
    integer_id = grammar.add_token('Integer')

    with pytest.raises(GrammarError):
        grammar.add_parser(expr_id, make_optional(integer_id))

    with pytest.raises(GrammarError):
        grammar.add_parser(expr_id, make_sequence(stmt_id))

    with pytest.raises(GrammarError):
        grammar.add_parser(expr_id, make_sequence(expr_id, stmt_id))

    with pytest.raises(GrammarError):
        grammar.add_parser(expr_id,
                           make_sequence(expr_id, make_optional(stmt_id)))

    with pytest.raises(GrammarError):
        grammar.add_parser(expr_id, make_sequence(expr_id, expr_id))
Пример #3
0
def test_extend_fail_grammar():
    grammar1 = Grammar()
    grammar1.add_parselet('expr', kind=ParseletKind.Pratt)

    grammar2 = Grammar()
    grammar2.add_parselet('expr', kind=ParseletKind.Packrat)

    with pytest.raises(GrammarError):
        Grammar.merge(grammar1, grammar2)
Пример #4
0
def test_add_incorrect_parselet():
    grammar = Grammar()
    symbol_count = len(grammar.symbols)
    for name in {'+', 'Name'}:
        with pytest.raises(GrammarError):
            grammar.add_parselet(name)

    assert len(
        grammar.tokens
    ) == symbol_count, "Count of symbols in grammar is changed after failed call"
    assert len(
        grammar.symbols
    ) == symbol_count, "Count of symbols in grammar is changed after failed call"
Пример #5
0
def test_add_parselet():
    grammar = Grammar()
    symbol_count = len(grammar.symbols)
    expr_id = grammar.add_parselet('expr')

    assert expr_id.kind == ParseletKind.Packrat
    assert len(grammar.parselets) == 1
    assert len(grammar.symbols) == symbol_count + 1
Пример #6
0
def test_make_parselet():
    grammar = Grammar()
    name_id = grammar.add_parselet('name')

    comb = make_parselet(name_id)
    assert isinstance(comb, ParseletCombinator)
    assert comb.parser_id == name_id
    assert comb.result_type == SyntaxNode
    assert comb.variables == {}
Пример #7
0
def test_extend_packrat_grammar():
    grammar1 = Grammar()
    grammar1.add_token('Number')
    grammar1.add_token('String')
    expr_id = grammar1.add_parselet('expr', result_type=object)
    grammar1.add_parser(expr_id, 'Number')
    grammar1.add_parser(expr_id, 'String')

    grammar2 = Grammar()
    grammar2.add_token('Number')
    grammar2.add_token('String')
    expr_id = grammar2.add_parselet('expr', result_type=object)
    grammar2.add_parser(expr_id, 'Number')
    grammar2.add_parser(expr_id, 'String')

    result = Grammar.merge(grammar1, grammar2)
    expr_id = result.parselets['expr']
    assert expr_id in result.tables
    assert len(cast(PackratTable, result.tables[expr_id]).parselets) == 4
Пример #8
0
def test_make_sequence():
    grammar = Grammar()
    name_id = grammar.add_token('Name')
    expr_id = grammar.add_parselet('expr')

    comb = make_sequence(name_id, expr_id)
    assert isinstance(comb, SequenceCombinator)
    assert len(comb) == 2
    assert isinstance(comb[0], TokenCombinator)
    assert isinstance(comb[1], ParseletCombinator)
    assert comb.result_type == SyntaxNode
Пример #9
0
def test_add_packrat_parser():
    grammar = Grammar()
    stmt_id = grammar.add_parselet('stmt',
                                   kind=ParseletKind.Packrat,
                                   result_type=SyntaxToken)
    star_id = grammar.add_implicit('*')

    assert grammar.add_parser(
        stmt_id,
        make_sequence(grammar.add_implicit('('), stmt_id,
                      grammar.add_implicit(')')))
    assert grammar.add_parser(stmt_id,
                              make_sequence(grammar.add_implicit('(')))
    assert grammar.add_parser(stmt_id, star_id)
    assert grammar.add_parser(stmt_id, stmt_id)
Пример #10
0
def test_flat_sequence():
    grammar = Grammar()
    name_id = grammar.add_token('Name')
    expr_id = grammar.add_parselet('expr')

    combinators = tuple(
        flat_sequence(TokenCombinator(name_id),
                      ParseletCombinator(expr_id),
                      SequenceCombinator((
                          TokenCombinator(name_id),
                          ParseletCombinator(expr_id),
                      )),
                      kind=SequenceCombinator))

    assert len(combinators) == 4
    assert isinstance(combinators[0], TokenCombinator)
    assert isinstance(combinators[1], ParseletCombinator)
    assert isinstance(combinators[2], TokenCombinator)
    assert isinstance(combinators[3], ParseletCombinator)
Пример #11
0
def test_flat_combinator():
    grammar = Grammar()
    name_id = grammar.add_token('Name')
    expr_id = grammar.add_parselet('expr')

    # convert token id to token combinator
    comb = flat_combinator(name_id)
    assert isinstance(comb, TokenCombinator)
    assert comb.token_id is name_id

    # convert parselet id to parselet combinator
    comb = flat_combinator(expr_id)
    assert isinstance(comb, ParseletCombinator)
    assert comb.parser_id is expr_id
    assert comb.priority is None

    # don't convert combinator
    comb = TokenCombinator(name_id)
    result = flat_combinator(comb)
    assert comb is result
Пример #12
0
def test_add_pratt_parser():
    grammar = Grammar()
    expr_id = grammar.add_parselet('expr',
                                   kind=ParseletKind.Pratt,
                                   result_type=SyntaxToken)
    integer_id = grammar.add_token('Integer')
    string_id = grammar.add_token('String')
    plus_id = grammar.add_implicit('+')
    star_id = grammar.add_implicit('*')

    table = cast(PrattTable, grammar.tables[expr_id])

    assert table.prefix_tokens == set()
    assert grammar.add_parser(expr_id, integer_id)
    assert integer_id in table.prefix_tokens, "Cleanup of pratt table prefix tokens is not worked"
    assert grammar.add_parser(expr_id, make_named('value', string_id))
    assert string_id in table.prefix_tokens, "Cleanup of pratt table prefix tokens is not worked"
    assert grammar.add_parser(expr_id, make_sequence(expr_id, plus_id,
                                                     expr_id))
    assert grammar.add_parser(
        expr_id,
        make_sequence(make_named('lhs', expr_id), make_named('op', star_id),
                      expr_id))
Пример #13
0
def grammar() -> Grammar:
    grammar = Grammar()

    whitespace_id = grammar.add_pattern(grammar.add_token('Whitespace'),
                                        r'\s+')
    grammar.add_trivia(whitespace_id)

    grammar.add_pattern(grammar.add_token('Name'), r'[a-zA-Z_][a-zA-Z0-9]*')
    grammar.add_pattern(grammar.add_token('Number'), r'[0-9]+')

    make_implicit = grammar.add_implicit

    expr_id = grammar.add_parselet('expr',
                                   kind=ParseletKind.Pratt,
                                   result_type=object)

    # expr := value:Number
    grammar.add_parser(expr_id, "value:Number",
                       make_call(lambda value: value.value, object))

    # expr := lhs:expr op:'+' rhs:expr
    grammar.add_parser(expr_id,
                       'lhs:expr "**" rhs:expr <899>',
                       make_call(lambda lhs, rhs: (lhs, '**', rhs), object),
                       priority=900)

    # expr := lhs:expr op:'+' rhs:expr
    grammar.add_parser(expr_id,
                       'lhs:expr "+" rhs:expr <600>',
                       make_call(lambda lhs, rhs: (lhs, '+', rhs), object),
                       priority=600)

    # expr := lhs:expr op:'-' rhs:expr
    grammar.add_parser(expr_id,
                       'lhs:expr "-" rhs:expr <600>',
                       make_call(lambda lhs, rhs: (lhs, '-', rhs), object),
                       priority=600)

    # expr := lhs:expr op:'*' rhs:expr
    grammar.add_parser(expr_id,
                       'lhs:expr "*" rhs:expr <700>',
                       make_call(lambda lhs, rhs: (lhs, '*', rhs), object),
                       priority=700)

    # expr := lhs:expr op:'/' rhs:expr
    grammar.add_parser(expr_id,
                       'lhs:expr "/" rhs:expr <700>',
                       make_call(lambda lhs, rhs: (lhs, '/', rhs), object),
                       priority=700)

    # expr := op:'-' value:expr
    grammar.add_parser(expr_id, '"-" value:expr <800>',
                       make_call(lambda value: ('-', value), object))

    # expr := op:'-' value:expr
    grammar.add_parser(expr_id, '"+" value:expr <800>',
                       make_call(lambda value: ('+', value), object))

    # expr := '(' value:expr ')'
    grammar.add_parser(expr_id, '"(" value:expr ")"',
                       make_return_variable('value'))

    return grammar
Пример #14
0
def create_combinator_grammar() -> Grammar:
    """
    Create grammar for parse combinator definition

    P.S. This grammar is used for bootstrap process of initial grammar, e.g. definition of combinators in grammar
    """
    grammar = Grammar()
    grammar.extend(create_core_grammar())

    # tokens
    name_id = grammar.tokens['Name']
    string_id = grammar.tokens['String']
    number_id = grammar.tokens['Integer']
    colon_id = grammar.add_implicit(':')
    parent_open_id = grammar.tokens['(']
    parent_close_id = grammar.tokens[')']
    square_open_id = grammar.tokens['[']
    square_close_id = grammar.tokens[']']
    curly_open_id = grammar.tokens['{']
    curly_close_id = grammar.tokens['}']
    less_id = grammar.tokens['<']
    great_id = grammar.tokens['>']

    # parse combinator definition
    comb_id = grammar.add_parselet('combinator', result_type=CombinatorNode)
    seq_id = grammar.add_parselet('combinator_sequence', result_type=SequenceNode)

    # combinator := name: Name ":" combinator=combinator            ; named variable
    grammar.add_parser(
        comb_id,
        make_sequence(make_named('name', name_id), colon_id, make_named('combinator', comb_id)),
        make_ctor(NamedNode)
    )

    # combinator := name: Name  [ '<' priority: Number '>' ]        ; reference to parselet or token
    grammar.add_parser(
        comb_id,
        make_sequence(make_named('name', name_id), make_optional(less_id, make_named('priority', number_id), great_id)),
        make_ctor(ReferenceNode)
    )

    # combinator := value: String                                   ; reference to implicit token
    grammar.add_parser(comb_id, make_named('value', string_id), make_ctor(ImplicitNode))

    # combinator := '[' combinator: combinator_sequence ']'         ; optional combinator
    grammar.add_parser(
        comb_id,
        make_sequence(square_open_id, make_named('combinator', seq_id), square_close_id),
        make_ctor(OptionalNode)
    )

    # combinator := '{' combinator: combinator_sequence '}'         ; repeat combinator
    grammar.add_parser(
        comb_id,
        make_sequence(curly_open_id, make_named('combinator', seq_id), curly_close_id),
        make_ctor(RepeatNode)
    )

    # combinator := '(' combinator: combinator_sequence ')'         ; parenthesis combinator
    grammar.add_parser(
        comb_id,
        make_sequence(parent_open_id, make_named('combinator', seq_id), parent_close_id),
        make_return_variable('combinator')
    )

    # combinator_sequence := combinators:combinator combinators:{ combinator }              ; sequence combinator
    grammar.add_parser(
        seq_id,
        make_sequence(make_named('combinators', comb_id), make_named('combinators', make_repeat(comb_id))),
        make_ctor(SequenceNode)
    )

    return grammar
Пример #15
0
def test_add_idempotent_parselet():
    grammar = Grammar()
    p1 = grammar.add_parselet('name')
    p2 = grammar.add_parselet('name')

    assert p1 is p2 and p1 == p2
Пример #16
0
def test_add_parselet_different_kind():
    grammar = Grammar()
    grammar.add_parselet('expr', kind=ParseletKind.Packrat)
    with pytest.raises(GrammarError):
        grammar.add_parselet('expr', kind=ParseletKind.Pratt)