Permalink
Browse files

rewrite using parsley

  • Loading branch information...
1 parent c269de9 commit d3f07957ac6913f48767f9b9b4246216b28202c1 @uberj committed Apr 7, 2013
Showing with 332 additions and 0 deletions.
  1. +57 −0 parsley/invdsl.parsley
  2. +65 −0 parsley/invdsl.py
  3. +210 −0 parsley/tests.py
@@ -0,0 +1,57 @@
+# http://people.mozilla.com/~juber/public/docs/_build/html/flows.html
+
+ws = ' '*
+not_ws = :c ?(c not in (' ', '\t')) -> c
+letter = :c ?('a' <= c <= 'z' or 'A' <= c <= 'Z') -> c
+
+# Lexical Operators
+NOT = '!'
+AND = <letter+>:and_ ?(and_.lower() == 'and') -> AND_op
+OR = <letter+>:or_ ?(or_.lower() == 'or') -> OR_op
+
+# Directive
+EQ = '=:'
+d_lhs = letter | '_'
+d_rhs = letterOrDigit | '_' | '.' | ',' | ':' | '-'
+DRCT = <d_lhs+>:d EQ <d_rhs+>:v -> directive(d, v)
+
+# Regular Expression
+RE = '/' <(not_ws)+>:r -> regexpr(r)
+
+# Regular text
+special = '_' | '.' | '-' | ':'
+text = (~OR ~AND ~NOT (letterOrDigit | special ))+
+TEXT = <text+>:t -> text(t)
+
+
+# DSF (Device Specific Filters)
+DSF = DRCT | RE | TEXT
+
+# An atmon
+atom = DSF | parens
+
+value = NOT ws atom:a -> NOT_op(a)
+ | atom
+
+# Parens
+parens = '(' ws expr:e ws ')' -> e
+
+# Operators Precidence
+# 1) i_and
+# 2) 2_and
+# 3) e_or
+
+# x AND y <-- Explicit AND
+e_and = AND:op ws value:v -> (op, v)
+
+# x y <-- Implicit AND
+i_and = (' '+ ~OR ~AND) value:v -> (AND_op, v)
+
+# x OR y <-- Explicit OR
+e_or = OR:op ws expr_2:v -> (op, v)
+
+
+# Compile
+expr = expr_2:left ws e_or*:right -> compile(left, right)
+expr_2 = expr_3:left ws e_and*:right -> compile(left, right)
+expr_3 = value:left i_and*:right -> compile(left, right)
View
@@ -0,0 +1,65 @@
+import parsley
+from ometa.runtime import ParseError
+
+
+class ICompiler(object):
+ ParseError = ParseError
+ g = None # Memozie
+
+ def __init__(self, fname, DEBUG=False):
+ self.fname = fname
+ self.DEBUG = DEBUG
+
+ def __call__(self, *args, **kwargs):
+ grammar_text = open(self.fname).read()
+ g = parsley.makeGrammar(
+ grammar_text, self.bindings
+ )
+ return g(*args, **kwargs)
+
+ def print_grammar(self):
+ print '-------------------'
+ print self.grammar_text
+ print '-------------------'
+
+
+class DebugCompiler(ICompiler):
+ def __init__(self, fname, **kwargs):
+ self.bindings = {
+ 'directive': self.directive,
+ 'regexpr': self.regexpr,
+ 'text': self.text,
+ 'compile': self.arith_compile,
+ 'AND_op': lambda a, b: '({a} AND {b})'.format(a=a, b=b),
+ 'OR_op': lambda a, b: '({a} OR {b})'.format(a=a, b=b),
+ 'NOT_op': lambda a: '(NOT {0})'.format(a)
+ }
+ super(DebugCompiler, self).__init__(fname, **kwargs)
+
+ def directive(self, d, v):
+ print 'matched DRCT ' + str((d, v))
+ return d, v
+
+ def regexpr(self, r):
+ print 'matched RE ' + r
+ return r
+
+ def text(self, t):
+ print 'matched TEXT ' + t
+ return t
+
+ def arith_compile(self, initial, values):
+ print initial, values
+ ret = initial
+ for op, value in values:
+ ret = op(ret, value)
+ return ret
+ def OR(self):
+ return
+
+
+
+if __name__ == '__main__':
+ import sys
+ invdsl = DebugCompiler('invdsl.parsley')
+ print invdsl(sys.argv[1]).expr()
View
@@ -0,0 +1,210 @@
+import unittest
+
+from invdsl import DebugCompiler
+
+grammar_file = 'invdsl.parsley'
+
+class T(unittest.TestCase):
+ def setUp(self):
+ self.test_g = DebugCompiler(grammar_file)
+
+ def parse(self, input_):
+ return getattr(self.test_g(input_), self.rule)()
+
+ def fail(self, input_):
+ self.assertRaises(self.test_g.ParseError, self.parse, input_)
+
+class DRCTTest(T):
+ rule = 'DRCT'
+ def test1(self):
+ self.assertEqual(('foo', 'bar'), self.parse('foo=:bar'))
+
+ def test2(self):
+ lhs = 'foo'
+ rhs = '-df:,832kjda_'
+ self.assertEqual((lhs, rhs), self.parse('{0}=:{1}'.format(lhs, rhs)))
+
+ def test3(self):
+ lhs = 'foo'
+ rhs = '-=df:,832kjda_'
+ self.fail('{0}=:{1}'.format(lhs, rhs))
+
+ def test4(self):
+ self.fail('foo')
+
+class TextTest(T):
+ rule = 'TEXT'
+
+ def test1(self):
+ t = 'foo'
+ self.assertEqual(t, self.parse(t))
+
+ def test11(self):
+ t = '11:22::'
+ self.assertEqual(t, self.parse(t))
+
+ def test2(self):
+ t = '!foo'
+ self.fail(t)
+
+ def test3(self):
+ t = 'foo=:bar'
+ self.fail(t)
+
+ def test4(self):
+ t = '!foo'
+ self.fail(t)
+
+ def test5(self):
+ t = 'foo bar'
+ self.fail(t)
+
+class RETest(T):
+ rule = 'RE'
+
+ def test1(self):
+ t = '/foo'
+ self.assertEqual(t[1:], self.parse(t))
+
+ def test2(self):
+ t = '/f.[d]{}^oo'
+ self.assertEqual(t[1:], self.parse(t))
+
+ def test3(self):
+ t = '/foo /bar'
+ self.fail(t)
+
+class ANDTest(T):
+ rule = 'AND'
+
+ def test1(self):
+ t = 'AND'
+ self.assertTrue(callable(self.parse(t)))
+
+ def test2(self):
+ t = 'aND'
+ self.assertTrue(callable(self.parse(t)))
+
+class ORTest(T):
+ rule = 'OR'
+
+ def test1(self):
+ t = 'OR'
+ self.assertTrue(callable(self.parse(t)))
+
+ def test2(self):
+ t = 'oR'
+ self.assertTrue(callable(self.parse(t)))
+
+class BoolEXPRTest(T):
+ rule = 'expr'
+
+ def test1(self):
+ t = '8 AND 8'
+ out = '(' + t + ')'
+ self.assertEqual(out, self.parse(t))
+
+ def test2(self):
+ t = '8 8'
+ out = '(8 AND 8)'
+ self.assertEqual(out, self.parse(t))
+
+ def test3(self):
+ t = '8 8 8'
+ out = '((8 AND 8) AND 8)'
+ self.assertEqual(out, self.parse(t))
+
+ def test4(self):
+ t = '8 OR 8 OR 8'
+ out = '((8 OR 8) OR 8)'
+ self.assertEqual(out, self.parse(t))
+
+ def test5(self):
+ t = '8 8 OR 8'
+ out = '((8 AND 8) OR 8)'
+ self.assertEqual(out, self.parse(t))
+
+ def test6(self):
+ t = '8 OR 8 8'
+ out = '(8 OR (8 AND 8))'
+ self.assertEqual(out, self.parse(t))
+
+class BareNOTTests(T):
+ rule = 'value'
+
+ def test1(self):
+ t = '!a'
+ out = '(NOT a)'
+ self.assertEqual(out, self.parse(t))
+
+ def test2(self):
+ t = 'a'
+ out = 'a'
+ self.assertEqual(out, self.parse(t))
+
+class EXPRTests(T):
+ rule = 'expr'
+
+ def test1(self):
+ t = '!(a b)'
+ out = '(NOT (a AND b))'
+ self.assertEqual(out, self.parse(t))
+
+ def test2(self):
+ t = 'a'
+ out = 'a'
+ self.assertEqual(out, self.parse(t))
+
+ def test15(self):
+ t = "(!c)"
+ out = '(NOT c)'
+ self.assertEqual(out, self.parse(t))
+
+ def test16(self):
+ t = "(!a c)"
+ out = '((NOT a) AND c)'
+ self.assertEqual(out, self.parse(t))
+
+ def test17(self):
+ t = "(!(a c))"
+ out = '(NOT (a AND c))'
+ self.assertEqual(out, self.parse(t))
+
+ def test18(self):
+ t = "a !c"
+ out = '(a AND (NOT c))'
+ self.assertEqual(out, self.parse(t))
+
+ def test19(self):
+ t = "a !(c OR b)"
+ out = '(a AND (NOT (c OR b)))'
+ self.assertEqual(out, self.parse(t))
+
+ def test20(self):
+ t = "a ! c"
+ out = '(a AND (NOT c))'
+ self.assertEqual(out, self.parse(t))
+
+ def test22(self):
+ t = "type=:a !(c OR !b)"
+ out = "(('type', 'a') AND (NOT (c OR (NOT b))))"
+ self.assertEqual(out, self.parse(t))
+
+ def test23(self):
+ t = "a b d OR c f g"
+ out = '(((a AND b) AND d) OR ((c AND f) AND g))'
+ self.assertEqual(out, self.parse(t))
+
+ def test25(self):
+ t = "foo-bar baz"
+ out = '(foo-bar AND baz)'
+ self.assertEqual(out, self.parse(t))
+
+ def test26(self):
+ t = "type=:foo.bar !type=:baz"
+ out = "(('type', 'foo.bar') AND (NOT ('type', 'baz')))"
+ self.assertEqual(out, self.parse(t))
+
+
+if __name__ == '__main__':
+ unittest.main()

0 comments on commit d3f0795

Please sign in to comment.