-- Implementierung der Stackmachine für rekursive Programme -- VLFP 2006 -- -- Jost Berthold, FB12, Philipps-Universität Marburg --------------------------------------------------------- import Data.List import Data.Maybe -- Maschinenbefehle data Instr = Jmp [Int] | Jmc [Int] | Load Int | Exec BaseVal | Create Int [Int] | Ret | Noop -- no operation deriving (Eq,Show) -- omega, erweiterbar nach Bedarf data BaseVal = N Int | B Bool | Q Double | Plus | Minus | Times | Div -- arithmetisch (Int) | LessEq -- Zahl -> boolean | And | Or | Not -- boolesch | If -- built-in conditional deriving (Eq,Show) ---------------------------------------------- -- Maschinenzustand -- Kellerspitze jeweils links! -- auf DK und FK befinden sich nur (N Int) und (B Bool)! -- (natürliche Zahlen auf FK werden als N codiert) type MachState = (Int , [BaseVal], [BaseVal]) -- Semantik der Basisoperationen, liefert Resultat-DK zurück doOp :: BaseVal -> [BaseVal] -> [BaseVal] doOp (N n) dk = (N n):dk doOp (B b) dk = (B b):dk doOp (Q b) dk = (Q b):dk -- Q doOp Plus ((Q k):(Q l):rest) = Q (k+l):rest doOp Minus ((Q k):(Q l):rest) = Q (l-k):rest doOp Times ((Q k):(Q l):rest) = Q (k*l):rest doOp Div ((Q k):(Q l):rest) = Q (l / k):rest doOp LessEq ((Q k):(Q l):rest) = B (l<=k):rest -- N doOp Plus ((N k):(N l):rest) = N (k+l):rest doOp Minus ((N k):(N l):rest) = N (l-k):rest doOp Times ((N k):(N l):rest) = N (k*l):rest doOp Div ((N k):(N l):rest) = Q (fromIntegral l / fromIntegral k):rest doOp LessEq ((N k):(N l):rest) = B (l<=k):rest -- typecasts doOp Plus ((Q k):(N l):rest) = Q (k+fromIntegral l):rest doOp Plus ((N l):(Q k):rest) = Q (k+fromIntegral l):rest doOp Minus ((N k):(Q l):rest) = Q (l-fromIntegral k):rest doOp Minus ((Q k):(N l):rest) = Q (fromIntegral l - k):rest doOp Times ((Q k):(N l):rest) = Q (k*fromIntegral l):rest doOp Times ((N l):(Q k):rest) = Q (k*fromIntegral l):rest doOp Div ((N k):(Q l):rest) = Q (l / fromIntegral k):rest doOp Div ((Q k):(N l):rest) = Q (fromIntegral l / k):rest doOp LessEq ((Q k):(N l):rest) = B (fromIntegral l <= k):rest doOp LessEq ((N k):(Q l):rest) = B (l <= fromIntegral k):rest -- Bool doOp And ((B x):(B y):rest) = B (x && y):rest doOp Or ((B x):(B y):rest) = B (x || y):rest doOp Not ((B x):rest) = B (not x):rest doOp If ((B x):a1:a2:rest) = (if x then a1 else a2):rest doOp op dk = error ("Operation " ++ show op ++ " undefiniert mit DK " ++ shownDK ++ "...") where shownDK = unwords $ map show $ take 3 dk -- Semantik der Maschinenbefehle exec :: Instr -> MachState -> MachState exec (Jmp [n]) (_,d,fk) = (n,d,fk) exec (Jmc [n]) (m,(B b):d,fk) | not b = (n,d,fk) | b = (m+1,d,fk) exec (Load k) (m,d,fk) | 1 <= k && k < 1+ length fk = (m+1,(fk!!(k+1)):d,fk) exec (Exec op) (m,d,fk) = (m+1,doOp op d, fk) exec (Create k [r]) (m,d,fk) | length d >= k = (m+1, drop k d, (N r):(N (k+1)):(take k d) ++ fk) exec Ret (m,d,((N ra):(N dl):fk)) = (ra, d, drop (dl-1) fk) exec Noop (m,d,fk) = (m+1,d,fk) exec instr state = error ( "Befehl " ++ show instr ++ "undefiniert im Zustand " ++ show state) ----------------------------------------------- -- Programme: type SCTree = [([Int],Instr)] -- Baum-artige Adressen type SC = [Instr] -- Lineare Adressen (Instr.sequenz) -- Programmsemantik: single step ea :: SC -> MachState -> MachState ea is state@(m,_,_) | 1 <= m && m <= length is = exec (is!!(m-1)) state | otherwise = state -- Programmsemantik: iteration ia is state@(m,_,_) | m == 0 = state | otherwise = ia is (ea is state) iaTrace :: SC -> MachState -> [MachState] iaTrace is = (takeWhile (not . isStopState)) . (iterate (ea is)) where isStopState (m,_,_) = m == 0 input :: [Int] -> MachState input inNums = (1,[],N 0:N (1+length inNums):map N inNums) output :: MachState -> [String] output (0,dk,[]) = map fromB dk where fromB (N k) = show k fromB (Q q) = show q fromB x = error ("fromB: unerwarteter Wert " ++ show x) output state = error ("output: Zustand " ++ show state) run :: SC -> [Int] -> [String] run prog ins = output (ia prog (input ins)) trace :: SC -> [Int] -> ([MachState],[String]) trace prog ins = (states, output (ea prog (last states))) where states = iaTrace prog (input ins) ----------------------------------------------- -- F x = if 1 <= x then x-1 else F (F (x+2)) example :: [([Int],Instr)] example = ([1],Exec (N 1)): ([] , Load 1): ([] , Exec LessEq): ([] , Jmc [3,1]): ([] , Load 1): ([] , Exec (N 1)): ([] , Exec Minus): ([] , Jmp [4,1]): ([3,1] , Load 1): ([] , Exec (N 2)): ([] , Exec Plus): ([] , Create 1 [0,1,3,1]): ([] , Jmp [1]): ([0,1,3,1], Create 1 [0,3,1]): ([0,3,1] , Noop): ([1,4] , Noop): ([] , Ret): [] -- pretty printer für Maschinenprogramme ppProgT :: SCTree -> String ppProgT p = unlines (map (ppInstT indent) p) where indent = 1+ 2 * maximum (map (length . fst) p) ppInstT :: Int -> ([Int],Instr) -> String ppInstT indent (lb,i) = (replicate (indent - length showLb) ' ') ++ showLb ++ show i where showLb = tail ((concatMap (('.':) . show) lb) ++ ": ") ppProg :: SC -> String ppProg is = unlines (zipWith (\n i -> show n ++ ":\t" ++ show i) [1..] is) -- conversion to std. addresses: convert :: SCTree -> SC convert ts = error "Schreib mich!" ------------------------------------------------ -- rekursive Programme: data Program = Rek [(Int,Expr)] -- Stelligkeit der F für pretty-printer angeben data Expr = Var Int | F Int [Expr] -- Int = Index von F im zugeh.GlS | Con BaseVal [Expr] deriving Eq instance Show Expr where show (Var i) = "x_" ++ show i show (F i args) = "F_" ++ show i ++ "(" ++ arglist ++ ") " where arglist = drop 2 (concatMap (\e -> ", "++ show e) args) show (Con (N b) [] ) = show b show (Con (B b) [] ) = show b -- waere schoener mit pattern match passend zu Funktionen in BaseVal show (Con b args ) = show b ++ "(" ++ arglist ++ ")" where arglist = drop 2 (concatMap (\e -> ", "++ show e) args) instance Show Program where show (Rek defs) = unlines (zipWith showF [1..] defs) showF :: Int -> (Int,Expr) -> String showF k (args,expr) = "F"++ show k ++ varString ++ " = " ++ show expr where varString = concatMap (\i -> " x_"++ show i) [1..args] ------------------------------------ -- Übersetzung transConv :: Program -> SC transConv = convert . trans trans :: Program -> SCTree trans (Rek defs) = concatMap ( (++ [([],Ret)]) . uncurry exptrans) (zip (map snd defs) (map (:[]) [1..])) exptrans :: Expr -> [Int] -> SCTree exptrans (Var i) l = [(l,Load i)] exptrans (Con b []) l = [(l,Exec b)] exptrans (Con b args) l = (l,Noop): concatMap (uncurry exptrans) (zip args (map (:l) [1..]) ) ++ [([],Exec b)] exptrans (Con If [e,e1,e2]) l = (l,Noop): exptrans e (1:l) ++ ([],Jmc (3:l)): exptrans e1 (2:l) ++ ([],Jmp (4:l)): (3:l,Noop): exptrans e2 (3:l) ++ [(4:l,Noop)] -- jump target only exptrans (F j args) l = (l,Noop): concatMap (uncurry exptrans) (zip args (map (:l) [1..j]) ) ++ ([],Create j (0:l)): ([],Jmp [j]): [(0:l,Noop)] exF :: Program exF = Rek -- F x = if 1 <= x then x-1 else F (F (x+2)) [(1, Con If [Con LessEq [Con (N 1) [],Var 1] , Con Minus [Var 1,Con (N 1) []] , F 1 [F 1 [Con Minus [Var 1, Con (N 2) []]]]] )] {- F x_1 = G x_1 x_1 (A 0 x_1) G x_1 x_2 x_3 = if (x_2 - x_3 < 0.001) then x_3 else G x_1 x_3 (A x_1 x_3)) A x_1 x_2 = (x_2 + x_1/x_2) / 2 -} ex2 :: Program ex2 = Rek [ -- F (1,F 2 [Var 1,Var 1, F 3 [Con (N 0) [], Var 1]]) -- G ,(3, Con If [Con LessEq [Con Minus [Var 2,Var 3],Con (Q 0.001 ) []] ,Var 3 ,F 2 [Var 1, Var 3, F 3 [Var 1,Var 3]] ]) -- A ,(2, Con Div [Con Plus [Var 2 , Con Div [Var 1,Var 2]] ,Con (N 2) []]) ]