There was more than "some" bugs, and a lack of strictness that led to
a stack overflow for high values of x... So here is a better version
(not quite there still, but better).

--
Jedaï
{-# OPTIONS -fbang-patterns -funbox-strict-fields #-}
module Minim (Statement (..), Test (..), Program (..), Expr (..), eval) where 
import qualified Data.Map as M
import Data.Char

data Statement = 
    Assign String Expr
    | Inc String
    | Dec String
    | Cond Test Statement Statement
    | Goto String
    | Print Expr
    | Nl
    | Input String
    deriving (Show)

data Test = 
    Le Expr Expr
    | Eq Expr Expr
    | And Test Test
    | Or Test Test
    | Not Test
    deriving (Show)

data Expr =
    Str String
    | Number !Int
    | EVar String
    deriving (Eq, Ord)

instance Show Expr where
    show (Str s) = s
    show (Number i) = show i
    show (EVar s) = "Variable : " ++ s

newtype Program = Program ([Statement],[(String,[Statement])])
    deriving (Show)
eval :: Program -> IO ()
eval (Program (xs, tags)) =
    evalS xs tags M.empty

evalS :: [Statement] -> [(String, [Statement])] -> M.Map String Expr -> IO ()
evalS (s0:ss) tags !context =  
    case s0 of
      Assign str expr -> evalS ss tags 
                         $ M.insert str (evalE expr context) context
      Inc str -> evalS ss tags $ M.insertWith' inc_expr str undefined context
            where
              inc_expr _ !(Number i) = Number $ i + 1
              inc_expr _ _ = error $ "You can't increment "
                           ++ str ++ ", it isn't numeric.\n" 
      Dec str -> evalS ss tags $ M.insertWith' dec_expr str undefined context
            where
              dec_expr _ !(Number i) = Number $ i - 1
              dec_expr _ _ = error $ "You can't increment "
                             ++ str ++ ", it isn't numeric.\n"
      Cond test s1 s2 -> if evalT test context
                         then evalS (s1:ss) tags context
                         else evalS (s2:ss) tags context
      Goto str -> maybe 
                  (error $ "No such tag : " ++ str)
                  (\nss -> evalS nss tags context)
                  $ lookup str tags
      Print expr -> do putStr (show $ evalE expr context)
                       evalS ss tags context
      Nl -> do putStrLn ""
               evalS ss tags context
      Input str -> do input <- getLine
                      let expr = if (not $ null input) && all isDigit input
                                 then Number $ read input
                                 else Str input
                      evalS ss tags $ M.insert str expr context
evalS [] _ _ = return ()

evalE :: Expr -> M.Map String Expr -> Expr
evalE (EVar str) context = maybe 
                           (error $ "There's no such variable : " ++ str) 
                           id $ M.lookup str context
evalE e _ = e

evalT :: Test -> M.Map String Expr -> Bool
evalT t context = case t of
                    Eq e1 e2 -> evalE e1 context == evalE e2 context
                    Le e1 e2 -> evalE e1 context < evalE e2 context
                    And t1 t2 -> evalT t1 context && evalT t2 context
                    Or t1 t2 -> evalT t1 context || evalT t2 context
                    Not t1 -> not $ evalT t1 context
module MinimParser (parseFile) where
import Minim
import Text.ParserCombinators.Parsec hiding (spaces, parseTest, token)
import Text.ParserCombinators.Parsec.Expr
import Text.ParserCombinators.Parsec.Token hiding (symbol)
import Control.Monad

sp :: Parser ()
sp = skipMany $ char ' '

spaces :: Parser ()
spaces = skipMany1 $ char ' '

token :: Parser a -> Parser ()
token p = spaces >> p >> spaces

symbol :: Parser String
symbol = many1 letter

litVar :: Parser Expr
litVar = liftM EVar symbol

litString :: Parser Expr
litString = liftM Str $ between (char '"') (char '"') $ many (noneOf "\"")

litNumber :: Parser Expr
litNumber = return . Number . read =<< many digit

parseExpr :: Parser Expr
parseExpr = litVar <|> litString <|> litNumber

opTable = [ [infixOp "and" And AssocNone, infixOp "or" Or AssocNone],
            [Prefix (string "not" >> spaces >> return Not)] ]

infixOp name op assoc = 
    Infix (try $ token (string name) >> return op) assoc

parseTest :: Parser Test
parseTest = buildExpressionParser opTable simpleTest

simpleTest :: Parser Test
simpleTest =
    between (char '(' >> sp) (sp >> char ')') parseTest <|>
    do e1 <- parseExpr
       op <- between sp sp $ oneOf "=<>"
       e2 <- parseExpr
       return $ case op of
                  '=' -> Eq e1 e2
                  '<' -> Le e1 e2
                  '>' -> Le e2 e1

printS :: Parser Statement
printS = liftM Print $ string "print" >> spaces >> parseExpr

inputS :: Parser Statement
inputS = liftM Input $ string "input" >> spaces >> symbol

assignS :: Parser Statement
assignS = do
  var <- symbol
  token $ string "is"
  expr <- parseExpr
  return $ Assign var expr

gotoS :: Parser Statement
gotoS = liftM Goto $ string "goto" >> spaces >> symbol

incS :: Parser Statement
incS = liftM Inc $ string "++" >> sp >> symbol

decS :: Parser Statement
decS = liftM Dec $ string "--" >> sp >> symbol

condS :: Parser Statement
condS = do
  string "if" >> spaces
  test <- parseTest
  token $ string "then"
  s1 <- parseStatement
  token $ string "else"
  s2 <- parseStatement
  return $ Cond test s1 s2

nlS :: Parser Statement
nlS = string "nl" >> return Nl

parseStatement :: Parser Statement
parseStatement =
    try (incS <|> decS <|> printS <|> try condS <|> inputS <|> gotoS <|> nlS) 
            <|> assignS

comments :: Parser ()
comments = char '#' >> skipMany (noneOf "\n")

parseProgram :: Parser Program
parseProgram =
    skipMany ((sp <|> comments) >> newline) >>
    (try (do
          stat <- parseStatement
          sp >> newline
          program <- parseProgram
          case program of
            Program (stats, tags) -> return $ Program (stat:stats, tags)
        ) <|>
     (do tag <- symbol
         newline
         program <- parseProgram
         case program of
           Program (stats, tags) -> return $ Program (stats, (tag,stats):tags)
     ) <|> (eof >> ( return $ Program ([], []) ))
    )

parseFile          :: String -> IO Program
parseFile fileName = do 
  input <- readFile fileName
  case (parse parseProgram fileName input) of
    Left err -> error $ show err
    Right p -> return p
module Main where
import MinimParser
import Minim (eval)
import System
import System.IO

main :: IO ()
main = do
  hSetBuffering stdout NoBuffering
  arg <- getArgs
  program <- parseFile $ arg!!0
  eval program
_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to