sml.py
author Eugen Sawin <sawine@me73.com>
Wed, 18 May 2011 03:23:35 +0200
changeset 20 1f97011ea795
parent 19 42bc914ee673
child 21 b7480b2e8fd2
permissions -rw-r--r--
Fixed variable bug with de morgan.
     1 """
     2 Name: sml - Simple Modal Logic Lib.
     3 Description: sml provides classes for simple modal logic formulae construction and reduction. It also features a disfunctional parser for the SML syntax.
     4 Author: Eugen Sawin <sawine@informatik.uni-freiburg.de>
     5 """
     6 import re
     7 
     8 def main():
     9     # edit your formulae here
    10     p = Variable(["p"])
    11     q = Variable(["q"])
    12     r = Variable(["r"])
    13     formula1 = Imp([Eq([p, q]), r])
    14     formula2 = Not([Or([p, q])])
    15     formula3 = Not([Not([p])])
    16     formula4 = Imp([Box([Imp([Diamond([p]), p])]), Box([Imp([p, Box([p])])])])
    17     formula = formula4 # choose your formula here
    18 
    19     args = parse_arguments()
    20     if (args.formula):
    21         scanner = Scanner(TOKENS)
    22         parser = Parser(SYNTAX, scanner)
    23         (accepted, out, tree_nodes) = parser.parse(args.formula)
    24         if not accepted:
    25             print EXCUSE 
    26             return
    27         tree = SyntaxTree(out, tree_nodes) 
    28         formula = tree.root
    29    
    30     print "formula: \t\t\t%s (%i)" % (formula, formula.depth())
    31     formula = expand_imp_eq(formula)
    32     print "expanded -> and <->: \t\t%s (%i)" % (formula, formula.depth()) 
    33     formula = de_morgan(formula)
    34     print "reduced ~: \t\t\t%s (%i)" % (formula, formula.depth()) 
    35     formula = reduce_iter(formula)
    36     print "reduced iterated modalities: \t%s (%i)" % (formula, formula.depth()) 
    37     formula = reduce_iter(dist_mod((formula)))
    38     print "distributed modalities+: \t%s (%i)" % (formula, formula.depth()) 
    39     formula = None
    40 
    41 def values(fun, f):  
    42     return [fun(v) for v in f.values] 
    43 
    44 def expand_imp_eq(formula):  
    45     expmap = {IMP: lambda (a, b): Or([Not([a]), b]),
    46               EQ: lambda (a, b): And([Or([Not([a]), b]), Or([a, Not([b])])])}   
    47     if not isinstance(formula, Operator):   
    48         return formula
    49     if formula.id in expmap:
    50         return expmap[formula.id](values(expand_imp_eq, formula))   
    51     return formula.__class__(values(expand_imp_eq, formula))
    52 
    53 def de_morgan(formula):    
    54     negmap = {AND: lambda (a, b): Or([negate(a), negate(b)]),
    55               OR: lambda (a, b): And([negate(a), negate(b)]),
    56               BOX: lambda (a,): Diamond([negate(a)]),
    57               DIAMOND: lambda (a,): Box([negate(a)]),
    58               VARIABLE: lambda (a,): Not([Variable([a])]),
    59               NOT: lambda (a,): a}    
    60     def negate(f):
    61         return negmap[f.id](f.values)
    62     if not isinstance(formula, Operator):
    63         return formula  
    64     if formula.id == NOT:
    65         return negate(formula.values[0])
    66     return formula.__class__(values(de_morgan, formula))
    67 
    68 def reduce_iter(formula):  
    69     redset = set(((BOX, BOX), (DIAMOND, DIAMOND), (DIAMOND, BOX), (BOX, DIAMOND)))
    70     if not isinstance(formula, Operator) or not isinstance(formula.values[0], Operator):
    71         return formula
    72     ids = (formula.id, formula.values[0].id)
    73     if ids in redset:
    74         return reduce_iter(formula.values[0])
    75     return formula.__class__(values(reduce_iter, formula))
    76 
    77 def dist_mod(formula):    
    78     distmap = {(BOX, AND): lambda (a, b): And([Box([a]), Box([b])]),
    79                (DIAMOND, OR): lambda (a, b): Or([Diamond([a]), Diamond([b])]),
    80                (BOX, OR): lambda (a, b): (isinstance(a, Box) or isinstance(b, Box))               
    81                and Or([Box([a]), Box([b])]) or Box([Or([a, b])]),
    82                (DIAMOND, AND): lambda (a, b): (isinstance(a, Diamond) or isinstance(b, Diamond))
    83                and And([Diamond([a]), Diamond([b])]) or Diamond([And([a, b])])}   
    84     if not isinstance(formula, Operator) or not isinstance(formula.values[0], Operator):
    85         return formula
    86     ids = (formula.id, formula.values[0].id)
    87     if ids in distmap:
    88         return distmap[ids](values(dist_mod, formula.values[0]))
    89     return formula.__class__(values(dist_mod, formula))        
    90  
    91 NOT = "~"
    92 AND = "&"
    93 OR = "|"
    94 IMP = "->"
    95 EQ = "<->"
    96 BOX = "[]"
    97 DIAMOND = "<>"
    98 VARIABLE = "VAR"
    99 LB = "("
   100 RB = ")"
   101 
   102 class Operator(object):
   103     def __init__(self, id, arity, values):
   104         self.id = id         
   105         self.arity = arity
   106         self.values = values
   107     def depth(self):       
   108         if self.arity == 0:
   109             return 0
   110         if self.id in (BOX, DIAMOND):
   111             return 1 + self.values[0].depth()
   112         if self.id == NOT:           
   113             return self.values[0].depth()
   114         return max(self.values[0].depth(), self.values[1].depth())
   115     def __str__(self):
   116         out = self.id
   117         if self.arity == 0:
   118             out = str(self.values[0])           
   119         if self.arity == 1:
   120             out = self.id + str(self.values[0])
   121         elif self.arity  == 2:          
   122             out = "(" + str(self.values[0]) + " " + self.id + " " + str(self.values[1]) + ")"
   123         return out
   124 
   125 class Not(Operator):
   126     arity = 1
   127     def __init__(self, values):
   128         Operator.__init__(self, NOT, Not.arity, values)       
   129     def __call__(self):
   130         pass
   131 
   132 class And(Operator):
   133     arity = 2
   134     def __init__(self, values):
   135         Operator.__init__(self, AND, And.arity, values)
   136        
   137 class Or(Operator):
   138     arity = 2
   139     def __init__(self, values):
   140         Operator.__init__(self, OR, Or.arity, values)       
   141 
   142 class Imp(Operator):
   143     arity = 2
   144     def __init__(self, values):
   145         Operator.__init__(self, IMP, Imp.arity, values)
   146     
   147 class Eq(Operator):
   148     arity = 2
   149     def __init__(self, values):
   150         Operator.__init__(self, EQ, Eq.arity, values)
   151     
   152 class Box(Operator):
   153     arity = 1
   154     def __init__(self, values):
   155         Operator.__init__(self, BOX, Box.arity, values)
   156 
   157 class Diamond(Operator):
   158     arity = 1
   159     def __init__(self, values):
   160         Operator.__init__(self, DIAMOND, Diamond.arity, values)
   161     
   162 class Variable(Operator):
   163     arity = 0
   164     def __init__(self, values):
   165         Operator.__init__(self, VARIABLE, Variable.arity, values)   
   166 
   167 class Lb(Operator):
   168     arity = -1
   169     def __init__(self, values):
   170         Operator.__init__(self, LB, Lb.arity, values)
   171 
   172 class Rb(Operator):
   173     arity = -1
   174     def __init__(self, values):
   175         Operator.__init__(self, RB, Rb.arity, values)
   176 
   177 class Formula(object):
   178     def __init__(self):
   179         pass 
   180 
   181 TOKENS = {re.compile("\("): Lb,
   182           re.compile("\)"): Rb,
   183           re.compile("~"): Not,
   184           re.compile("&"): And,
   185           re.compile("\|"): Or,
   186           re.compile("->"): Imp,
   187           re.compile("<->"): Eq,
   188           re.compile("\[\]"): Box,
   189           re.compile("<>"): Diamond,
   190           re.compile("[a-z]+"): Variable}
   191          
   192 class Scanner(object):
   193     def __init__(self, tokens, source=None):
   194         self.tokens = tokens
   195         self.reset(source)
   196     def next(self):         
   197         while self.i < len(self.source):
   198             re.purge()
   199             for p, token in self.tokens.iteritems():
   200                 m = p.match(self.source[self.i:])               
   201                 if m:                  
   202                     self.i += m.end(0)
   203                     value = m.group(0)  
   204                     return (token, value)
   205             self.i += 1
   206         return (None, None)
   207     def reset(self, source):
   208         self.i = 0
   209         self.source = source
   210 
   211 S = Formula
   212 A = "A"
   213 #0 S: (Variable,),
   214 #1 S: (Not, S),
   215 #2 S: (Lb, A, Rb),
   216 #3 S: (Box, S),
   217 #4 S: (Diamond, S),
   218 #5 A: (S, And, S),
   219 #6 A: (S, Or, S),
   220 #7 A: (S, Imp, S),
   221 #8 A: (S, Eq, S)
   222 SYNTAX = ((Variable,),
   223           (Not, S),
   224           (Lb, A, Rb),
   225           (Box, S),
   226           (Diamond, S),
   227           (S, And, S),
   228           (S, Or, S),
   229           (S, Imp, S),
   230           (S, Eq, S))
   231 
   232 class Node(object):
   233     def __init__(self, parent, value):
   234         self.parent = parent
   235         self.value = value
   236         self.children = []
   237     def addChild(self, value):
   238         self.children.append(self, node)
   239 
   240 class Parser(object):
   241     def __init__(self, syntax, scanner):
   242         self.syntax = syntax
   243         self.scanner = scanner
   244     def parse(self, source):
   245         table = {(S, Variable): 0,
   246                  (S, Not): 1,
   247                  (S, Lb): 2,
   248                  (S, Box): 3,
   249                  (S, Diamond): 4,
   250                  (A, S, And): 5,
   251                  (A, S, Or): 6,
   252                  (A, S, Imp): 7,
   253                  (A, S, Eq): 8,
   254                  (A, Variable, And): 5,
   255                  (A, Variable, Or): 6,
   256                  (A, Variable, Imp): 7,
   257                  (A, Variable, Eq): 8,
   258                  (A, Not, Variable, And): 5,
   259                  (A, Not, Variable, Or): 6,
   260                  (A, Not, Variable, Imp): 7,
   261                  (A, Not, Variable, Eq): 8}
   262         stack = [S]
   263         tree_nodes = []
   264         tree = None
   265         out = []
   266         self.scanner.reset(source)
   267         (token, value) = self.scanner.next()
   268         (lookahead, la_value) = self.scanner.next()
   269         (lookahead2, la2_value) = self.scanner.next()
   270         accepted = True 
   271         while token and len(stack): 
   272             top = stack[-1]               
   273             if top == token:
   274                 tree_nodes.append((token, value))
   275                 stack.pop()
   276                 #tree_nodes.append((stack.pop(), value))
   277                 (token, value) = (lookahead, la_value)
   278                 (lookahead, la_value) = (lookahead2, la2_value)
   279                 #(lookahead, _) = self.scanner.next()
   280                 (lookahead2, la2_value) = self.scanner.next()
   281             else:
   282                 action = None
   283                 if (top, token) in table:
   284                     action = table[(top, token)]
   285                 elif (top, token, lookahead) in table:
   286                     action = table[(top, token, lookahead)]  
   287                 elif (top, token, lookahead, lookahead2) in table:
   288                     action = table[(top, token, lookahead, lookahead2)]    
   289                 if action is None:
   290                     accepted = False
   291                     break
   292                 
   293                 out.append(action)
   294                 stack.pop()
   295                 stack.extend(reversed(self.syntax[action]))
   296         accepted = accepted and not len(stack)
   297         return (accepted, out, tree_nodes) 
   298 
   299 class SyntaxTree(object):
   300     def __init__(self, seq, tree_nodes):
   301         seq.reverse()
   302         tree_nodes.reverse()      
   303         self.root = self.compile(seq, tree_nodes)[0]       
   304     def compile(self, seq, tree_nodes):
   305         stack = []     
   306         while len(seq):           
   307             s = seq.pop()
   308             if s == 0:
   309                 c, v = tree_nodes.pop()
   310                 while c != Variable:
   311                     c, v = tree_nodes.pop()
   312                 stack.append(Variable(v))
   313             elif s == 1:
   314                 stack.append(Not(self.compile(seq, tree_nodes)))
   315             elif s == 2:
   316                 stack.extend(self.compile(seq, tree_nodes))
   317             elif s == 3:
   318                 stack.append(Box(self.compile(seq, tree_nodes)))
   319             elif s == 4:
   320                 stack.append(Diamond(self.compile(seq, tree_nodes)))
   321             elif s == 5:
   322                 stack.append(And(self.compile(seq, tree_nodes)))
   323             elif s == 6:
   324                 stack.append(Or(self.compile(seq, tree_nodes)))
   325             elif s == 7:
   326                 stack.append(Imp(self.compile(seq, tree_nodes)))
   327             elif s == 8:
   328                 stack.append(Eq(self.compile(seq, tree_nodes)))       
   329         return stack   
   330 
   331 from argparse import ArgumentParser
   332 
   333 def parse_arguments():
   334     parser = ArgumentParser(description="Parses simple modal logic syntax.")
   335     parser.add_argument("-f", "--formula", help="your formula") 
   336     return parser.parse_args()
   337  
   338 EXCUSE = """The formula was not accepted by me, because I do not like you. It is not personal. I seem to have some issues with formulae with inner nesting and I generally suffer from my hasty implementation. I would like to thank you for your coorporation by providing the formula within the code file itself. Thank you. ~The Parser"""
   339 
   340 if __name__ == "__main__":
   341     main()