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