Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: uberj/inv-dsl
base: e5412ee191
...
head fork: uberj/inv-dsl
compare: 2a96fda9e2
  • 3 commits
  • 9 files changed
  • 0 commit comments
  • 1 contributor
View
3  .gitignore
@@ -0,0 +1,3 @@
+*.pyc
+parser.out
+parsetab.py
View
7 Makefile
@@ -0,0 +1,7 @@
+all:
+ python invparse.py
+
+clean:
+ rm -f *.pyc
+ rm -f parser.out
+ rm -f parsetab.py
View
0  README.md
No changes.
View
14 README.mkd
@@ -0,0 +1,14 @@
+Requirements:
+```
+ply
+```
+
+WARNING: shift/reduce conflict for AND in state 21 resolved as shift
+WARNING: shift/reduce conflict for OR in state 21 resolved as shift
+WARNING: shift/reduce conflict for NOT in state 21 resolved as shift
+WARNING: shift/reduce conflict for LPAREN in state 21 resolved as shift
+WARNING: shift/reduce conflict for DIRECTIVE in state 21 resolved as shift
+WARNING: shift/reduce conflict for TEXT in state 21 resolved as shift
+WARNING: shift/reduce conflict for RE in state 21 resolved as shift
+
+These are OK. The parser doesn't know whether to reduce an ``expression OR expression`` and decides to shift's the OR statement onto the stack. This is what we want because a space between two terms implies and AND, and OR is always the last thing you should do.
View
34 invfilter.py
@@ -0,0 +1,34 @@
+class _Filter(object):
+ """The Base class of different filters. Implement these methods
+ """
+ def __str__(self):
+ return self.value
+
+ def __repr__(self):
+ return "<{1}>".format(self.__class__, self)
+
+ def compile_Q(self, ntype):
+ pass
+
+class TextFilter(_Filter):
+ def __init__(self, rvalue):
+ self.value = rvalue
+ self.Q = self.compile_Q(rvalue)
+
+ def compile_Q(self, value):
+ pass
+
+class REFilter(TextFilter):
+ def compile_Q(self, value):
+ pass
+
+class DirectiveFilter(_Filter):
+ def __init__(self, rvalue, directive, dvalue):
+ self.value = rvalue
+ self.directive = directive
+ self.dvalue = dvalue
+ self.Q = self.compile_Q(directive, dvalue)
+
+ def compile_Q(self, directive, dvalue):
+ pass
+
View
68 invlex.py
@@ -0,0 +1,68 @@
+import pdb
+import re
+import ply.lex as lex
+from invfilter import *
+
+class InvLexer(object):
+
+ tokens = (
+ 'TEXT','DIRECTIVE', 'RE',
+ 'AND','NOT','OR',
+ 'LPAREN','RPAREN',
+ )
+
+ # Tokens
+
+ t_LPAREN = r'\('
+ t_RPAREN = r'\)'
+
+ def t_NOT(self, t):
+ r'-'
+ t.value = 'NOT'
+ return t
+
+ def t_AND(self, t):
+ r'AND'
+ return t
+
+ def t_OR(self, t):
+ r'OR'
+ return t
+
+ def t_DIRECTIVE(self, t):
+ r'([a-zA-Z_]+)=:([a-zA-Z0-9_\.]+)'
+ r = r'([a-zA-Z_]+)=:([a-zA-Z0-9_\.]+)'
+ match = re.compile(r).match(t.value)
+ directive = match.groups(1)[0]
+ dvalue = match.groups(1)[1]
+ t.value = DirectiveFilter(t.value, directive, dvalue)
+ return t
+
+ def t_RE(self, t):
+ r'/[a-zA-Z0-9_\.]+'
+ r = r'/([a-zA-Z0-9_\.]+)'
+ match = re.compile(r).match(t.value)
+ rvalue = match.groups(1)[0]
+ t.value = REFilter(rvalue)
+ return t
+
+ def t_TEXT(self, t):
+ r'[a-zA-Z0-9_\.]+'
+ t.value = TextFilter(t.value)
+ return t
+
+
+ # Ignored characters
+ t_ignore = " \t"
+
+ def t_newline(self, t):
+ r'\n+'
+ t.lexer.lineno += t.value.count("\n")
+
+ def t_error(self, t):
+ print("Illegal character '%s'" % t.value[0])
+ t.lexer.skip(1)
+
+ # Build the lexer
+ def build_lexer(self, **kwargs):
+ self.lexer = lex.lex(module=self, **kwargs)
View
194 invparse.py
@@ -0,0 +1,194 @@
+import pdb
+from invlex import InvLexer
+import ply.yacc as yacc
+
+class BOP(object):
+ def __init__(self, value, l_child, r_child):
+ self.value = value
+ self.l_child = l_child
+ self.r_child = r_child
+
+ def __str__(self):
+ return self.value
+
+ def __repr__(self):
+ return "<BOP {0}>".format(self.value)
+
+class UOP(object):
+ def __init__(self, value, child):
+ self.value = value
+ self.child = child
+
+ def __str__(self):
+ return self.value
+
+ def __repr__(self):
+ return "<UOP {0}>".format(self.value)
+
+
+precedence = (
+ ('left', 'AND'),
+ ('left', 'IMPAND'),
+ ('left', 'PAREN'),
+ ('right', 'NOT'),
+ )
+
+def p_statement_expr(p):
+ 'statement : expression'
+ p[0] = p[1]
+
+def p_expression_expr_term(p):
+ 'expression : expression term %prec IMPAND'
+ p[0] = BOP('AND', p[1], p[2])
+
+def p_expression_term(p):
+ 'expression : term'
+ p[0] = p[1]
+
+def p_expression_uminus(p):
+ 'expression : NOT expression'
+ p[0] = UOP('NOT', p[2])
+
+
+def p_expression_binop(p):
+ '''expression : expression OR expression
+ | expression NOT expression
+ | expression AND expression
+ | LPAREN expression RPAREN expression %prec PAREN
+ | expression LPAREN expression RPAREN %prec PAREN'''
+ if p[2] == 'NOT':
+ p3_invert = UOP('NOT', p[3])
+ p[0] = BOP('AND', p[1], p3_invert)
+ elif p[2] == 'AND' or p[2] == 'OR':
+ p[0] = BOP(p[2], p[1], p[3])
+ elif p[1] == '(': # Matched 'LPAREN expression RPAREN expression'
+ p[0] = BOP('AND', p[2], p[4])
+ elif p[2] == '(':
+ p[0] = BOP('AND', p[1], p[3])
+
+
+
+def p_expression_group(p):
+ 'expression : LPAREN expression RPAREN %prec PAREN'
+ p[0] = p[2]
+
+def p_term_DIRECTIVE(p):
+ 'term : DIRECTIVE'
+ p[0] = p[1]
+
+def p_term_TEXT(p):
+ 'term : TEXT'
+ p[0] = p[1]
+
+def p_term_RE(p):
+ 'term : RE'
+ p[0] = p[1]
+
+def p_error(p):
+ if not p:
+ print "Syntax error at end of line"
+ else:
+ print "Syntax error at '{0}'".format(p.value)
+
+def build_parser():
+ lexer = InvLexer()
+ lexer.build_lexer()
+ tokens = lexer.tokens
+ p = yacc.yacc()
+ def parse(s):
+ return yacc.parse(s, lexer=lexer.lexer)
+ return parse
+
+if __name__ == "__main__":
+ lexer = InvLexer()
+ lexer.build_lexer()
+ tokens = lexer.tokens
+ p = yacc.yacc()
+
+ """
+ s = "a b"
+ print
+ print '-' * 10
+ s = "a OR -b AND c"
+ print s
+ yacc.parse(s, lexer=lexer.lexer)
+ print
+ print '-' * 10
+ print s
+ s = "(a OR b) AND c"
+ p.parse(s)
+ print
+ print '-' * 10
+
+ s = "-(a OR b) AND c"
+ print s
+ p.parse(s)
+ print
+ print '-' * 10
+
+ s = "type=:a vlan=:b"
+ print s
+ p.parse(s)
+ print
+ print '-' * 10
+
+ s = "/a"
+ print s
+ p.parse(s)
+ print
+ print '-'*10
+
+ s = "(a) (b)"
+ print s
+ p.parse(s)
+ print
+ print '-'*10
+
+ s = "a (b)"
+ print s
+ p.parse(s)
+ print
+ print '-'*10
+
+ s = "(a) b"
+ print s
+ p.parse(s)
+ print
+ print '-'*10
+
+ s = "(((a)) (b))"
+ print s
+ p.parse(s)
+ print
+ print '-'*10
+
+ s = "(a b c)"
+ print s
+ p.parse(s)
+ print
+ print '-'*10
+
+ s = "a b d OR c f g"
+ print s
+ p.parse(s)
+ print
+ print '-'*10
+
+ s = "(a (b c))"
+ print s
+ p.parse(s)
+ print
+ print '-'*10
+
+ s = "a -(c OR b)"
+ print s
+ p.parse(s)
+ print
+ print '-'*10
+
+ s = "a AND"
+ print s
+ p.parse(s)
+ print
+ print '-'*10
+ """
View
139 parser_tests.py
@@ -0,0 +1,139 @@
+import pdb
+import unittest
+from invparse import build_parser
+from utils import make_stack
+
+class TestParser(unittest.TestCase):
+ def compare(self, ss, expected_stack_str):
+ parse = build_parser()
+ root_node = parse(ss)
+ stack = make_stack(root_node)
+ actual = ' '.join(stack)
+ self.assertEqual(actual, expected_stack_str, msg="Actual: {0} "
+ "Excpected: {1} Parsing: {2}".format(actual, expected_stack_str,
+ ss))
+ def test1(self):
+ ss = "(a AND (b OR (c d)))"
+ exp = 'a b c d AND OR AND'
+ self.compare(ss, exp)
+
+ def test2(self):
+ ss = "(a AND b)"
+ exp = 'a b AND'
+ self.compare(ss, exp)
+
+ def test3(self):
+ ss = "(a b)"
+ exp = 'a b AND'
+ self.compare(ss, exp)
+
+ def test4(self):
+ ss = "((a b))"
+ exp = 'a b AND'
+ self.compare(ss, exp)
+
+ def test5(self):
+ ss = "((a b) c)"
+ exp = 'a b AND c AND'
+ self.compare(ss, exp)
+
+ def test6(self):
+ ss = "(a (b c))"
+ exp = 'a b c AND AND'
+ self.compare(ss, exp)
+
+ def test7(self):
+ ss = "((a AND (b OR (c d))))"
+ exp = 'a b c d AND OR AND'
+ self.compare(ss, exp)
+
+ def test8(self):
+ ss = "(a AND b OR c AND d)"
+ exp = 'a b AND c d AND OR'
+ self.compare(ss, exp)
+
+ def test9(self):
+ ss = "(a AND (b OR c) AND d)"
+ exp = 'a b c OR AND d AND'
+ self.compare(ss, exp)
+
+ def test10(self):
+ ss = "((a AND (b OR c) AND d))"
+ exp = 'a b c OR AND d AND'
+ self.compare(ss, exp)
+
+ def test11(self):
+ ss = "(a b c)"
+ exp = 'a b AND c AND'
+ self.compare(ss, exp)
+
+ def test12(self):
+ ss = "(a b OR c)"
+ exp = 'a b AND c OR'
+ self.compare(ss, exp)
+
+ def test24(self):
+ ss = "(a OR b c)"
+ exp = 'a b c AND OR'
+ self.compare(ss, exp)
+
+
+ def test13(self):
+ ss = "(a (b OR c))"
+ exp = 'a b c OR AND'
+ self.compare(ss, exp)
+
+ def test14(self):
+ ss = "((a OR b) c)"
+ exp = 'a b OR c AND'
+ self.compare(ss, exp)
+
+ def test15(self):
+ ss = "(-c)"
+ exp = 'c NOT'
+ self.compare(ss, exp)
+
+ def test16(self):
+ ss = "(-a c)"
+ exp = 'a NOT c AND'
+ self.compare(ss, exp)
+
+ def test17(self):
+ ss = "(-(a c))"
+ exp = 'a c AND NOT'
+ self.compare(ss, exp)
+
+ def test18(self):
+ ss = "a -c"
+ exp = 'a c NOT AND'
+ self.compare(ss, exp)
+
+ def test19(self):
+ ss = "a -(c OR b)"
+ exp = 'a c b OR NOT AND'
+ self.compare(ss, exp)
+
+ def test20(self):
+ ss = "a - c"
+ exp = 'a c NOT AND'
+ self.compare(ss, exp)
+
+ def test21(self):
+ ss = "a -(c OR b)"
+ exp = 'a c b OR NOT AND'
+ self.compare(ss, exp)
+
+ def test22(self):
+ ss = "vlan=:a -(c OR -b)"
+ exp = 'vlan=:a c b NOT OR NOT AND'
+ self.compare(ss, exp)
+
+ def test23(self):
+ ss = "a b d OR c f g"
+ exp = 'a b AND d AND c f AND g AND OR'
+ self.compare(ss, exp)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
View
32 utils.py
@@ -0,0 +1,32 @@
+import pdb
+
+def make_stack(node):
+ def _make_stack(stack, node):
+ if not node:
+ return stack
+ if (hasattr(node, 'l_child') and hasattr(node, 'r_child')):
+ if hasattr(node, 'l_child'):
+ _make_stack(stack, node.l_child)
+ if hasattr(node, 'r_child'):
+ _make_stack(stack, node.r_child)
+ stack.append(node.value)
+ else:
+ if hasattr(node, 'child'):
+ _make_stack(stack, node.child)
+ stack.append(node.value)
+ else:
+ stack.append(node.value)
+ stack = []
+ _make_stack(stack, node)
+ return stack
+
+def print_stack(stack):
+ print ' '.join([token.value for token in stack])
+
+def istype(a, b):
+ if not a:
+ return False
+ if a.type_ == b.title():
+ return True
+ else:
+ return False

No commit comments for this range

Something went wrong with that request. Please try again.