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