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