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