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