interpreter interprets.
This commit is contained in:
		
							parent
							
								
									8f47919624
								
							
						
					
					
						commit
						8eb307e3e1
					
				|  | @ -43,8 +43,8 @@ seqGoals [x] = [Goal] ++ x ++ [LastCall] | ||||||
| seqGoals [x, [Cut]] = [Goal] ++ x ++ [LastCall, Cut] | seqGoals [x, [Cut]] = [Goal] ++ x ++ [LastCall, Cut] | ||||||
| seqGoals (x:xs) = [Goal] ++ x ++ [Call] ++ seqGoals xs | seqGoals (x:xs) = [Goal] ++ x ++ [Call] ++ seqGoals xs | ||||||
| 
 | 
 | ||||||
| compileRule :: Id -> Id -> PrlgInt -> Code | compileClause :: Id -> Id -> PrlgInt -> Code | ||||||
| compileRule proveop andop = go | compileClause proveop andop = go | ||||||
|   where |   where | ||||||
|     go :: PrlgInt -> Code |     go :: PrlgInt -> Code | ||||||
|     go h@(CallI x args) |     go h@(CallI x args) | ||||||
|  |  | ||||||
							
								
								
									
										128
									
								
								app/Frontend.hs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								app/Frontend.hs
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,128 @@ | ||||||
|  | module Frontend where | ||||||
|  | 
 | ||||||
|  | import qualified Compiler as C | ||||||
|  | import Control.Monad.IO.Class | ||||||
|  | import Control.Monad.Trans.Class | ||||||
|  | import Control.Monad.Trans.State.Lazy | ||||||
|  | import Data.Foldable (traverse_) | ||||||
|  | import qualified Data.Map as M | ||||||
|  | import qualified Interpreter as I | ||||||
|  | import qualified Parser as P | ||||||
|  | import System.Console.Haskeline | ||||||
|  | import qualified Text.Megaparsec as MP | ||||||
|  | import Text.Pretty.Simple | ||||||
|  | 
 | ||||||
|  | data PrlgState = | ||||||
|  |   PrlgState | ||||||
|  |     { defs :: I.Defs | ||||||
|  |     , ops :: P.Ops | ||||||
|  |     , strtable :: I.StrTable | ||||||
|  |     } | ||||||
|  |   deriving (Show) | ||||||
|  | 
 | ||||||
|  | type PrlgEnv a = StateT PrlgState (InputT IO) a | ||||||
|  | 
 | ||||||
|  | ppr :: Show a => a -> PrlgEnv () | ||||||
|  | ppr x = | ||||||
|  |   liftIO $ | ||||||
|  |   pPrintOpt | ||||||
|  |     CheckColorTty | ||||||
|  |     defaultOutputOptionsDarkBg | ||||||
|  |       { outputOptionsCompactParens = True | ||||||
|  |       , outputOptionsIndentAmount = 2 | ||||||
|  |       , outputOptionsPageWidth = 80 | ||||||
|  |       } | ||||||
|  |     x | ||||||
|  | 
 | ||||||
|  | withStrTable :: (I.StrTable -> (I.StrTable, a)) -> PrlgEnv a | ||||||
|  | withStrTable f = do | ||||||
|  |   st <- gets strtable | ||||||
|  |   let (st', x) = f st | ||||||
|  |   modify (\s -> s {strtable = st'}) | ||||||
|  |   return x | ||||||
|  | 
 | ||||||
|  | findStruct :: String -> Int -> PrlgEnv I.Id | ||||||
|  | findStruct str arity = do | ||||||
|  |   stri <- findAtom str | ||||||
|  |   return I.Id {I.str = stri, I.arity = arity} | ||||||
|  | 
 | ||||||
|  | findAtom :: String -> PrlgEnv Int | ||||||
|  | findAtom = withStrTable . flip I.strtablize | ||||||
|  | 
 | ||||||
|  | interpret :: String -> PrlgEnv Bool | ||||||
|  | interpret = (>> return True) . lex | ||||||
|  |   where | ||||||
|  |     lex input = do | ||||||
|  |       case MP.parse P.lexPrlg "-" input of | ||||||
|  |         Left bundle -> liftIO $ putStr (MP.errorBundlePretty bundle) | ||||||
|  |         Right toks -> parse toks | ||||||
|  |     parse toks = do | ||||||
|  |       case MP.parse P.parsePrlg "-" toks of | ||||||
|  |         Left bundle -> liftIO $ putStr (MP.errorBundlePretty bundle) | ||||||
|  |         Right asts -> traverse_ prologize asts | ||||||
|  |     prologize ast = do | ||||||
|  |       o <- gets ops | ||||||
|  |       case P.ast2prlg o ast of | ||||||
|  |         Left err -> liftIO $ putStrLn $ "expression parsing: " ++ err | ||||||
|  |         Right prlg -> intern prlg | ||||||
|  |     intern prlgs = do | ||||||
|  |       prlgi <- withStrTable $ flip C.strtablizePrlg prlgs | ||||||
|  |       compile prlgi | ||||||
|  |     compile prlgi | ||||||
|  |       {- TODO: switch between prove goal/compile clause here -} | ||||||
|  |      = do | ||||||
|  |       commaId <- findStruct "," 2 | ||||||
|  |       let code = C.seqGoals $ C.compileGoals commaId prlgi | ||||||
|  |       execute code | ||||||
|  |     execute code = do | ||||||
|  |       ds <- gets defs | ||||||
|  |       let (_, res) = I.prove code ds | ||||||
|  |       case res of | ||||||
|  |         Left err -> liftIO $ putStrLn err | ||||||
|  |         Right res -> | ||||||
|  |           liftIO $ | ||||||
|  |           putStrLn $ | ||||||
|  |           if res | ||||||
|  |             then "yes." | ||||||
|  |             else "no proof." | ||||||
|  | 
 | ||||||
|  | addBuiltins = do | ||||||
|  |   a1 <- findStruct "a" 1 | ||||||
|  |   a <- findAtom "a" | ||||||
|  |   b <- findAtom "b" | ||||||
|  |   c <- findAtom "c" | ||||||
|  |   b0 <- findStruct "b" 0 | ||||||
|  |   modify $ \s -> | ||||||
|  |     s | ||||||
|  |       { defs = | ||||||
|  |           M.fromList | ||||||
|  |             [ (a1, [[I.U (I.Atom a), I.NoGoal], [I.U (I.Atom b), I.NoGoal]]) | ||||||
|  |             , ( b0 | ||||||
|  |               , [ [I.Goal, I.U (I.Struct a1), I.U (I.Atom c), I.LastCall] | ||||||
|  |                 , [I.Goal, I.U (I.Struct a1), I.U (I.Atom b), I.LastCall] | ||||||
|  |                 ]) | ||||||
|  |             ] | ||||||
|  |       , ops = [(",", P.Op 1000 $ P.Infix P.X P.Y)] | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  | interpreterStart :: PrlgEnv () | ||||||
|  | interpreterStart = do | ||||||
|  |   addBuiltins | ||||||
|  |   interpreterLoop | ||||||
|  | 
 | ||||||
|  | interpreterLoop :: PrlgEnv () | ||||||
|  | interpreterLoop = do | ||||||
|  |   minput <- lift $ getInputLine "prlg> " | ||||||
|  |   case minput of | ||||||
|  |     Nothing -> return () | ||||||
|  |     Just input -> do | ||||||
|  |       continue <- interpret input | ||||||
|  |       if continue | ||||||
|  |         then interpreterLoop | ||||||
|  |         else return () | ||||||
|  | 
 | ||||||
|  | interpreter :: InputT IO () | ||||||
|  | interpreter = | ||||||
|  |   evalStateT | ||||||
|  |     interpreterStart | ||||||
|  |     (PrlgState {defs = M.empty, ops = [], strtable = I.emptystrtable}) | ||||||
							
								
								
									
										44
									
								
								app/Main.hs
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								app/Main.hs
									
									
									
									
									
								
							|  | @ -1,46 +1,8 @@ | ||||||
| module Main where | module Main where | ||||||
| 
 | 
 | ||||||
| import qualified Compiler as C | import Control.Monad | ||||||
| import Control.Monad.IO.Class |  | ||||||
| import qualified Data.Map as M |  | ||||||
| import qualified Interpreter as I |  | ||||||
| import qualified Parser as P |  | ||||||
| import System.Console.Haskeline | import System.Console.Haskeline | ||||||
| import qualified Text.Megaparsec as MP | import Frontend (interpreter) | ||||||
| import Text.Pretty.Simple |  | ||||||
| 
 |  | ||||||
| ppr :: Show a => a -> IO () |  | ||||||
| ppr = |  | ||||||
|   pPrintOpt |  | ||||||
|     CheckColorTty |  | ||||||
|     defaultOutputOptionsDarkBg |  | ||||||
|       { outputOptionsCompactParens = True |  | ||||||
|       , outputOptionsIndentAmount = 2 |  | ||||||
|       , outputOptionsPageWidth = 80 |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
| interpret :: String -> IO () |  | ||||||
| interpret = lex |  | ||||||
|   where |  | ||||||
|     lex input = |  | ||||||
|       case MP.parse P.lexPrlg "-" input of |  | ||||||
|         Left bundle -> putStr (MP.errorBundlePretty bundle) |  | ||||||
|         Right toks -> parse toks |  | ||||||
|     parse toks = |  | ||||||
|       case MP.parse P.parsePrlg "-" toks of |  | ||||||
|         Left bundle -> putStr (MP.errorBundlePretty bundle) |  | ||||||
|         Right ast -> prologize ast |  | ||||||
|     prologize ast = ppr $ map (P.ast2prlg P.defaultOps) ast |  | ||||||
| 
 | 
 | ||||||
| main :: IO () | main :: IO () | ||||||
| main = | main = runInputT defaultSettings interpreter | ||||||
|   runInputT defaultSettings loop |  | ||||||
|   where |  | ||||||
|     loop :: InputT IO () |  | ||||||
|     loop = do |  | ||||||
|       minput <- getInputLine "prlg> " |  | ||||||
|       case minput of |  | ||||||
|         Nothing -> return () |  | ||||||
|         Just input -> do |  | ||||||
|           liftIO $ interpret input |  | ||||||
|           loop |  | ||||||
|  |  | ||||||
|  | @ -13,7 +13,6 @@ import Text.Megaparsec | ||||||
| import Text.Megaparsec.Char | import Text.Megaparsec.Char | ||||||
| 
 | 
 | ||||||
| import Compiler (PrlgStr(..)) | import Compiler (PrlgStr(..)) | ||||||
| import Debug.Trace |  | ||||||
| 
 | 
 | ||||||
| singleToks = ",;|()[]" | singleToks = ",;|()[]" | ||||||
| 
 | 
 | ||||||
|  | @ -83,7 +82,8 @@ instance TraversableStream [Lexeme] where | ||||||
|       handleEmpty x = x |       handleEmpty x = x | ||||||
|       go |       go | ||||||
|         | o <= pstateOffset pst = |         | o <= pstateOffset pst = | ||||||
|           ( Just . handleEmpty $ pstateLinePrefix pst ++ |           ( Just . handleEmpty $ | ||||||
|  |             pstateLinePrefix pst ++ | ||||||
|             takeWhile (/= '\n') (concatMap showTok $ pstateInput pst) |             takeWhile (/= '\n') (concatMap showTok $ pstateInput pst) | ||||||
|           , pst) |           , pst) | ||||||
|         | o > pstateOffset pst = |         | o > pstateOffset pst = | ||||||
|  | @ -209,34 +209,21 @@ numArgs (Op _ f) = go f | ||||||
| 
 | 
 | ||||||
| type Ops = [(String, Op)] | type Ops = [(String, Op)] | ||||||
| 
 | 
 | ||||||
| defaultOps :: Ops |  | ||||||
| defaultOps = |  | ||||||
|   [ ("", Op 0 $ Infix X Y) |  | ||||||
|   , ("+", Op 100 $ Prefix X) |  | ||||||
|   , ("!", Op 100 $ Suffix Y) |  | ||||||
|   , ("-", Op 100 $ Prefix Y) |  | ||||||
|   , ("*", Op 100 $ Infix Y X) |  | ||||||
|   , ("+", Op 200 $ Infix Y X) |  | ||||||
|   , ("++", Op 200 $ Infix X Y) |  | ||||||
|   , ("-", Op 200 $ Infix Y X) |  | ||||||
|   , ("<", Op 300 $ Infix X X) |  | ||||||
|   , (">", Op 300 $ Infix X X) |  | ||||||
|   , ("=", Op 400 $ Infix X X) |  | ||||||
|   , (",", Op 800 $ Infix X Y) |  | ||||||
|   , (";", Op 900 $ Infix X Y) |  | ||||||
|   , (":-", Op 1000 $ Infix X X) |  | ||||||
|   ] |  | ||||||
| 
 |  | ||||||
| type PrlgError = String | type PrlgError = String | ||||||
|  | 
 | ||||||
| type PrlgResult = Either PrlgError PrlgStr | type PrlgResult = Either PrlgError PrlgStr | ||||||
|  | 
 | ||||||
| err :: PrlgError -> Either PrlgError a | err :: PrlgError -> Either PrlgError a | ||||||
| err = Left | err = Left | ||||||
| 
 | 
 | ||||||
| ast2prlg :: Ops -> AST -> PrlgResult | ast2prlg :: Ops -> AST -> PrlgResult | ||||||
| ast2prlg ot (List _ _) = err "no lists yet" | ast2prlg ot = ast2prlg' (("", Op 0 $ Infix X Y) : ot) | ||||||
| ast2prlg ot (Seq ss) = shunt ot ss | 
 | ||||||
| ast2prlg ot (Literal s) = pure (LiteralS s) | ast2prlg' :: Ops -> AST -> PrlgResult | ||||||
| ast2prlg ot (Call fn ss) = CallS fn <$> traverse (shunt ot) ss | ast2prlg' ot (List _ _) = err "no lists yet" | ||||||
|  | ast2prlg' ot (Seq ss) = shunt ot ss | ||||||
|  | ast2prlg' ot (Literal s) = pure (LiteralS s) | ||||||
|  | ast2prlg' ot (Call fn ss) = CallS fn <$> traverse (shunt ot) ss | ||||||
| 
 | 
 | ||||||
| shunt :: Ops -> [AST] -> PrlgResult | shunt :: Ops -> [AST] -> PrlgResult | ||||||
| shunt optable = start | shunt optable = start | ||||||
|  | @ -245,38 +232,51 @@ shunt optable = start | ||||||
|     start [x] = rec x --singleton, possibly either a single operator name or a single value |     start [x] = rec x --singleton, possibly either a single operator name or a single value | ||||||
|     start [] = err "empty parentheses?" |     start [] = err "empty parentheses?" | ||||||
|     start xs = wo [] [] xs |     start xs = wo [] [] xs | ||||||
|  |     resolve = foldr1 (<|>) | ||||||
|     {- "want operand" state, incoming literal -} |     {- "want operand" state, incoming literal -} | ||||||
|     wo :: Ops -> [PrlgStr] -> [AST] -> PrlgResult |     wo :: Ops -> [PrlgStr] -> [AST] -> PrlgResult | ||||||
|     wo ops vs (l@(Literal x):xs) |     wo ops vs (l@(Literal x):xs) = | ||||||
|       | Right _ <- getPrefix x |       resolve | ||||||
|       , Right (ops', vs') <- pushPrefix ops vs x = wo ops' vs' xs |         [ do getPrefix x | ||||||
|       | isOperand x = do l' <- rec l |              (ops', vs') <- pushPrefix ops vs x | ||||||
|                          ho ops (l' : vs) xs |              wo ops' vs' xs | ||||||
|       | otherwise = err "expected operand" |         , do getOperand x | ||||||
|  |              l' <- rec l | ||||||
|  |              ho ops (l' : vs) xs | ||||||
|  |         , err "expected operand" | ||||||
|  |         ] | ||||||
|     {- incoming non-literal (i.e., surely operand), push it and switch to "have operand" -} |     {- incoming non-literal (i.e., surely operand), push it and switch to "have operand" -} | ||||||
|     wo ops vs (x:xs) = do x' <- rec x |     wo ops vs (x:xs) = do | ||||||
|                           ho ops (x' : vs) xs |       x' <- rec x | ||||||
|  |       ho ops (x' : vs) xs | ||||||
|     {- end of stream, but the operand is missing -} |     {- end of stream, but the operand is missing -} | ||||||
|     wo ops vs [] = err "expected final operand" |     wo ops vs [] = err "expected final operand" | ||||||
|     {- "have operand" state, incoming operator -} |     {- "have operand" state, expecting an operator -} | ||||||
|     ho :: Ops -> [PrlgStr] -> [AST] -> PrlgResult |     ho :: Ops -> [PrlgStr] -> [AST] -> PrlgResult | ||||||
|     ho ops vs xs'@(Literal x:xs) |     ho ops vs xs'@(Literal x:xs) = | ||||||
|       | Right _ <- getSuffix x |       resolve | ||||||
|       , Right (ops', vs') <- pushSuffix ops vs x = ho ops' vs' xs |         [ do getSuffix x | ||||||
|       | Right _ <- getInfix x |              (ops', vs') <- pushSuffix ops vs x | ||||||
|       , Right (ops', vs') <- pushInfix ops vs x = wo ops' vs' xs |              ho ops' vs' xs | ||||||
|       | isOperand x = ho ops vs (Literal "" : xs') -- app (see below) |         , do getInfix x | ||||||
|       | Right _ <- getPrefix x = ho ops vs (Literal "" : xs') -- also app! |              (ops', vs') <- pushInfix ops vs x | ||||||
|       | otherwise = err "expected infix or suffix operator" |              wo ops' vs' xs | ||||||
|     {- incoming operand; there's an app between -} |         , do getOperand x | ||||||
|  |              ho ops vs (Literal "" : xs') -- app (see below) | ||||||
|  |         , do getPrefix x | ||||||
|  |              ho ops vs (Literal "" : xs') -- also app! | ||||||
|  |         , err "expected infix or suffix operator" | ||||||
|  |         ] | ||||||
|  |     {- incoming non-literal operand; there must be an app in between -} | ||||||
|     ho ops vs xs@(_:_) = ho ops vs (Literal "" : xs) |     ho ops vs xs@(_:_) = ho ops vs (Literal "" : xs) | ||||||
|     {- the last operand was last, pop until finished -} |     {- the last operand was last, pop until finished -} | ||||||
|     ho [] [res] [] = pure res |     ho [] [res] [] = pure res | ||||||
|     ho ops vs [] = do (ops', vs') <- pop ops vs |     ho ops vs [] = do | ||||||
|                       ho ops' vs' [] |       (ops', vs') <- pop ops vs | ||||||
|  |       ho ops' vs' [] | ||||||
|     {- recurse to delimited subexpression -} |     {- recurse to delimited subexpression -} | ||||||
|     rec :: AST -> PrlgResult |     rec :: AST -> PrlgResult | ||||||
|     rec = ast2prlg optable |     rec = ast2prlg' optable | ||||||
|     {- pop a level, possibly uncovering a higher prio -} |     {- pop a level, possibly uncovering a higher prio -} | ||||||
|     pop ((x, Op _ (Infix _ _)):ops) (r:l:vs) = pure (ops, (CallS x [l, r] : vs)) |     pop ((x, Op _ (Infix _ _)):ops) (r:l:vs) = pure (ops, (CallS x [l, r] : vs)) | ||||||
|     pop ((x, Op _ (Prefix _)):ops) (p:vs) = pure (ops, (CallS x [p] : vs)) |     pop ((x, Op _ (Prefix _)):ops) (p:vs) = pure (ops, (CallS x [p] : vs)) | ||||||
|  | @ -288,7 +288,9 @@ shunt optable = start | ||||||
|     getPrefix x = uniq [op | (s, op@(Op _ (Prefix _))) <- optable, s == x] |     getPrefix x = uniq [op | (s, op@(Op _ (Prefix _))) <- optable, s == x] | ||||||
|     getSuffix x = uniq [op | (s, op@(Op _ (Suffix _))) <- optable, s == x] |     getSuffix x = uniq [op | (s, op@(Op _ (Suffix _))) <- optable, s == x] | ||||||
|     getInfix x = uniq [op | (s, op@(Op _ (Infix _ _))) <- optable, s == x] |     getInfix x = uniq [op | (s, op@(Op _ (Infix _ _))) <- optable, s == x] | ||||||
|     isOperand x = null [op | (s, op) <- optable, s == x] |     getOperand x | ||||||
|  |       | null [op | (s, op) <- optable, s == x] = pure () | ||||||
|  |       | otherwise = err "expected an operand" | ||||||
|     {- actual pushery -} |     {- actual pushery -} | ||||||
|     canPush :: Ops -> Op -> Either PrlgError Bool |     canPush :: Ops -> Op -> Either PrlgError Bool | ||||||
|     canPush [] op = pure True |     canPush [] op = pure True | ||||||
|  | @ -336,5 +338,6 @@ shunt optable = start | ||||||
|       cp <- canPush ops op |       cp <- canPush ops op | ||||||
|       if cp |       if cp | ||||||
|         then pure ((x, op) : ops, vs) |         then pure ((x, op) : ops, vs) | ||||||
|         else do (ops', vs') <- pop ops vs |         else do | ||||||
|                 shunt1 ops' vs' x op --prefix would behave differently here but that's impossible by canPush |           (ops', vs') <- pop ops vs | ||||||
|  |           shunt1 ops' vs' x op --prefix would behave differently here but that's impossible by canPush | ||||||
|  |  | ||||||
|  | @ -25,10 +25,10 @@ executable prlg | ||||||
|     main-is:          Main.hs |     main-is:          Main.hs | ||||||
| 
 | 
 | ||||||
|     -- Modules included in this executable, other than Main. |     -- Modules included in this executable, other than Main. | ||||||
|     other-modules: Interpreter, Compiler, Parser |     other-modules: Interpreter, Compiler, Parser, Frontend | ||||||
| 
 | 
 | ||||||
|     -- LANGUAGE extensions used by modules in this package. |     -- LANGUAGE extensions used by modules in this package. | ||||||
|     -- other-extensions: |     -- other-extensions: | ||||||
|     build-depends:    base >=4.16, containers, megaparsec, haskeline, pretty-simple, split |     build-depends:    base >=4.16, containers, megaparsec, haskeline, pretty-simple, split, transformers | ||||||
|     hs-source-dirs:   app |     hs-source-dirs:   app | ||||||
|     default-language: Haskell2010 |     default-language: Haskell2010 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue