summaryrefslogtreecommitdiff
path: root/src/examples/ebnf.py
blob: 8dfcaeacbadd3dc1936c076b9707fc2affb7e592 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# This module tries to implement ISO 14977 standard with pyparsing.
# pyparsing version 1.1 or greater is required.

# ISO 14977 standardize The Extended Backus-Naur Form(EBNF) syntax.
# You can read a final draft version here:
# http://www.cl.cam.ac.uk/~mgk25/iso-ebnf.html


from pyparsing import *


all_names = '''
integer
meta_identifier
terminal_string
optional_sequence
repeated_sequence
grouped_sequence
syntactic_primary
syntactic_factor
syntactic_term
single_definition
definitions_list
syntax_rule
syntax
'''.split()


integer = Word(nums)
meta_identifier = Word(alphas, alphanums + '_')
terminal_string = Suppress("'") + CharsNotIn("'") + Suppress("'") ^ \
                  Suppress('"') + CharsNotIn('"') + Suppress('"')
definitions_list = Forward()
optional_sequence = Suppress('[') + definitions_list + Suppress(']')
repeated_sequence = Suppress('{') + definitions_list + Suppress('}')
grouped_sequence = Suppress('(') + definitions_list + Suppress(')')
syntactic_primary = optional_sequence ^ repeated_sequence ^ \
                    grouped_sequence ^ meta_identifier ^ terminal_string
syntactic_factor = Optional(integer + Suppress('*')) + syntactic_primary
syntactic_term = syntactic_factor + Optional(Suppress('-') + syntactic_factor)
single_definition = delimitedList(syntactic_term, ',')
definitions_list << delimitedList(single_definition, '|')
syntax_rule = meta_identifier + Suppress('=') + definitions_list + \
              Suppress(';')

ebnfComment = ( "(*" +
                         ZeroOrMore( CharsNotIn("*") | ( "*" + ~Literal(")") ) ) +
                        "*)" ).streamline().setName("ebnfComment")

syntax = OneOrMore(syntax_rule)
syntax.ignore(ebnfComment)


def do_integer(str, loc, toks):
    return int(toks[0])
    
def do_meta_identifier(str, loc, toks):
    if toks[0] in symbol_table:
        return symbol_table[toks[0]]
    else:
        forward_count.value += 1
        symbol_table[toks[0]] = Forward()
        return symbol_table[toks[0]]

def do_terminal_string(str, loc, toks):
    return Literal(toks[0])

def do_optional_sequence(str, loc, toks):
    return Optional(toks[0])

def do_repeated_sequence(str, loc, toks):
    return ZeroOrMore(toks[0])

def do_grouped_sequence(str, loc, toks):
    return Group(toks[0])

def do_syntactic_primary(str, loc, toks):
    return toks[0]

def do_syntactic_factor(str, loc, toks):
    if len(toks) == 2:
        # integer * syntactic_primary
        return And([toks[1]] * toks[0])
    else:
        # syntactic_primary
        return [ toks[0] ]

def do_syntactic_term(str, loc, toks):
    if len(toks) == 2:
        # syntactic_factor - syntactic_factor
        return NotAny(toks[1]) + toks[0]
    else:
        # syntactic_factor
        return [ toks[0] ]

def do_single_definition(str, loc, toks):
    toks = toks.asList()
    if len(toks) > 1:
        # syntactic_term , syntactic_term , ...
        return And(toks)
    else:
        # syntactic_term
        return [ toks[0] ]

def do_definitions_list(str, loc, toks):
    toks = toks.asList()
    if len(toks) > 1:
        # single_definition | single_definition | ...
        return Or(toks)
    else:
        # single_definition
        return [ toks[0] ]

def do_syntax_rule(str, loc, toks):
    # meta_identifier = definitions_list ;
    assert toks[0].expr is None, "Duplicate definition"
    forward_count.value -= 1
    toks[0] << toks[1]
    return [ toks[0] ]

def do_syntax(str, loc, toks):
    # syntax_rule syntax_rule ...
    return symbol_table



symbol_table = {}
class forward_count:
    pass
forward_count.value = 0
for name in all_names:
    expr = vars()[name]
    action = vars()['do_' + name]
    expr.setName(name)
    expr.setParseAction(action)
    #~ expr.setDebug()


def parse(ebnf, given_table={}):
    symbol_table.clear()
    symbol_table.update(given_table)
    forward_count.value = 0
    table = syntax.parseString(ebnf)[0]
    assert forward_count.value == 0, "Missing definition"
    for name in table:
        expr = table[name]
        expr.setName(name)
        #~ expr.setDebug()
    return table