module Main where import Prelude hiding (Ordering(..)) import System.Environment (getArgs) import System.IO import System.IO.Unsafe import Control.DeepSeq import Data.Char (toUpper) import Text.Parsec import Text.ParserCombinators.Parsec.Char import Text.Parsec.String (Parser) --import Debug.Trace trace _ x = x data Command = READ Int | WRITE Int | LOAD Int | STORE Int | LIT Int | ADD | MUL | SUB | DIV | MOD | LT | EQ | NE | GT | LE | GE | JMP Int | JMC Int | NOP deriving (Show, Eq) step :: Command -> (Int,[Int],[(Int,Int)],[Int],[Int]) -> (Int,[Int],[(Int,Int)],[Int],[Int]) step (READ n) (m,d,h,first:rest,out) = (m+1,d,update h n first,rest,out) step (WRITE n) (m,d,h,inp,out) = (m+1,d,h,inp,out ++ [write (get m h n)]) step (LOAD n) (m,d,h,inp,out) = let d1 = get m h n in seq d1 (m+1,d1:d,h,inp,out) step (STORE n) (m,d1:d',h,inp,out) = (m+1,d',update h n d1,inp,out) step (STORE _) (m,[],_,_,_) = error $ "In Programmzeile " ++ show m ++ ", Versuch auf leeren Datenkeller zuzugreifen." step (LIT z) (m,d,h,inp,out) = (m+1,z:d,h,inp,out) step ADD (m,d1:d2:d',h,inp,out) = (m+1,(d2+d1):d',h,inp,out) step MUL (m,d1:d2:d',h,inp,out) = (m+1,(d2*d1):d',h,inp,out) step SUB (m,d1:d2:d',h,inp,out) = (m+1,(d2-d1):d',h,inp,out) step DIV (m,d1:d2:d',h,inp,out) = (m+1,(div d2 d1):d',h,inp,out) step MOD (m,d1:d2:d',h,inp,out) = (m+1,(mod d2 d1):d',h,inp,out) step LT (m,d1:d2:d',h,inp,out) = (m+1,(if d2d1 then 1 else 0):d',h,inp,out) step LE (m,d1:d2:d',h,inp,out) = (m+1,(if d2<=d1 then 1 else 0):d',h,inp,out) step GE (m,d1:d2:d',h,inp,out) = (m+1,(if d2>=d1 then 1 else 0):d',h,inp,out) step (JMP e) (m,d,h,inp,out) = (e,d,h,inp,out) step (JMC e) (m,0:d',h,inp,out) = (e,d',h,inp,out) step (JMC e) (m,1:d',h,inp,out) = (m+1,d',h,inp,out) step (JMC _) (m,_:_,_,_,_) = error $ "In Programmzeile " ++ show m ++ ", versuchter JMC, obwohl Datenkellerspitze weder 0 noch 1." step NOP (m,d,h,inp,out) = (m+1,d,h,inp,out) step _ (m,[],_,_,_) = error $ "In Programmzeile " ++ show m ++ ", Versuch auf leeren Datenkeller zuzugreifen." step _ (m,[_],_,_,_) = error $ "In Programmzeile " ++ show m ++ ", nicht genug Werte auf Datenkeller." get :: Int -> [(Int,Int)] -> Int -> Int get m ((a,b):h) n = if a==n then b else get m h n get m [] _ = error $ "In Programmzeile " ++ show m ++ ", Versuch auf unbelegten Hauptspeicherplatz zuzugreifen." update :: [(Int,Int)] -> Int -> Int -> [(Int,Int)] update ((a,b):h) n c | a==n = (a,c):h | an = (n,c):(a,b):h update [] n c = [(n,c)] run :: [Command] -> (Int,[Int],[(Int,Int)],[Int],[Int]) -> [(Int,[Int],[(Int,Int)],[Int],[Int])] run prog conf = if valid prog conf then conf:(run prog (next prog conf)) else [conf] valid :: [Command] -> (Int,[Int],[(Int,Int)],[Int],[Int]) -> Bool valid prog (m,d,h,inp,out) = 1<=m && m<=(length prog) next :: [Command] -> (Int,[Int],[(Int,Int)],[Int],[Int]) -> (Int,[Int],[(Int,Int)],[Int],[Int]) next prog (m,d,h,inp,out) = step (prog !! (m-1)) (m,d,h,inp,out) --- BEGIN PARSER parseProgFromFile :: String -> IO (Either ParseError [Command]) parseProgFromFile fname = do input <- readFile fname return (runP progParser 1 fname input) progParser :: CharParser Int [Command] progParser = do ret <- many (try (lineParser "")) spaces "" (State s _ _) <- getParserState trace ("Parser State:\n" ++ s) (return ret) if s == "" then return (fromJust ret) else fail ("\n\nthe following program part can't be parsed:\n\n" ++ s) where fromJust [] = [] fromJust (Nothing : mas) = fromJust mas fromJust ((Just a) : mas) = a : fromJust mas lineParser :: CharParser Int (Maybe Command) lineParser = (try commentParser >> return Nothing) <|> (do { pc <- pcParser "expected command number\n"; ppc <- getState; if pc /= ppc then ((fail ("expected command number " ++ show ppc ++ ", but read command number " ++ show pc ++ "\n"))) else do { char ':' "expected ':'"; spaces ""; cmd <- (commandParser ""); spaces ""; optional (char ';'); spaces ""; (commentParser <|> return ()) "comment or end of line or other command number"; modifyState (+1); return (Just cmd); } }) pcParser :: CharParser Int Int pcParser = do spaces "" num <- (many1 (digit "")) "" spaces "" return ((read num)::Int) commandParser :: CharParser Int Command commandParser = do { cmd <- ((many1 letter) <|> lookAhead ((space <|> char ';') >> return "")) "expected command"; spaces ""; let noArgCmd = readCmd (map toUpper cmd) oneArgCmd = readCmd' (map toUpper cmd) in case noArgCmd of Just c -> return c Nothing -> case oneArgCmd of Just c -> do sign <- if cmd == "LIT" then neg else return 1 arg <- (pcParser "command argument") if cmd == "LIT" || cmd == "JMC" || cmd == "JMP" then return (c (sign*arg)) else return (c arg) Nothing -> unexpected (cmd ++ " is not a command.") } where neg = spaces >> (try (char '-' >> ((many1 space >> fail "spaces after '-', expected digit") <|> return (-1))) <|> return 1) readCmd cmd = case cmd of "ADD" -> Just ADD "MUL" -> Just MUL "SUB" -> Just SUB "DIV" -> Just DIV "MOD" -> Just MOD "LT" -> Just LT "EQ" -> Just EQ "NE" -> Just NE "GT" -> Just GT "LE" -> Just LE "GE" -> Just GE "NOP" -> Just NOP _ -> Nothing readCmd' cmd = case cmd of "READ" -> Just READ "WRITE" -> Just WRITE "LOAD" -> Just LOAD "STORE" -> Just STORE "LIT" -> Just LIT "JMP" -> Just JMP "JMC" -> Just JMC _ -> Nothing commentParser :: CharParser Int () commentParser = do { spaces >> try ((char '#') ""); -- (State s _ _) <- getParserState; -- trace ("Parser State comment1:\n" ++ s) (return ()); manyTill anyChar (lookAhead (char '\n')); -- (State s _ _) <- getParserState; -- trace ("Parser State comment2:\n" ++ s) (return ()); return (); } --- END PARSER printProg :: String -> Int -> [Command] -> IO() printProg fn cols cs = writeFile fn $ showProg cols cs showProg :: Int -> [Command] -> String showProg cols commands = let rows = ((length(commands)-1) `div` cols)+1 in trace ("maxrows = " ++ show rows) $ showProg' 0 0 (length commands) commands cols rows showProg' :: Int -> Int -> Int -> [Command] -> Int -> Int -> String showProg' row col clen c maxcol maxrow = let pc = (col * maxrow) + row in if pc >= clen then "\n" ++ (if row < maxrow-1 then showProg' (row+1) 0 clen c maxcol maxrow else "") else (if pc<9 then " " else "") ++ show (pc+1) ++ ": " ++ show (c!!pc) ++ ";" ++ (if col < maxcol-1 then "\t" ++ showProg' row (col+1) clen c maxcol maxrow else "\n" ++ (if row < maxrow-1 then showProg' (row+1) 0 clen c maxcol maxrow else "")) initial :: (Int,[Int],[(Int,Int)],[Int],[Int]) initial = (1,[],[],map inp [0..],[]) where inp x = (read (unsafePerformIO (putStr (if x>0 then "naechste Eingabe: " else "naechste Eingabe: ") >> hFlush stdout >> getLine)))::Int write :: Int -> Int write n = unsafePerformIO (putStr "naechste Ausgabe: " >> print n >> return n) main = do args <- getArgs case args of [] -> error "Bitte uebergeben Sie die auszufuehrende Datei als Argument (Bsp. ./amrun prog.am)" [fname] -> do parsed <- parseProgFromFile fname case parsed of Left err -> putStrLn $ show err Right prog -> do putStrLn ("Es laeuft das Programm:\n\n" ++ showProg 3 prog) seq (last (force (run prog initial))) (return ()) (_:_) -> error "Mehrere Argumente uebergeben. Uebergeben Sie nur die auszufuehrende Datei als Argument (Bsp. ./amrun prog.am)" where force [] = [] force (e@(_,_,hs,_,out):rest) = deepseq hs (deepseq out (e:force rest))