sawine@12: """ sawine@13: Name: sml - Simple Modal Logic Lib. sawine@17: Description: sml provides classes for simple modal logic formulae construction and reduction. It also features a disfunctional parser for the SML syntax. sawine@12: Author: Eugen Sawin sawine@12: """ sawine@12: import re sawine@12: sawine@12: def main(): sawine@15: # edit your formulae here sawine@12: p = Variable(["p"]) sawine@12: q = Variable(["q"]) sawine@22: r = Variable(["r"]) sawine@22: s = Variable(["s"]) sawine@12: formula1 = Imp([Eq([p, q]), r]) sawine@12: formula2 = Not([Or([p, q])]) sawine@12: formula3 = Not([Not([p])]) sawine@20: formula4 = Imp([Box([Imp([Diamond([p]), p])]), Box([Imp([p, Box([p])])])]) sawine@22: formula5 = Imp([And([Or([p, q]), r]), Not([s])]) sawine@22: formula = formula5 # choose your formula here sawine@12: sawine@12: args = parse_arguments() sawine@12: if (args.formula): sawine@12: scanner = Scanner(TOKENS) sawine@12: parser = Parser(SYNTAX, scanner) sawine@12: (accepted, out, tree_nodes) = parser.parse(args.formula) sawine@12: if not accepted: sawine@12: print EXCUSE sawine@12: return sawine@12: tree = SyntaxTree(out, tree_nodes) sawine@12: formula = tree.root sawine@12: sawine@22: original = formula sawine@22: print "formula: \t\t\t%s [%i]" % (formula, formula.depth()) sawine@20: formula = expand_imp_eq(formula) sawine@22: print "expanded -> and <->: \t\t%s [%i]" % (formula, formula.depth()) sawine@20: formula = de_morgan(formula) sawine@22: print "reduced ~: \t\t\t%s [%i]" % (formula, formula.depth()) sawine@20: formula = reduce_iter(formula) sawine@22: print "reduced iterated modalities: \t%s [%i]" % (formula, formula.depth()) sawine@20: formula = reduce_iter(dist_mod((formula))) sawine@22: print "distributed modalities+: \t%s [%i]" % (formula, formula.depth()) sawine@21: formula = mcnf(formula) sawine@22: print "alpha mcnf: \t\t\t%s [%i]" % (formula, formula.depth()) sawine@22: cnf_formula = cnf(original) sawine@22: print "alpha cnf: \t\t\t%s [%i]" % (cnf_formula, cnf_formula.depth()) sawine@19: sawine@19: def values(fun, f): sawine@19: return [fun(v) for v in f.values] sawine@12: sawine@14: def expand_imp_eq(formula): sawine@20: expmap = {IMP: lambda (a, b): Or([Not([a]), b]), sawine@20: EQ: lambda (a, b): And([Or([Not([a]), b]), Or([a, Not([b])])])} sawine@20: if not isinstance(formula, Operator): sawine@12: return formula sawine@20: if formula.id in expmap: sawine@20: return expmap[formula.id](values(expand_imp_eq, formula)) sawine@20: return formula.__class__(values(expand_imp_eq, formula)) sawine@14: sawine@20: def de_morgan(formula): sawine@14: negmap = {AND: lambda (a, b): Or([negate(a), negate(b)]), sawine@14: OR: lambda (a, b): And([negate(a), negate(b)]), sawine@18: BOX: lambda (a,): Diamond([negate(a)]), sawine@18: DIAMOND: lambda (a,): Box([negate(a)]), sawine@20: VARIABLE: lambda (a,): Not([Variable([a])]), sawine@19: NOT: lambda (a,): a} sawine@14: def negate(f): sawine@20: return negmap[f.id](f.values) sawine@14: if not isinstance(formula, Operator): sawine@18: return formula sawine@14: if formula.id == NOT: sawine@14: return negate(formula.values[0]) sawine@19: return formula.__class__(values(de_morgan, formula)) sawine@18: sawine@18: def reduce_iter(formula): sawine@18: redset = set(((BOX, BOX), (DIAMOND, DIAMOND), (DIAMOND, BOX), (BOX, DIAMOND))) sawine@18: if not isinstance(formula, Operator) or not isinstance(formula.values[0], Operator): sawine@18: return formula sawine@18: ids = (formula.id, formula.values[0].id) sawine@18: if ids in redset: sawine@18: return reduce_iter(formula.values[0]) sawine@19: return formula.__class__(values(reduce_iter, formula)) sawine@19: sawine@20: def dist_mod(formula): sawine@19: distmap = {(BOX, AND): lambda (a, b): And([Box([a]), Box([b])]), sawine@19: (DIAMOND, OR): lambda (a, b): Or([Diamond([a]), Diamond([b])]), sawine@19: (BOX, OR): lambda (a, b): (isinstance(a, Box) or isinstance(b, Box)) sawine@19: and Or([Box([a]), Box([b])]) or Box([Or([a, b])]), sawine@19: (DIAMOND, AND): lambda (a, b): (isinstance(a, Diamond) or isinstance(b, Diamond)) sawine@19: and And([Diamond([a]), Diamond([b])]) or Diamond([And([a, b])])} sawine@19: if not isinstance(formula, Operator) or not isinstance(formula.values[0], Operator): sawine@19: return formula sawine@19: ids = (formula.id, formula.values[0].id) sawine@19: if ids in distmap: sawine@20: return distmap[ids](values(dist_mod, formula.values[0])) sawine@21: return formula.__class__(values(dist_mod, formula)) sawine@21: sawine@21: def mcnf(formula): sawine@21: if formula.id == OR: sawine@21: conj_id = int(formula.values[1].id == AND) sawine@21: return And([Or([formula.values[conj_id].values[0], formula.values[1 - conj_id]]), sawine@21: Or([formula.values[conj_id].values[1], formula.values[1 - conj_id]])]); sawine@22: return formula sawine@22: sawine@22: def cnf(formula): sawine@22: def part(f): sawine@22: for v in f.values: sawine@22: if v.id != VARIABLE: sawine@22: sub_formulae.append(v) sawine@22: part(v) sawine@22: sub_formulae = [formula] sawine@22: part(formula) sawine@22: sub_formulae.reverse() sawine@22: known = {} sawine@22: bicon = [] sawine@22: for s in sub_formulae: sawine@22: for i, v in enumerate(s.values): sawine@22: if v in known: sawine@22: s.values[i] = known[v] sawine@22: bicon.append(Eq([Variable(["x" + str(len(bicon))]), s])) sawine@22: known[s] = bicon[-1].values[0] sawine@22: def conjunct(f): sawine@22: if len(bicon): sawine@22: return And([f, conjunct(bicon.pop())]) sawine@22: return f sawine@22: return mcnf(dist_mod(reduce_iter(de_morgan(expand_imp_eq(conjunct(known[formula])))))) sawine@22: sawine@22: """ The Parser - Do NOT look below this line! """ sawine@22: sawine@12: NOT = "~" sawine@12: AND = "&" sawine@12: OR = "|" sawine@12: IMP = "->" sawine@12: EQ = "<->" sawine@12: BOX = "[]" sawine@12: DIAMOND = "<>" sawine@14: VARIABLE = "VAR" sawine@12: LB = "(" sawine@12: RB = ")" sawine@12: sawine@12: class Operator(object): sawine@12: def __init__(self, id, arity, values): sawine@12: self.id = id sawine@12: self.arity = arity sawine@12: self.values = values sawine@20: def depth(self): sawine@20: if self.arity == 0: sawine@20: return 0 sawine@20: if self.id in (BOX, DIAMOND): sawine@20: return 1 + self.values[0].depth() sawine@20: if self.id == NOT: sawine@20: return self.values[0].depth() sawine@20: return max(self.values[0].depth(), self.values[1].depth()) sawine@22: def __eq__(self, other): sawine@22: return self.id == other.id and self.values == other.values # commutative types not considered yet sawine@12: def __str__(self): sawine@12: out = self.id sawine@12: if self.arity == 0: sawine@20: out = str(self.values[0]) sawine@12: if self.arity == 1: sawine@12: out = self.id + str(self.values[0]) sawine@12: elif self.arity == 2: sawine@12: out = "(" + str(self.values[0]) + " " + self.id + " " + str(self.values[1]) + ")" sawine@12: return out sawine@12: sawine@12: class Not(Operator): sawine@12: arity = 1 sawine@12: def __init__(self, values): sawine@12: Operator.__init__(self, NOT, Not.arity, values) sawine@12: def __call__(self): sawine@12: pass sawine@12: sawine@12: class And(Operator): sawine@12: arity = 2 sawine@12: def __init__(self, values): sawine@12: Operator.__init__(self, AND, And.arity, values) sawine@12: sawine@12: class Or(Operator): sawine@12: arity = 2 sawine@12: def __init__(self, values): sawine@12: Operator.__init__(self, OR, Or.arity, values) sawine@12: sawine@12: class Imp(Operator): sawine@12: arity = 2 sawine@12: def __init__(self, values): sawine@12: Operator.__init__(self, IMP, Imp.arity, values) sawine@12: sawine@12: class Eq(Operator): sawine@12: arity = 2 sawine@12: def __init__(self, values): sawine@12: Operator.__init__(self, EQ, Eq.arity, values) sawine@12: sawine@12: class Box(Operator): sawine@12: arity = 1 sawine@12: def __init__(self, values): sawine@12: Operator.__init__(self, BOX, Box.arity, values) sawine@12: sawine@12: class Diamond(Operator): sawine@12: arity = 1 sawine@12: def __init__(self, values): sawine@12: Operator.__init__(self, DIAMOND, Diamond.arity, values) sawine@12: sawine@12: class Variable(Operator): sawine@12: arity = 0 sawine@12: def __init__(self, values): sawine@12: Operator.__init__(self, VARIABLE, Variable.arity, values) sawine@12: sawine@12: class Lb(Operator): sawine@12: arity = -1 sawine@12: def __init__(self, values): sawine@12: Operator.__init__(self, LB, Lb.arity, values) sawine@12: sawine@12: class Rb(Operator): sawine@12: arity = -1 sawine@12: def __init__(self, values): sawine@12: Operator.__init__(self, RB, Rb.arity, values) sawine@12: sawine@12: class Formula(object): sawine@12: def __init__(self): sawine@12: pass sawine@12: sawine@12: TOKENS = {re.compile("\("): Lb, sawine@12: re.compile("\)"): Rb, sawine@12: re.compile("~"): Not, sawine@12: re.compile("&"): And, sawine@12: re.compile("\|"): Or, sawine@12: re.compile("->"): Imp, sawine@12: re.compile("<->"): Eq, sawine@12: re.compile("\[\]"): Box, sawine@12: re.compile("<>"): Diamond, sawine@12: re.compile("[a-z]+"): Variable} sawine@12: sawine@12: class Scanner(object): sawine@12: def __init__(self, tokens, source=None): sawine@12: self.tokens = tokens sawine@12: self.reset(source) sawine@12: def next(self): sawine@12: while self.i < len(self.source): sawine@12: re.purge() sawine@12: for p, token in self.tokens.iteritems(): sawine@12: m = p.match(self.source[self.i:]) sawine@12: if m: sawine@12: self.i += m.end(0) sawine@12: value = m.group(0) sawine@12: return (token, value) sawine@12: self.i += 1 sawine@12: return (None, None) sawine@12: def reset(self, source): sawine@12: self.i = 0 sawine@12: self.source = source sawine@12: sawine@12: S = Formula sawine@12: A = "A" sawine@12: #0 S: (Variable,), sawine@12: #1 S: (Not, S), sawine@12: #2 S: (Lb, A, Rb), sawine@12: #3 S: (Box, S), sawine@12: #4 S: (Diamond, S), sawine@12: #5 A: (S, And, S), sawine@12: #6 A: (S, Or, S), sawine@12: #7 A: (S, Imp, S), sawine@12: #8 A: (S, Eq, S) sawine@12: SYNTAX = ((Variable,), sawine@12: (Not, S), sawine@12: (Lb, A, Rb), sawine@12: (Box, S), sawine@12: (Diamond, S), sawine@12: (S, And, S), sawine@12: (S, Or, S), sawine@12: (S, Imp, S), sawine@12: (S, Eq, S)) sawine@12: sawine@12: class Node(object): sawine@12: def __init__(self, parent, value): sawine@12: self.parent = parent sawine@12: self.value = value sawine@12: self.children = [] sawine@12: def addChild(self, value): sawine@12: self.children.append(self, node) sawine@12: sawine@12: class Parser(object): sawine@12: def __init__(self, syntax, scanner): sawine@12: self.syntax = syntax sawine@12: self.scanner = scanner sawine@12: def parse(self, source): sawine@12: table = {(S, Variable): 0, sawine@12: (S, Not): 1, sawine@12: (S, Lb): 2, sawine@12: (S, Box): 3, sawine@12: (S, Diamond): 4, sawine@12: (A, S, And): 5, sawine@12: (A, S, Or): 6, sawine@12: (A, S, Imp): 7, sawine@12: (A, S, Eq): 8, sawine@12: (A, Variable, And): 5, sawine@12: (A, Variable, Or): 6, sawine@12: (A, Variable, Imp): 7, sawine@12: (A, Variable, Eq): 8, sawine@12: (A, Not, Variable, And): 5, sawine@12: (A, Not, Variable, Or): 6, sawine@12: (A, Not, Variable, Imp): 7, sawine@12: (A, Not, Variable, Eq): 8} sawine@12: stack = [S] sawine@12: tree_nodes = [] sawine@12: tree = None sawine@12: out = [] sawine@12: self.scanner.reset(source) sawine@12: (token, value) = self.scanner.next() sawine@12: (lookahead, la_value) = self.scanner.next() sawine@12: (lookahead2, la2_value) = self.scanner.next() sawine@12: accepted = True sawine@12: while token and len(stack): sawine@12: top = stack[-1] sawine@12: if top == token: sawine@12: tree_nodes.append((token, value)) sawine@12: stack.pop() sawine@12: #tree_nodes.append((stack.pop(), value)) sawine@12: (token, value) = (lookahead, la_value) sawine@12: (lookahead, la_value) = (lookahead2, la2_value) sawine@12: #(lookahead, _) = self.scanner.next() sawine@12: (lookahead2, la2_value) = self.scanner.next() sawine@12: else: sawine@12: action = None sawine@12: if (top, token) in table: sawine@12: action = table[(top, token)] sawine@12: elif (top, token, lookahead) in table: sawine@12: action = table[(top, token, lookahead)] sawine@12: elif (top, token, lookahead, lookahead2) in table: sawine@12: action = table[(top, token, lookahead, lookahead2)] sawine@12: if action is None: sawine@12: accepted = False sawine@12: break sawine@12: sawine@12: out.append(action) sawine@12: stack.pop() sawine@12: stack.extend(reversed(self.syntax[action])) sawine@12: accepted = accepted and not len(stack) sawine@12: return (accepted, out, tree_nodes) sawine@12: sawine@12: class SyntaxTree(object): sawine@12: def __init__(self, seq, tree_nodes): sawine@12: seq.reverse() sawine@12: tree_nodes.reverse() sawine@12: self.root = self.compile(seq, tree_nodes)[0] sawine@12: def compile(self, seq, tree_nodes): sawine@12: stack = [] sawine@12: while len(seq): sawine@12: s = seq.pop() sawine@12: if s == 0: sawine@12: c, v = tree_nodes.pop() sawine@12: while c != Variable: sawine@12: c, v = tree_nodes.pop() sawine@12: stack.append(Variable(v)) sawine@12: elif s == 1: sawine@12: stack.append(Not(self.compile(seq, tree_nodes))) sawine@12: elif s == 2: sawine@12: stack.extend(self.compile(seq, tree_nodes)) sawine@12: elif s == 3: sawine@12: stack.append(Box(self.compile(seq, tree_nodes))) sawine@12: elif s == 4: sawine@12: stack.append(Diamond(self.compile(seq, tree_nodes))) sawine@12: elif s == 5: sawine@12: stack.append(And(self.compile(seq, tree_nodes))) sawine@12: elif s == 6: sawine@12: stack.append(Or(self.compile(seq, tree_nodes))) sawine@12: elif s == 7: sawine@12: stack.append(Imp(self.compile(seq, tree_nodes))) sawine@12: elif s == 8: sawine@12: stack.append(Eq(self.compile(seq, tree_nodes))) sawine@12: return stack sawine@12: sawine@12: from argparse import ArgumentParser sawine@12: sawine@12: def parse_arguments(): sawine@22: 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: parser.add_argument("-f", "--formula", help="provide your own formula here, or better don't") sawine@12: return parser.parse_args() sawine@12: sawine@20: 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: sawine@12: if __name__ == "__main__": sawine@12: main()