341 lines
9.7 KiB
Haskell
341 lines
9.7 KiB
Haskell
module Builtins where
|
|
|
|
import Code
|
|
( Builtin(..)
|
|
, Cho(..)
|
|
, Code
|
|
, Datum(..)
|
|
, Dereferenced(..)
|
|
, Heap(..)
|
|
, Instr(..)
|
|
, Interp(..)
|
|
, InterpFn
|
|
, InterpFn
|
|
, derefHeap
|
|
, heapStruct
|
|
, newHeapVars
|
|
)
|
|
import qualified Compiler as Co
|
|
import Control.Monad.IO.Class (liftIO)
|
|
import Control.Monad.Trans.Class (lift)
|
|
import Control.Monad.Trans.State.Lazy (get, gets, modify)
|
|
import Data.Functor.Identity (runIdentity)
|
|
import Data.List (intercalate)
|
|
import qualified Data.Map as M
|
|
import Data.Maybe (fromJust)
|
|
import Env (PrlgEnv(..), findAtom, findStruct, prlgError)
|
|
import qualified IR
|
|
import Interpreter (backtrack)
|
|
import qualified Operators as O
|
|
import System.Console.Haskeline (getInputChar, outputStr, outputStrLn)
|
|
|
|
bi = Builtin
|
|
|
|
continue = pure Nothing
|
|
|
|
showTerm itos heap = runIdentity . heapStruct atom struct hrec heap
|
|
where
|
|
atom (Atom a) = pure $ "'" ++ itos M.! a ++ "'"
|
|
atom (Number n) = pure (show n)
|
|
atom VoidRef = pure "_"
|
|
struct (Struct (IR.Id h _)) args =
|
|
pure $ "'" ++ itos M.! h ++ "'(" ++ intercalate "," args ++ ")"
|
|
hrec (HeapRef hr) ref =
|
|
pure $
|
|
(if hr == ref
|
|
then "_X"
|
|
else "_Rec") ++
|
|
show hr
|
|
|
|
printLocals :: InterpFn
|
|
printLocals = do
|
|
scope <- gets (gvar . cur)
|
|
heap <- gets (heap . cur)
|
|
IR.StrTable _ _ itos <- gets strtable
|
|
flip traverse (M.assocs scope) $ \(local, ref) ->
|
|
lift . outputStrLn $
|
|
"_Local" ++ show local ++ " = " ++ showTerm itos heap ref
|
|
continue
|
|
|
|
promptRetry :: InterpFn
|
|
promptRetry = do
|
|
last <- gets (null . cho)
|
|
if last
|
|
then continue
|
|
else promptRetry'
|
|
|
|
promptRetry' :: InterpFn
|
|
promptRetry' = do
|
|
x <- lift $ getInputChar "? "
|
|
case x of
|
|
Just ';' -> backtrack
|
|
_ -> continue
|
|
|
|
withArgs :: [Int] -> ([Int] -> InterpFn) -> InterpFn
|
|
withArgs as f = do
|
|
scope <- gets (hvar . cur)
|
|
if all (`M.member` scope) as
|
|
then f $ map (scope M.!) as
|
|
else prlgError "arguments not bound"
|
|
|
|
write' :: InterpFn -> InterpFn
|
|
write' c =
|
|
withArgs [0] $ \[arg] -> do
|
|
heap <- gets (heap . cur)
|
|
IR.StrTable _ _ itos <- gets strtable
|
|
lift . outputStr $ showTerm itos heap arg
|
|
c --this now allows error fallthrough but we might like EitherT
|
|
|
|
write = write' continue
|
|
|
|
nl :: InterpFn
|
|
nl = do
|
|
lift $ outputStrLn ""
|
|
continue
|
|
|
|
writeln :: InterpFn
|
|
writeln = write' nl
|
|
|
|
assertFact :: (Code -> IR.Id -> PrlgEnv ()) -> InterpFn
|
|
assertFact addClause =
|
|
withArgs [0] $ \[arg] -> do
|
|
heap <- gets (heap . cur)
|
|
case Co.compileGoal . Co.squashVars <$>
|
|
Co.heapStructPrlgInt Nothing heap arg of
|
|
Just (U (Struct s):head) -> do
|
|
addClause (head ++ [NoGoal]) s
|
|
continue
|
|
_ -> prlgError "assert fact failure"
|
|
|
|
assertRule :: (Code -> IR.Id -> PrlgEnv ()) -> InterpFn
|
|
assertRule addClause =
|
|
withArgs [0, 1] $ \args -> do
|
|
scope <- gets (hvar . cur)
|
|
heap <- gets (heap . cur)
|
|
comma <- findAtom ","
|
|
cut <- findAtom "!"
|
|
case Co.squashVars . IR.CallI 0 <$>
|
|
traverse (Co.heapStructPrlgInt Nothing heap) args of
|
|
Just (IR.CallI 0 [hs, gs]) ->
|
|
let (U (Struct s):cs) =
|
|
Co.compileGoal hs ++ Co.seqGoals (Co.compileGoals comma cut gs)
|
|
in do addClause cs s
|
|
continue
|
|
_ -> prlgError "assert clause failure"
|
|
|
|
retractall :: InterpFn
|
|
retractall =
|
|
withArgs [0] $ \[arg] -> do
|
|
heap <- gets (heap . cur)
|
|
case derefHeap heap arg of
|
|
BoundRef _ (Atom a) ->
|
|
dropProcedure (IR.Id {IR.arity = 0, IR.str = a}) >> continue
|
|
BoundRef _ (Struct id) -> dropProcedure id >> continue
|
|
_ -> prlgError "retractall needs a struct"
|
|
|
|
call :: InterpFn
|
|
call =
|
|
withArgs [0] $ \[arg] -> do
|
|
heap@(Heap _ hmap) <- gets (heap . cur)
|
|
let exec base struct arity = do
|
|
cur <- gets cur
|
|
modify $ \s ->
|
|
s
|
|
{ cur =
|
|
cur
|
|
{ gol =
|
|
[Call, Goal, U struct] ++
|
|
[U $ hmap M.! (base + i) | i <- [1 .. arity]] ++ gol cur
|
|
}
|
|
}
|
|
continue
|
|
case derefHeap heap arg of
|
|
BoundRef addr struct@(Struct IR.Id {IR.arity = arity}) ->
|
|
exec addr struct arity
|
|
BoundRef addr (Atom a) ->
|
|
exec addr (Struct IR.Id {IR.arity = 0, IR.str = a}) 0
|
|
_ -> prlgError "not callable"
|
|
|
|
exec :: InterpFn
|
|
exec =
|
|
withArgs [0] $ \[arg] -> do
|
|
heap <- gets (heap . cur)
|
|
case Co.squashVars <$> Co.heapStructPrlgInt Nothing heap arg of
|
|
Just gs -> do
|
|
cur <- gets cur
|
|
comma <- findAtom ","
|
|
cut <- findAtom "!"
|
|
modify $ \s ->
|
|
s
|
|
{ cur =
|
|
cur
|
|
{ hvar = M.empty
|
|
, hed = Co.seqGoals (Co.compileGoals comma cut gs)
|
|
, gol = [LastCall]
|
|
}
|
|
}
|
|
continue
|
|
_ -> prlgError "goal exec failure"
|
|
|
|
{- struct assembly/disassembly -}
|
|
struct :: InterpFn
|
|
struct = do
|
|
heap <- gets (heap . cur)
|
|
scope <- gets (hvar . cur)
|
|
case derefHeap heap <$> scope M.!? 0 of
|
|
Just (BoundRef addr (Struct IR.Id {IR.arity = arity, IR.str = str})) ->
|
|
structUnify arity str
|
|
_ -> structAssemble
|
|
|
|
heapListLength listAtom heap ref = go 0 ref ref (id, fromJust . step)
|
|
where
|
|
nil r
|
|
| BoundRef _ str <- derefHeap heap r = str == Atom listAtom
|
|
| otherwise = False
|
|
step r
|
|
| BoundRef addr (Struct IR.Id {IR.arity = 2, IR.str = listAtom'}) <-
|
|
derefHeap heap r
|
|
, listAtom == listAtom' = Just (addr + 2)
|
|
| otherwise = Nothing
|
|
go n fast slow (f1, f2)
|
|
| nil fast = Just n
|
|
| Just fast' <- step fast =
|
|
if slow == fast'
|
|
then Nothing
|
|
else go (n + 1) fast' (f1 slow) (f2, f1)
|
|
| otherwise = Nothing
|
|
|
|
structAssemble :: InterpFn
|
|
structAssemble = do
|
|
heap <- gets (heap . cur)
|
|
scope <- gets (hvar . cur)
|
|
case derefHeap heap <$> scope M.!? 1 of
|
|
Just (BoundRef addr (Atom str)) -> do
|
|
listAtom <- findAtom "[]"
|
|
case scope M.!? 2 >>= heapListLength listAtom heap of
|
|
Just arity -> structUnify arity str
|
|
_ -> prlgError "struct arity unknown"
|
|
_ -> prlgError "struct id unknown"
|
|
|
|
structUnify arity str = do
|
|
cur <- gets cur
|
|
let h = heap cur
|
|
scope = hvar cur
|
|
listAtom <- findAtom "[]"
|
|
let hcode = map U $ maybe VoidRef HeapRef . (scope M.!?) <$> [0 .. 2]
|
|
(h', pvars) = newHeapVars arity h
|
|
structData =
|
|
Struct IR.Id {IR.arity = arity, IR.str = str} : map HeapRef pvars
|
|
paramsData =
|
|
concatMap
|
|
(\pv -> [Struct IR.Id {IR.arity = 2, IR.str = listAtom}, HeapRef pv])
|
|
pvars ++
|
|
[Atom listAtom]
|
|
gcode = map U $ structData ++ [Atom str] ++ paramsData
|
|
modify $ \s ->
|
|
s {cur = cur {heap = h', gol = gcode ++ gol cur, hed = hcode ++ hed cur}}
|
|
continue
|
|
|
|
{- operator management -}
|
|
op :: InterpFn
|
|
op = do
|
|
heap <- gets (heap . cur)
|
|
scope <- gets (hvar . cur)
|
|
IR.StrTable _ _ itos <- gets strtable
|
|
case sequence $ map (fmap (derefHeap heap) . (scope M.!?)) [0 .. 2] of
|
|
Just [BoundRef _ (Number prio), BoundRef _ (Atom fixityAtom), BoundRef _ (Atom opatom)]
|
|
| Just op <-
|
|
(,) <$> itos M.!? opatom <*>
|
|
(O.Op prio <$> ((itos M.!? fixityAtom) >>= O.fixity)) -> do
|
|
modify $ \s -> s {ops = op : ops s}
|
|
continue
|
|
_ -> prlgError "bad op spec"
|
|
|
|
stashOps :: InterpFn
|
|
stashOps = do
|
|
currentOps <- gets ops
|
|
modify $ \s -> s {opstash = currentOps : opstash s}
|
|
continue
|
|
|
|
popOps :: InterpFn
|
|
popOps = do
|
|
currentOps <- gets opstash
|
|
case currentOps of
|
|
[] -> prlgError "no op stash to pop"
|
|
(ops':opss) -> do
|
|
modify $ \s -> s {ops = ops', opstash = opss}
|
|
continue
|
|
|
|
{- adding the builtins -}
|
|
addOp :: (String, O.Op) -> PrlgEnv ()
|
|
addOp op = modify $ \s -> s {ops = op : ops s}
|
|
|
|
modDef :: ([Code] -> Maybe [Code]) -> IR.Id -> PrlgEnv ()
|
|
modDef fn struct =
|
|
modify $ \s -> s {defs = M.alter (maybe (fn []) fn) struct $ defs s}
|
|
|
|
addClauseZ :: Code -> IR.Id -> PrlgEnv ()
|
|
addClauseZ code = modDef $ Just . ([code] ++)
|
|
|
|
addClauseA :: Code -> IR.Id -> PrlgEnv ()
|
|
addClauseA code = modDef $ Just . (code :)
|
|
|
|
addProcedure :: [Code] -> IR.Id -> PrlgEnv ()
|
|
addProcedure heads = modDef $ Just . const heads
|
|
|
|
dropProcedure :: IR.Id -> PrlgEnv ()
|
|
dropProcedure = modDef $ const Nothing
|
|
|
|
addProc :: [Code] -> String -> Int -> PrlgEnv ()
|
|
addProc c n a = findStruct n a >>= addProcedure c
|
|
|
|
addBi :: InterpFn -> String -> Int -> PrlgEnv ()
|
|
addBi b n a =
|
|
addProc [[U (LocalRef $ r - 1) | r <- [1 .. a]] ++ [Invoke $ bi b]] n a
|
|
|
|
{- actual prlgude -}
|
|
addPrelude :: PrlgEnv ()
|
|
addPrelude = do
|
|
pure undefined
|
|
{- primitives -}
|
|
addBi (pure Nothing) "true" 0
|
|
addBi backtrack "fail" 0
|
|
addOp $ O.xfx "=" 700
|
|
addProc [[U (LocalRef 0), U (LocalRef 0), NoGoal]] "=" 2
|
|
{- clauses -}
|
|
addOp $ O.xfy "," 1000
|
|
addOp $ O.xfx ":-" 1200
|
|
addOp $ O.fx ":-" 1200
|
|
horn1 <- findStruct ":-" 1
|
|
horn2 <- findStruct ":-" 2
|
|
let assertCode ac =
|
|
[ [ U (Struct horn2)
|
|
, U (LocalRef 0)
|
|
, U (LocalRef 1)
|
|
, Cut
|
|
, Invoke . bi $ assertRule ac
|
|
]
|
|
, [U (Struct horn1), U (LocalRef 0), Cut, Invoke $ bi exec]
|
|
, [U (LocalRef 0), Invoke . bi $ assertFact ac]
|
|
]
|
|
in do addProc (assertCode addClauseA) "asserta" 1
|
|
addProc (assertCode addClauseZ) "assertz" 1
|
|
addProc (assertCode addClauseZ) "assert" 1
|
|
addBi retractall "retractall" 1
|
|
addBi call "call" 1
|
|
addBi struct "struct" 3
|
|
{- operators -}
|
|
addBi op "op" 3
|
|
addBi stashOps "stash_operators" 0
|
|
addBi popOps "pop_operators" 0
|
|
{- query tools -}
|
|
addBi printLocals "print_locals" 0
|
|
addBi promptRetry' "prompt_retry" 0
|
|
addBi (printLocals >> promptRetry) "query" 0
|
|
{- IO -}
|
|
addBi write "write" 1
|
|
addBi writeln "writeln" 1
|
|
addBi nl "nl" 0
|
|
{- debug -}
|
|
addBi (get >>= liftIO . print >> pure Nothing) "interpreter_trace" 0
|