sml.py
author Eugen Sawin <sawine@me73.com>
Tue, 31 May 2011 00:58:51 +0200
changeset 26 88451ad84297
parent 25 3418ff66b9ed
permissions -rw-r--r--
Added satisfiabilty test via minisat.
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
sawine@26
     7
TMP_DIMACS = "dimacs~"
sawine@26
     8
TMP_SOLUTION = "solution~"
sawine@26
     9
sawine@12
    10
def main():
sawine@15
    11
    # edit your formulae here
sawine@23
    12
    p = Variable("p")
sawine@23
    13
    q = Variable("q")
sawine@23
    14
    r = Variable("r") 
sawine@23
    15
    s = Variable("s")
sawine@23
    16
    formula1 = Imp(Eq(p, q), r)
sawine@23
    17
    formula2 = Not(Or(p, q))
sawine@23
    18
    formula3 = Not(Not(p))
sawine@23
    19
    formula4 = Imp(Box(Imp(Diamond(p), p)), Box(Imp(p, Box(p))))
sawine@23
    20
    formula5 = Imp(And(Or(p, q), r), Not(s))
sawine@25
    21
    formula6 = And(Or(p, Or(q, Not(s))), And(Or(r, Not(s)), Or(Not(p), s)))
sawine@25
    22
    formula = formula6 # choose your formula here
sawine@12
    23
sawine@25
    24
    args = parse_arguments()   
sawine@12
    25
    if (args.formula):
sawine@12
    26
        scanner = Scanner(TOKENS)
sawine@12
    27
        parser = Parser(SYNTAX, scanner)
sawine@12
    28
        (accepted, out, tree_nodes) = parser.parse(args.formula)
sawine@12
    29
        if not accepted:
sawine@12
    30
            print EXCUSE 
sawine@12
    31
            return
sawine@12
    32
        tree = SyntaxTree(out, tree_nodes) 
sawine@12
    33
        formula = tree.root
sawine@12
    34
   
sawine@22
    35
    original = formula
sawine@22
    36
    print "formula: \t\t\t%s [%i]" % (formula, formula.depth())
sawine@20
    37
    formula = expand_imp_eq(formula)
sawine@22
    38
    print "expanded -> and <->: \t\t%s [%i]" % (formula, formula.depth()) 
sawine@20
    39
    formula = de_morgan(formula)
sawine@22
    40
    print "reduced ~: \t\t\t%s [%i]" % (formula, formula.depth()) 
sawine@20
    41
    formula = reduce_iter(formula)
sawine@22
    42
    print "reduced iterated modalities: \t%s [%i]" % (formula, formula.depth()) 
sawine@20
    43
    formula = reduce_iter(dist_mod((formula)))
sawine@22
    44
    print "distributed modalities+: \t%s [%i]" % (formula, formula.depth()) 
sawine@21
    45
    formula = mcnf(formula)
sawine@22
    46
    print "alpha mcnf: \t\t\t%s [%i]" % (formula, formula.depth())
sawine@26
    47
    #cnf_formula = cnf(original)
sawine@26
    48
    #print "alpha cnf: \t\t\t%s [%i]" % (cnf_formula, cnf_formula.depth())
sawine@26
    49
    
sawine@26
    50
    if not args.dimacs:
sawine@26
    51
        dimacs_file = TMP_DIMACS
sawine@26
    52
    else:
sawine@26
    53
        dimacs_file = args.dimacs
sawine@26
    54
    solution = sat_test(original, dimacs_file)
sawine@26
    55
    if solution:
sawine@26
    56
        print "satisfiable: \t\t\t%s " % (", ".join([str(s) for s in solution]))
sawine@26
    57
    else:
sawine@26
    58
        print "not satisfiable"
sawine@26
    59
    
sawine@26
    60
from subprocess import call, PIPE
sawine@19
    61
sawine@26
    62
def sat_test(formula, dimacs_file):
sawine@26
    63
    out, variables = dimacs(formula)
sawine@26
    64
    with open(dimacs_file, "w") as f:
sawine@26
    65
        f.write(out) 
sawine@26
    66
    call(["minisat2", "-verb=0", dimacs_file, TMP_SOLUTION], stdout=PIPE)
sawine@26
    67
    with open(TMP_SOLUTION) as f:
sawine@26
    68
        solved, assignment = f.readlines()  
sawine@26
    69
    solved = solved[0] == "S"      
sawine@26
    70
    if solved:
sawine@26
    71
        positive = set() 
sawine@26
    72
        for a in assignment.split():           
sawine@26
    73
            neg = a[0] == "-"
sawine@26
    74
            if not neg:
sawine@26
    75
                positive.add(int(a))       
sawine@26
    76
        solution = []
sawine@26
    77
        for v, n in variables.iteritems():           
sawine@26
    78
            if n in positive:              
sawine@26
    79
                solution.append(Variable(v))
sawine@26
    80
            else:
sawine@26
    81
                solution.append(Not(Variable(v)))
sawine@26
    82
        return solution
sawine@25
    83
sawine@25
    84
def dimacs(formula):
sawine@25
    85
    def clausify(f):        
sawine@25
    86
        if f.id == AND:    
sawine@25
    87
            c1 = clausify(f.values[0])   
sawine@25
    88
            c2 = clausify(f.values[1])   
sawine@25
    89
            if c1:
sawine@25
    90
                clauses.append(clausify(f.values[0]))  
sawine@25
    91
            if c2:
sawine@25
    92
                clauses.append(clausify(f.values[1]))              
sawine@25
    93
        if f.id == OR:
sawine@25
    94
            clause = []         
sawine@25
    95
            clause.extend(clausify(f.values[0]))
sawine@25
    96
            clause.extend(clausify(f.values[1]))            
sawine@25
    97
            return clause
sawine@25
    98
        if f.id == NOT:
sawine@25
    99
            return ["-" + clausify(f.values[0])]
sawine@25
   100
        if f.id == VARIABLE:
sawine@25
   101
            if f.values[0] in variables:
sawine@25
   102
                n = variables[f.values[0]]
sawine@25
   103
            else:
sawine@25
   104
                n = len(variables) + 1
sawine@25
   105
                variables[f.values[0]] = n
sawine@25
   106
            return str(n)
sawine@25
   107
      
sawine@25
   108
    variables = {}
sawine@25
   109
    clauses = []
sawine@25
   110
    clausify(formula)
sawine@25
   111
    out = "c DIMACS CNF input file generated by sml\n"
sawine@25
   112
    out += "c Author: Eugen Sawin <sawine@informatik.uni-freiburg.de>\n"
sawine@25
   113
    out += "p cnf %i %i\n" % (len(variables), len(clauses))    
sawine@25
   114
    out += " 0\n".join([" ".join(c) for c in clauses]) + " 0\n"
sawine@26
   115
    return out, variables
sawine@25
   116
sawine@19
   117
def values(fun, f):  
sawine@19
   118
    return [fun(v) for v in f.values] 
sawine@12
   119
sawine@14
   120
def expand_imp_eq(formula):  
sawine@23
   121
    expmap = {IMP: lambda (a, b): Or(Not(a), b),
sawine@23
   122
              EQ: lambda (a, b): And(Or(Not(a), b), Or(a, Not(b)))}   
sawine@20
   123
    if not isinstance(formula, Operator):   
sawine@12
   124
        return formula
sawine@20
   125
    if formula.id in expmap:
sawine@20
   126
        return expmap[formula.id](values(expand_imp_eq, formula))   
sawine@23
   127
    return formula.__class__(*values(expand_imp_eq, formula))
sawine@14
   128
sawine@20
   129
def de_morgan(formula):    
sawine@23
   130
    negmap = {AND: lambda (a, b): Or(negate(a), negate(b)),
sawine@23
   131
              OR: lambda (a, b): And(negate(a), negate(b)),
sawine@23
   132
              BOX: lambda (a,): Diamond(negate(a)),
sawine@23
   133
              DIAMOND: lambda (a,): Box(negate(a)),
sawine@23
   134
              VARIABLE: lambda (a,): Not(Variable(a)),
sawine@19
   135
              NOT: lambda (a,): a}    
sawine@14
   136
    def negate(f):
sawine@20
   137
        return negmap[f.id](f.values)
sawine@14
   138
    if not isinstance(formula, Operator):
sawine@18
   139
        return formula  
sawine@14
   140
    if formula.id == NOT:
sawine@14
   141
        return negate(formula.values[0])
sawine@23
   142
    return formula.__class__(*values(de_morgan, formula))
sawine@18
   143
sawine@18
   144
def reduce_iter(formula):  
sawine@18
   145
    redset = set(((BOX, BOX), (DIAMOND, DIAMOND), (DIAMOND, BOX), (BOX, DIAMOND)))
sawine@18
   146
    if not isinstance(formula, Operator) or not isinstance(formula.values[0], Operator):
sawine@18
   147
        return formula
sawine@18
   148
    ids = (formula.id, formula.values[0].id)
sawine@18
   149
    if ids in redset:
sawine@18
   150
        return reduce_iter(formula.values[0])
sawine@23
   151
    return formula.__class__(*values(reduce_iter, formula))
sawine@19
   152
sawine@20
   153
def dist_mod(formula):    
sawine@23
   154
    distmap = {(BOX, AND): lambda (a, b): And(Box(a), Box(b)),
sawine@23
   155
               (DIAMOND, OR): lambda (a, b): Or(Diamond(a), Diamond(b)),
sawine@19
   156
               (BOX, OR): lambda (a, b): (isinstance(a, Box) or isinstance(b, Box))               
sawine@23
   157
               and Or(Box(a), Box(b)) or Box(Or(a, b)),
sawine@19
   158
               (DIAMOND, AND): lambda (a, b): (isinstance(a, Diamond) or isinstance(b, Diamond))
sawine@23
   159
               and And(Diamond(a), Diamond(b)) or Diamond(And(a, b))}   
sawine@19
   160
    if not isinstance(formula, Operator) or not isinstance(formula.values[0], Operator):
sawine@19
   161
        return formula
sawine@19
   162
    ids = (formula.id, formula.values[0].id)
sawine@19
   163
    if ids in distmap:
sawine@24
   164
        return distmap[ids](values(dist_mod, formula.values[0]))
sawine@23
   165
    return formula.__class__(*values(dist_mod, formula))    
sawine@21
   166
sawine@21
   167
def mcnf(formula):
sawine@21
   168
    if formula.id == OR:
sawine@21
   169
        conj_id = int(formula.values[1].id == AND) 
sawine@23
   170
        return And(Or(formula.values[conj_id].values[0], formula.values[1 - conj_id]), 
sawine@23
   171
                    Or(formula.values[conj_id].values[1], formula.values[1 - conj_id]));
sawine@22
   172
    return formula
sawine@22
   173
sawine@22
   174
def cnf(formula):      
sawine@22
   175
    def part(f):
sawine@22
   176
        for v in f.values:
sawine@22
   177
            if v.id != VARIABLE:
sawine@22
   178
                sub_formulae.append(v)
sawine@25
   179
                part(v) 
sawine@25
   180
sawine@25
   181
    def conjunct(f):
sawine@25
   182
        if len(bicon):
sawine@25
   183
            return And(f, conjunct(bicon.pop()))
sawine@25
   184
        return f
sawine@25
   185
sawine@22
   186
    sub_formulae = [formula]
sawine@22
   187
    part(formula)
sawine@22
   188
    sub_formulae.reverse() 
sawine@22
   189
    known = {}
sawine@22
   190
    bicon = []
sawine@22
   191
    for s in sub_formulae:       
sawine@22
   192
        for i, v in enumerate(s.values):
sawine@22
   193
            if v in known:
sawine@22
   194
                s.values[i] = known[v]
sawine@23
   195
        bicon.append(Eq(Variable("x" + str(len(bicon))), s))
sawine@25
   196
        known[s] = bicon[-1].values[0]     
sawine@22
   197
    return mcnf(dist_mod(reduce_iter(de_morgan(expand_imp_eq(conjunct(known[formula]))))))
sawine@22
   198
sawine@22
   199
""" The Parser - Do NOT look below this line! """ 
sawine@23
   200
import re
sawine@22
   201
sawine@12
   202
NOT = "~"
sawine@12
   203
AND = "&"
sawine@12
   204
OR = "|"
sawine@12
   205
IMP = "->"
sawine@12
   206
EQ = "<->"
sawine@12
   207
BOX = "[]"
sawine@12
   208
DIAMOND = "<>"
sawine@14
   209
VARIABLE = "VAR"
sawine@12
   210
LB = "("
sawine@12
   211
RB = ")"
sawine@12
   212
sawine@12
   213
class Operator(object):
sawine@23
   214
    def __init__(self, id, arity, *values):
sawine@12
   215
        self.id = id         
sawine@12
   216
        self.arity = arity
sawine@23
   217
        self.values = list(values)
sawine@20
   218
    def depth(self):       
sawine@20
   219
        if self.arity == 0:
sawine@20
   220
            return 0
sawine@20
   221
        if self.id in (BOX, DIAMOND):
sawine@20
   222
            return 1 + self.values[0].depth()
sawine@20
   223
        if self.id == NOT:           
sawine@23
   224
            return self.values[0].depth()       
sawine@20
   225
        return max(self.values[0].depth(), self.values[1].depth())
sawine@22
   226
    def __eq__(self, other):
sawine@22
   227
        return self.id == other.id and self.values == other.values # commutative types not considered yet
sawine@12
   228
    def __str__(self):
sawine@12
   229
        out = self.id
sawine@12
   230
        if self.arity == 0:
sawine@20
   231
            out = str(self.values[0])           
sawine@12
   232
        if self.arity == 1:
sawine@12
   233
            out = self.id + str(self.values[0])
sawine@12
   234
        elif self.arity  == 2:          
sawine@12
   235
            out = "(" + str(self.values[0]) + " " + self.id + " " + str(self.values[1]) + ")"
sawine@12
   236
        return out
sawine@12
   237
sawine@12
   238
class Not(Operator):
sawine@12
   239
    arity = 1
sawine@23
   240
    def __init__(self, *values):
sawine@23
   241
        Operator.__init__(self, NOT, Not.arity, *values)       
sawine@12
   242
    def __call__(self):
sawine@12
   243
        pass
sawine@12
   244
sawine@12
   245
class And(Operator):
sawine@12
   246
    arity = 2
sawine@23
   247
    def __init__(self, *values):
sawine@23
   248
        Operator.__init__(self, AND, And.arity, *values)
sawine@12
   249
       
sawine@12
   250
class Or(Operator):
sawine@12
   251
    arity = 2
sawine@23
   252
    def __init__(self, *values):
sawine@23
   253
        Operator.__init__(self, OR, Or.arity, *values)       
sawine@12
   254
sawine@12
   255
class Imp(Operator):
sawine@12
   256
    arity = 2
sawine@23
   257
    def __init__(self, *values):
sawine@23
   258
        Operator.__init__(self, IMP, Imp.arity, *values)
sawine@12
   259
    
sawine@12
   260
class Eq(Operator):
sawine@12
   261
    arity = 2
sawine@23
   262
    def __init__(self, *values):
sawine@23
   263
        Operator.__init__(self, EQ, Eq.arity, *values)
sawine@12
   264
    
sawine@12
   265
class Box(Operator):
sawine@12
   266
    arity = 1
sawine@23
   267
    def __init__(self, *values):
sawine@23
   268
        Operator.__init__(self, BOX, Box.arity, *values)
sawine@12
   269
sawine@12
   270
class Diamond(Operator):
sawine@12
   271
    arity = 1
sawine@23
   272
    def __init__(self, *values):
sawine@23
   273
        Operator.__init__(self, DIAMOND, Diamond.arity, *values)
sawine@12
   274
    
sawine@12
   275
class Variable(Operator):
sawine@12
   276
    arity = 0
sawine@23
   277
    def __init__(self, *values):
sawine@23
   278
        Operator.__init__(self, VARIABLE, Variable.arity, *values)   
sawine@12
   279
sawine@12
   280
class Lb(Operator):
sawine@12
   281
    arity = -1
sawine@23
   282
    def __init__(self, *values):
sawine@23
   283
        Operator.__init__(self, LB, Lb.arity, *values)
sawine@12
   284
sawine@12
   285
class Rb(Operator):
sawine@12
   286
    arity = -1
sawine@23
   287
    def __init__(self, *values):
sawine@23
   288
        Operator.__init__(self, RB, Rb.arity, *values)
sawine@12
   289
sawine@12
   290
class Formula(object):
sawine@12
   291
    def __init__(self):
sawine@12
   292
        pass 
sawine@12
   293
sawine@12
   294
TOKENS = {re.compile("\("): Lb,
sawine@12
   295
          re.compile("\)"): Rb,
sawine@12
   296
          re.compile("~"): Not,
sawine@12
   297
          re.compile("&"): And,
sawine@12
   298
          re.compile("\|"): Or,
sawine@12
   299
          re.compile("->"): Imp,
sawine@12
   300
          re.compile("<->"): Eq,
sawine@12
   301
          re.compile("\[\]"): Box,
sawine@12
   302
          re.compile("<>"): Diamond,
sawine@12
   303
          re.compile("[a-z]+"): Variable}
sawine@12
   304
         
sawine@12
   305
class Scanner(object):
sawine@12
   306
    def __init__(self, tokens, source=None):
sawine@12
   307
        self.tokens = tokens
sawine@12
   308
        self.reset(source)
sawine@12
   309
    def next(self):         
sawine@12
   310
        while self.i < len(self.source):
sawine@12
   311
            re.purge()
sawine@12
   312
            for p, token in self.tokens.iteritems():
sawine@12
   313
                m = p.match(self.source[self.i:])               
sawine@12
   314
                if m:                  
sawine@12
   315
                    self.i += m.end(0)
sawine@12
   316
                    value = m.group(0)  
sawine@12
   317
                    return (token, value)
sawine@12
   318
            self.i += 1
sawine@12
   319
        return (None, None)
sawine@12
   320
    def reset(self, source):
sawine@12
   321
        self.i = 0
sawine@12
   322
        self.source = source
sawine@12
   323
sawine@12
   324
S = Formula
sawine@12
   325
A = "A"
sawine@12
   326
#0 S: (Variable,),
sawine@12
   327
#1 S: (Not, S),
sawine@12
   328
#2 S: (Lb, A, Rb),
sawine@12
   329
#3 S: (Box, S),
sawine@12
   330
#4 S: (Diamond, S),
sawine@12
   331
#5 A: (S, And, S),
sawine@12
   332
#6 A: (S, Or, S),
sawine@12
   333
#7 A: (S, Imp, S),
sawine@12
   334
#8 A: (S, Eq, S)
sawine@12
   335
SYNTAX = ((Variable,),
sawine@12
   336
          (Not, S),
sawine@12
   337
          (Lb, A, Rb),
sawine@12
   338
          (Box, S),
sawine@12
   339
          (Diamond, S),
sawine@12
   340
          (S, And, S),
sawine@12
   341
          (S, Or, S),
sawine@12
   342
          (S, Imp, S),
sawine@12
   343
          (S, Eq, S))
sawine@12
   344
sawine@12
   345
class Parser(object):
sawine@12
   346
    def __init__(self, syntax, scanner):
sawine@12
   347
        self.syntax = syntax
sawine@12
   348
        self.scanner = scanner
sawine@12
   349
    def parse(self, source):
sawine@12
   350
        table = {(S, Variable): 0,
sawine@12
   351
                 (S, Not): 1,
sawine@12
   352
                 (S, Lb): 2,
sawine@12
   353
                 (S, Box): 3,
sawine@12
   354
                 (S, Diamond): 4,
sawine@12
   355
                 (A, S, And): 5,
sawine@12
   356
                 (A, S, Or): 6,
sawine@12
   357
                 (A, S, Imp): 7,
sawine@12
   358
                 (A, S, Eq): 8,
sawine@12
   359
                 (A, Variable, And): 5,
sawine@12
   360
                 (A, Variable, Or): 6,
sawine@12
   361
                 (A, Variable, Imp): 7,
sawine@12
   362
                 (A, Variable, Eq): 8,
sawine@12
   363
                 (A, Not, Variable, And): 5,
sawine@12
   364
                 (A, Not, Variable, Or): 6,
sawine@12
   365
                 (A, Not, Variable, Imp): 7,
sawine@12
   366
                 (A, Not, Variable, Eq): 8}
sawine@12
   367
        stack = [S]
sawine@12
   368
        tree_nodes = []
sawine@12
   369
        tree = None
sawine@12
   370
        out = []
sawine@12
   371
        self.scanner.reset(source)
sawine@12
   372
        (token, value) = self.scanner.next()
sawine@12
   373
        (lookahead, la_value) = self.scanner.next()
sawine@12
   374
        (lookahead2, la2_value) = self.scanner.next()
sawine@12
   375
        accepted = True 
sawine@12
   376
        while token and len(stack): 
sawine@12
   377
            top = stack[-1]               
sawine@12
   378
            if top == token:
sawine@12
   379
                tree_nodes.append((token, value))
sawine@12
   380
                stack.pop()
sawine@12
   381
                #tree_nodes.append((stack.pop(), value))
sawine@12
   382
                (token, value) = (lookahead, la_value)
sawine@12
   383
                (lookahead, la_value) = (lookahead2, la2_value)
sawine@12
   384
                #(lookahead, _) = self.scanner.next()
sawine@12
   385
                (lookahead2, la2_value) = self.scanner.next()
sawine@12
   386
            else:
sawine@12
   387
                action = None
sawine@12
   388
                if (top, token) in table:
sawine@12
   389
                    action = table[(top, token)]
sawine@12
   390
                elif (top, token, lookahead) in table:
sawine@12
   391
                    action = table[(top, token, lookahead)]  
sawine@12
   392
                elif (top, token, lookahead, lookahead2) in table:
sawine@12
   393
                    action = table[(top, token, lookahead, lookahead2)]    
sawine@12
   394
                if action is None:
sawine@12
   395
                    accepted = False
sawine@12
   396
                    break
sawine@12
   397
                
sawine@12
   398
                out.append(action)
sawine@12
   399
                stack.pop()
sawine@12
   400
                stack.extend(reversed(self.syntax[action]))
sawine@12
   401
        accepted = accepted and not len(stack)
sawine@12
   402
        return (accepted, out, tree_nodes) 
sawine@12
   403
sawine@12
   404
class SyntaxTree(object):
sawine@12
   405
    def __init__(self, seq, tree_nodes):
sawine@12
   406
        seq.reverse()
sawine@12
   407
        tree_nodes.reverse()      
sawine@12
   408
        self.root = self.compile(seq, tree_nodes)[0]       
sawine@12
   409
    def compile(self, seq, tree_nodes):
sawine@12
   410
        stack = []     
sawine@12
   411
        while len(seq):           
sawine@12
   412
            s = seq.pop()
sawine@12
   413
            if s == 0:
sawine@12
   414
                c, v = tree_nodes.pop()
sawine@12
   415
                while c != Variable:
sawine@12
   416
                    c, v = tree_nodes.pop()
sawine@12
   417
                stack.append(Variable(v))
sawine@12
   418
            elif s == 1:
sawine@12
   419
                stack.append(Not(self.compile(seq, tree_nodes)))
sawine@12
   420
            elif s == 2:
sawine@12
   421
                stack.extend(self.compile(seq, tree_nodes))
sawine@12
   422
            elif s == 3:
sawine@12
   423
                stack.append(Box(self.compile(seq, tree_nodes)))
sawine@12
   424
            elif s == 4:
sawine@12
   425
                stack.append(Diamond(self.compile(seq, tree_nodes)))
sawine@12
   426
            elif s == 5:
sawine@12
   427
                stack.append(And(self.compile(seq, tree_nodes)))
sawine@12
   428
            elif s == 6:
sawine@12
   429
                stack.append(Or(self.compile(seq, tree_nodes)))
sawine@12
   430
            elif s == 7:
sawine@12
   431
                stack.append(Imp(self.compile(seq, tree_nodes)))
sawine@12
   432
            elif s == 8:
sawine@12
   433
                stack.append(Eq(self.compile(seq, tree_nodes)))       
sawine@12
   434
        return stack   
sawine@12
   435
sawine@12
   436
from argparse import ArgumentParser
sawine@12
   437
sawine@12
   438
def parse_arguments():
sawine@22
   439
    parser = ArgumentParser(description="Tries to parse simple modal logic syntax and reduces the formula. Edit your formula directly in the code file for best results.")
sawine@22
   440
    parser.add_argument("-f", "--formula", help="provide your own formula here, or better don't") 
sawine@25
   441
    parser.add_argument("-d", "--dimacs", help="output file for the CNF DIMACS output")
sawine@12
   442
    return parser.parse_args()
sawine@12
   443
 
sawine@20
   444
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
   445
sawine@12
   446
if __name__ == "__main__":
sawine@12
   447
    main()