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