{-# LANGUAGE Rank2Types #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE FlexibleInstances #-} module Main where import Language.Prolog.NanoProlog.Lib import Text.ParserCombinators.UU import System.IO -- * Running the Interpreter -- ** The main interpreter -- | The `main` program prompt for a file with Prolog rules and call the main -- interpreter loop main :: IO () main = do hSetBuffering stdin LineBuffering putStr "File with rules? " fn <- getLine s <- readFile fn let (rules, errors) = startParse (pList pRule) s if null errors then do mapM_ print rules loop rules else do putStrLn "No rules parsed" mapM_ print errors main -- | `loop` ask for a goal, and enuartes all solutions found, each preceded by -- a trace conatining the rules applied in a tree-like fashion loop :: [Rule] -> IO () loop rules = do putStr "goal? " s <- getLine unless (s == "quit") $ do let (goal, errors) = startParse pFun s if null errors then printSolutions (solve rules emptyEnv 0 [goal]) else do putStrLn "Some goals were expected:" mapM_ print errors loop rules -- | `printSolutions` takes the result of a treewalk, which constructs -- all the proofs, and pairs them with their final -- substitutions. Alternative approaches in printing are to print the -- raw proofs, i.e. without applying the final substitution (remove -- the @subst env@ ). This nicely shows how the intermediate variables -- come into life. By including the test on the length the facts -- directly stemming from the data base are not printed. This makes -- the proofs much shorter, but a bit less complete. printSolutions :: Result -> IO () printSolutions result = sequence_ [ do sequence_ [ putStrLn (prefix ++ " " ++ show (subst env pr)) | (prefix, pr@(p :<-: pp)) <- reverse proof -- , length pp >0 ] putStr "substitution: " putStrLn (show' env) void getLine | (proof, env) <- enumerateDepthFirst [] ["0"] result ]