aboutsummaryrefslogtreecommitdiff
path: root/Logic.icl
diff options
context:
space:
mode:
Diffstat (limited to 'Logic.icl')
-rw-r--r--Logic.icl119
1 files changed, 102 insertions, 17 deletions
diff --git a/Logic.icl b/Logic.icl
index 2c9cfde..0d739cd 100644
--- a/Logic.icl
+++ b/Logic.icl
@@ -103,9 +103,9 @@ where
instance toString TruthTable
where
toString t=:{exprs,options}
- = row_b +++ join row_s [pad_right head len \\ head <- map toString exprs & len <- padlens] +++ row_e +++
+ = row_b +++ join row_s [pad_right ' ' head len \\ head <- map toString exprs & len <- padlens] +++ row_e +++
line_b +++ join line_s [toString (repeatn len '-') \\ len <- padlens] +++ line_e +++
- foldr (+++) "" [row_b +++ join row_s [pad_right (toStringOrEmpty val) len \\ val <- map (eval o substitute_all options`) exprs & len <- padlens] +++ row_e \\ options` <- options]
+ foldr (+++) "" [row_b +++ join row_s [pad_right ' ' (toStringOrEmpty val) len \\ val <- map (eval o substitute_all options`) exprs & len <- padlens] +++ row_e \\ options` <- options]
where
row_b = " " // Row / Line begin, end, separator
row_e = " \n"
@@ -171,28 +171,27 @@ where
comp (Atom a) (Atom b) = a < b
comp (Atom _) _ = True
comp _ (Atom _) = False
- comp (App1 _ e) (App1 _ e`) = e < e`
- comp (App1 _ _) _ = True
- comp _ (App1 _ _) = False
- comp (App2 e1 op e2) (App2 e1` op` e2`)
- | e1 < e1` = True
- | e1` < e1 = False
- | e2 < e2` = True
- | e2` < e2 = False
- | otherwise = op < op`
+ comp (App1 Not e) (App1 Not e`) = e < e`
+ comp e1 e2
+ | isMember e1 (subexprs e2) = True
+ | isMember e2 (subexprs e1) = False
+ | otherwise = strlen (toString e1) < strlen (toString e2)
+// Does a the argument of a unary operator need parentheses?
needs_parentheses :: Expr -> Bool
needs_parentheses (App1 Not (B _)) = False
needs_parentheses (App1 Not (Atom _)) = False
needs_parentheses (App1 Not (App1 Not _)) = False
needs_parentheses _ = True
+// Does the first argument of a binary operator need parentheses?
needs_parentheses_left :: Expr -> Bool
needs_parentheses_left (App2 (B _) _ _) = False
needs_parentheses_left (App2 (Atom _) _ _) = False
needs_parentheses_left (App2 (App1 Not _) _ _) = False
needs_parentheses_left (App2 (App2 _ op1 _) op2 _) = binds_stronger op2 op1
+// Does the second argument of a binary operator need parentheses?
needs_parentheses_right :: Expr -> Bool
needs_parentheses_right (App2 _ _ (B _)) = False
needs_parentheses_right (App2 _ _ (Atom _)) = False
@@ -233,12 +232,6 @@ substitute _ e = e
substitute_all :: [AtomOption] Expr -> Expr
substitute_all opts e = foldr substitute e opts
-contains_atoms :: Expr -> Bool
-contains_atoms (B _) = False
-contains_atoms (Atom _) = True
-contains_atoms (App1 _ e) = contains_atoms e
-contains_atoms (App2 e1 _ e2) = contains_atoms e1 || contains_atoms e2
-
eval :: Expr -> [Bool]
eval (B b) = [b]
eval (App1 op e) = [apply1 op e` \\ e` <- eval e]
@@ -260,3 +253,95 @@ simple_truthtable e = {exprs = [Atom a \\ a <- all_atoms e] ++ [e], options = al
truthtable :: Expr -> TruthTable
truthtable e = {exprs = sorted_subexprs e ++ [e], options = all_atom_options e}
+NOT :== '~'
+AND :== '&'
+OR :== '|'
+IMPL :== '>'
+EQUIV :== '='
+
+parse :: String -> Expr
+parse s = parse_stack (shunting_yard (prep s) [] [])
+where
+ // Make sure every token is ONE character by replacing <-> and ->
+ // Remove whitespace
+ prep :: String -> [Char]
+ prep s
+ # s = repl "<->" EQUIV s
+ # s = repl "->" IMPL s
+ # cs = fromString s
+ # cs = removeAll ' ' cs
+ = cs
+ where
+ removeAll c cs
+ | isMember c cs = removeAll c (removeMember c cs)
+ | otherwise = cs
+
+ // The shunting yard algorithm for propositional logic; see https://en.wikipedia.org/wiki/Shunting-yard_algorithm
+ shunting_yard :: [Char] [Char] [Char] -> [Char]
+ shunting_yard [] output [] = output
+ shunting_yard [] output ['(':_] = abort "Mismatched parentheses"
+ shunting_yard [] output [')':_] = abort "Mismatched parentheses"
+ shunting_yard [] output [op:operators] = shunting_yard [] (output ++ [op]) operators
+ shunting_yard ['(':input] output operators = shunting_yard input output ['(':operators]
+ shunting_yard [')':input] output ['(':operators] = shunting_yard input output operators
+ shunting_yard [')':input] output [op:operators] = shunting_yard [')':input] (output ++ [op]) operators
+ shunting_yard [NOT:input] output operators = shunting_yard input output [NOT:operators]
+ shunting_yard [AND:input] output operators
+ | not (isEmpty operators) && (hd operators == NOT || hd operators == AND) = shunting_yard [AND:input] (output ++ [hd operators]) (tl operators)
+ | otherwise = shunting_yard input output [AND:operators]
+ shunting_yard [OR:input] output operators
+ | not (isEmpty operators) && (hd operators == NOT || hd operators == AND) = shunting_yard [OR:input] (output ++ [hd operators]) (tl operators)
+ | otherwise = shunting_yard input output [OR:operators]
+ shunting_yard [IMPL:input] output operators
+ | not (isEmpty operators) && (hd operators == NOT || hd operators == AND || hd operators == OR) = shunting_yard [IMPL:input] (output ++ [hd operators]) (tl operators)
+ | otherwise = shunting_yard input output [IMPL:operators]
+ shunting_yard [EQUIV:input] output operators
+ | not (isEmpty operators) && (hd operators == NOT || hd operators == AND || hd operators == OR || hd operators == IMPL) = shunting_yard [EQUIV:input] (output ++ [hd operators]) (tl operators)
+ | otherwise = shunting_yard input output [EQUIV:operators]
+ shunting_yard [ip:input] output operators
+ | cIsAtom [ip] || cIsBool [ip] = shunting_yard input (output ++ [ip]) operators
+ | otherwise = abort ("Unknown character " +++ toString ip)
+
+ // Parse the outcome of the shunting yard algorithm into one expression
+ parse_stack :: [Char] -> Expr
+ parse_stack [] = abort "Cannot parse: empty input"
+ parse_stack cs = parse_stack` cs []
+ where
+ parse_stack` :: [Char] [Expr] -> Expr
+ parse_stack` [] [e] = e
+ parse_stack` [] [] = abort "Cannot parse: not enough expressoins on the stack"
+ parse_stack` [] es = abort ("Cannot parse: too many expressions on the stack:\n" +++ join "\n" (map toString es) +++ "\n")
+ parse_stack` [AND:st] [e1:[e2:es]] = parse_stack` st [App2 e2 And e1:es]
+ parse_stack` [OR:st] [e1:[e2:es]] = parse_stack` st [App2 e2 Or e1:es]
+ parse_stack` [IMPL:st] [e1:[e2:es]] = parse_stack` st [App2 e2 Impl e1:es]
+ parse_stack` [EQUIV:st] [e1:[e2:es]] = parse_stack` st [App2 e2 Equiv e1:es]
+ parse_stack` [NOT:st] [e:es] = parse_stack` st [App1 Not e:es]
+ parse_stack` [c:st] es
+ | cIsAtom [c] = parse_stack` st [Atom (cToAtom [c]) : es]
+ | cIsBool [c] = parse_stack` st [B (cToBool [c]) : es]
+ parse_stack` [c:st] es = abort ("Cannot parse: tried to perform an operation (" +++ toString c +++ ") with too few expressions on the stack:\n" +++ join "," (map toString es) +++ "\n")
+
+ cIsOp :: [Char] -> Bool
+ cIsOp ['~'] = True
+ cIsOp ['&'] = True
+ cIsOp ['|'] = True
+ cIsOp ['->'] = True
+ cIsOp ['<->'] = True
+ cIsOp _ = False
+
+ cIsAtom :: [Char] -> Bool
+ cIsAtom [c] = 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z'
+ cIsAtom _ = False
+
+ cToAtom :: [Char] -> AtomName
+ cToAtom [c:cs] = c
+
+ cIsBool :: [Char] -> Bool
+ cIsBool ['1'] = True
+ cIsBool ['0'] = True
+ cIsBool _ = False
+
+ cToBool :: [Char] -> Bool
+ cToBool ['1'] = True
+ cToBool _ = False
+