blob: 4f05ec7c8dd5639db48d5a8ba39d2572b5a7f68f [file] [log] [blame]
# Copyright 2018 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Module containing the functionality to parse EBNF specifications."""
import logging
from ebnf_cc import symbol
class Grammar(symbol.BaseSymbol):
"""Parse tree node representing a complete EBNF grammar."""
def __init__(self):
super(Grammar, self).__init__()
logging.debug('%sAdding Grammar...',
(self._get_depth() * self._LOG_TAB))
self._push_depth()
while True:
if not self.add_optional(Rule):
break
self._pop_depth()
logging.debug('%sFinished adding Grammar.',
(self._get_depth() * self._LOG_TAB))
class Rule(symbol.BaseSymbol):
"""Parse tree node representing a top-level EBNF rule."""
def __init__(self):
super(Rule, self).__init__()
logging.debug('%sAdding Rule...', (self._get_depth() * self._LOG_TAB))
self._push_depth()
self.add(Lhs)
self.add_token('=')
self.add(Rhs)
self.add_token(';')
self._pop_depth()
logging.debug('%sFinished adding Rule.',
(self._get_depth() * self._LOG_TAB))
class Lhs(symbol.BaseSymbol):
"""Parse tree node representing the left-hand side of an EBNF rule."""
def __init__(self):
super(Lhs, self).__init__()
logging.debug('%sAdding Lhs...', (self._get_depth() * self._LOG_TAB))
self._push_depth()
self.add(Identifier)
self._pop_depth()
logging.debug('%sFinished adding Lhs.',
(self._get_depth() * self._LOG_TAB))
class Rhs(symbol.BaseSymbol):
"""Parse tree node representing the right-hand side of an EBNF rule."""
def __init__(self):
super(Rhs, self).__init__()
logging.debug('%sAdding Rhs...', (self._get_depth() * self._LOG_TAB))
self._push_depth()
if self.add_optional(Identifier):
pass
elif self.add_optional(Terminal):
pass
elif self.add_token_optional('['):
self.add(Rhs)
self.add_token(']')
elif self.add_token_optional('{'):
self.add(Rhs)
self.add_token('}')
elif self.add_token_optional('('):
self.add(Rhs)
self.add_token(')')
else:
raise symbol.SymbolException(
'Unexpected token "%s"' % self._current_token())
if self.add_token_optional('|'):
self.add(Rhs)
elif self.add_token_optional(','):
self.add(Rhs)
self._pop_depth()
logging.debug('%sFinished adding Rhs.',
(self._get_depth() * self._LOG_TAB))
class Terminal(symbol.BaseSymbol):
"""Parse tree node representing a terminal within an EBNF rule."""
def __init__(self):
super(Terminal, self).__init__()
logging.debug('%sAdding Terminal...',
(self._get_depth() * self._LOG_TAB))
self._push_depth()
if self.add_token_optional("'"):
self.add(EbnfToken)
self.add_token("'")
elif self.add_token_optional('"'):
self.add(EbnfToken)
self.add_token('"')
else:
raise symbol.SymbolException(
'Expected a \' or \", but instead '
'received "%s".' % self._current_token())
self._pop_depth()
logging.debug('%sFinished adding Terminal.',
(self._get_depth() * self._LOG_TAB))
class Identifier(symbol.BaseSymbol):
"""Parse tree node representing a identifier within an EBNF rule."""
def __init__(self):
super(Identifier, self).__init__()
logging.debug('%sAdding Identifier...',
(self._get_depth() * self._LOG_TAB))
self._push_depth()
curr_token = self._current_token()
if not curr_token:
raise symbol.SymbolException('Expected a nonempty token')
if not curr_token[0].isalpha():
raise symbol.SymbolException(
'Expected an identifier, which must '
'begin with a letter. Instead received "%s".' % curr_token)
self.add_token(curr_token)
self._pop_depth()
logging.debug('%sFinished adding Identifier.',
(self._get_depth() * self._LOG_TAB))
class EbnfToken(symbol.BaseSymbol):
"""Parse tree node representing any token."""
def __init__(self):
super(EbnfToken, self).__init__()
logging.debug('%sAdding EBNF Token...',
(self._get_depth() * self._LOG_TAB))
self._push_depth()
self.add_token(self._current_token())
self._pop_depth()
logging.debug('%sFinished adding EBNF Token.',
(self._get_depth() * self._LOG_TAB))