lep_parser.py

'''\
Logical Expression Recursive Descent Parser.

BNF:
    expr   = term
           = term oper term

    term   = name
           = '(' expr ')'
           = not term

    name   = alpha
           = alpha alnums
    alnums = alnum
           = alnum alnums
    alnum  = alpha
           = number

    oper   = '|'
           = '&'
           = '^'
    not    = 'not'
    alpha  = 'A'..'Z'
           = 'a'..'z'
    number = '0'..'9'

'''
####################################################################################################
from lep_objects import *
####################################################################################################


##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class source (object):
    '''Logical expression source text class.'''
    lib = {}

    def __init__ (selft):
        self.text = t
        self.index = 0

    def reset (self):
        self.index = 0
        return self()

    def incr (self):
        if len(self.text) <= self.index:
            return self()
        self.index += 1
        return self()

    def skip_ws (self):
        ch = self()
        while ch.isspace():
            ch = self.incr()
        return self()

    def word (selfw):
        n = len(w)
        ix = self.index
        w1 = self.text[ix:ix+n].upper()
        w2 = w.upper()
        if w1 == w2:
            self.index += n
            return True
        return False

    def __call__ (self):
        ix = self.index
        ch = self.text[ix:ix+1]
        return ch

    def __len__ (self):
        return len(self.text)

    def __str__ (self):
        return self.text

    def __repr__ (self):
        s = '"%s"[%d]'
        t = (self.textself.index)
        return s % t



##================================================================================================##
def parse_operator (src):
    '''Test: Operator.'''
    ch = src.skip_ws()

    # Operator,... give me in-for-mation,...
    if ch == '|':
        src.incr()
        return oper_or()
    if ch == '&':
        src.incr()
        return oper_and()
    if ch == '^':
        src.incr()
        return oper_xor()

    # If it's OR...
    if src.word('or'):
        print 'oper.OR:'
        return oper_or()
    # If it's AND...
    if src.word('and'):
        print 'oper.AND:'
        return oper_and()
    # If it's XOR...
    if src.word('xor'):
        print 'oper.XOR:'
        return oper_xor()
    # If it's NOR...
    if src.word('nor'):
        print 'oper.NOR:'
        return oper_nor()
    # If it's NAND...
    if src.word('nand'):
        print 'oper.NAND:'
        return oper_nand()

    # Unknown, so an error...
    raise SyntaxError('Operator expected! [@%d]' % src.index)


##================================================================================================##
def parse_term (src):
    '''Test: Term.'''
    ch = src.skip_ws()

    # If it's an open-parenthesis, this is an enclosed term...
    if ch == '(':
        src.incr()
        expr = parse_expression(src)
        ch = src.skip_ws()
        if ch != ')':
            raise SyntaxError('Close parenthesis expected! [@%d]' % src.index)
        src.incr()
        print 'term.expr: %s' % expr
        return expr

    # If it's a NOT...
    if src.word('not'):
        print 'expr.NOT:'
        # Create a NOT operator...
        op = oper_not()

        # Get the term...
        t1 = parse_term(src)
        print 'expr.t1: %s' % t1

        # Return a NOT expression...
        return expression(opt1None)

    # If it's an alpha, this is a name...
    if ch.isalpha():
        name = ch
        ch = src.incr()
        while ch.isalnum():
            name += ch
            ch = src.incr()
        if name in src.lib:
            print 'term.name: %s (exists)' % name
            return src.lib[name]
        # Add new term to library...
        print 'term.name: %s (NEW)' % name
        t = term(name)
        src.lib[name] = t
        return t

    # Unknown, so an error...
    raise SyntaxError('Term expected! [@%d]' % src.index)


##================================================================================================##
def parse_expression (src):
    '''Test: Expression.'''
    ch = src.skip_ws()

    # Get term #1...
    t1 = parse_term(src)
    print 'expr.t1: %s' % t1

    # Get operator (or fail if just a term)...
    try:
        op = parse_operator(src)
        print 'expr.op: %s' % op
    except SyntaxError:
        # Return a term...
        return t1

    # Get term #2...
    t2 = parse_term(src)
    print 'expr.t2: %s' % t2

    # Return an expression...
    return expression(opt1t2)



####################################################################################################
'''eof'''