packages feed

amrun-0.0.0.7: src/amrun.hs

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 d2<d1 then 1 else 0):d',h,inp,out)
step EQ (m,d1:d2:d',h,inp,out) =
   (m+1,(if d2==d1 then 1 else 0):d',h,inp,out)
step NE (m,d1:d2:d',h,inp,out) =
   (m+1,(if d2/=d1 then 1 else 0):d',h,inp,out)
step GT (m,d1:d2:d',h,inp,out) =
   (m+1,(if d2>d1 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
                     | a<n  = (a,b):(update h n c)
                     | a>n  = (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))