在Haskell中创建一个解释器

时间:2015-02-28 18:37:05

标签: haskell

所以,我需要编写一个函数evalS :: Statement -> Store -> Store,它将语句和存储作为输入,并返回一个可能已修改的存储。

以下案例已经发给我:

evalS w@(While e s1) s = case (evalE e s) of
                          (BoolVal True,s')  -> let s'' = evalS s1 s' in evalS w s''
                          (BoolVal False,s') -> s'
                          _                  -> error "Condition must be a BoolVal 

我需要写一下:

evalS Skip s             = ...
evalS (Expr e) s         = ...
evalS (Sequence s1 s2) s = ...
evalS (If e s1 s2) s     = ...

在If的情况下,如果e计算为非布尔值,我需要使用错误函数抛出错误。

示例输入/输出:

> run stmtParser "x=1+1" evalS
fromList [("x",2)]
> run stmtParser "x = 2; x = x + 3" evalS
fromList [("x",5)]
> run stmtParser "if true then x = 1 else x = 2 end" evalS
fromList [("x",1)]
> run stmtParser "x=2; y=x + 3; if y < 4 then z = true else z = false end" evalS
fromList [("x",2),("y",5),("z",false)]
> run stmtParser "x = 1; while x < 3 do x = x + 1 end" evalS
fromList [("x",3)]
> run stmtParser "x = 1 ; y = 1; while x < 5 do x = x + 1 ; y = y * x end" evalS
fromList [("x",5),("y",120)] 

stmtParser代码:

-- Sequence of statements
stmtParser :: Parser Statement
stmtParser = stmtParser1 `chainl1` (P.semi lexer >> return Sequence)

-- Single statements
stmtParser1 :: Parser Statement
stmtParser1 = (Expr <$> exprParser)
          <|> do
              P.reserved lexer "if"
              cond <- exprParser
              P.reserved lexer "then"
              the <- stmtParser
              P.reserved lexer "else"
              els <- stmtParser
              P.reserved lexer "end"
              return (If cond the els)
          <|> do
              P.reserved lexer "while"
              cond <- exprParser
              P.reserved lexer "do"
              body <- stmtParser
              P.reserved lexer "end"
              return (While cond body)

我做了什么:

我不确定是否需要在此问题中使用evalE。我把它写在以前的问题中。 evalE的签名是evalE :: Expression -> Store -> (Value, Store),请我写一下:

evalE (Var x) s = ...
evalE (Val v) s = ...
evalE (Assignment x e) s = ...

我已经完成了上述工作。

ATTEMPT:

evalS Skip s             = show s -- I am assuming that since Skip returns an empty String, I just need to return an empty String.
evalS (Sequence s1 s2) s = evalS s1 >> evalS s2 -- sequence1 then sequence2. I am not quite sure what to do with the s.
evalS (Expr e) s         = ... Not sure what to do, here.
evalS (If e s1 s2) s     = do
   x <- evalE e
   case x of
      BoolVal True -> evalS s1
      BoolVal False -> evalS s2

我在编写上述陈述时遇到了麻烦。

供参考,以下是给我使用的完整骨架:

-- Necessary imports
import Control.Applicative ((<$>),liftA,liftA2)
import Data.Map
import Text.Parsec
import Text.Parsec.Expr
import Text.Parsec.Language (emptyDef)
import Text.Parsec.String (Parser)
import qualified Text.Parsec.Token as P


--------- AST Nodes ---------

-- Variables are identified by their name as string
type Variable = String

-- Values are either integers or booleans
data Value = IntVal Int       -- Integer value
           | BoolVal Bool     -- Boolean value

-- Expressions are variables, literal values, unary and binary operations
data Expression = Var Variable                    -- e.g. x
                | Val Value                       -- e.g. 2
                | BinOp Op Expression Expression  -- e.g. x + 3
                | Assignment Variable Expression  -- e.g. x = 3

-- Statements are expressions, conditionals, while loops and sequences
data Statement = Expr Expression                   -- e.g. x = 23
               | If Expression Statement Statement -- if e then s1 else s2 end
               | While Expression Statement        -- while e do s end
               | Sequence Statement Statement      -- s1; s2
               | Skip                              -- no-op

-- All binary operations
data Op = Plus         --  +  :: Int -> Int -> Int
        | Minus        --  -  :: Int -> Int -> Int
        | Times        --  *  :: Int -> Int -> Int
        | GreaterThan  --  >  :: Int -> Int -> Bool
        | Equals       --  == :: Int -> Int -> Bool
        | LessThan     --  <  :: Int -> Int -> Bool

-- The `Store` is an associative map from `Variable` to `Value` representing the memory
type Store = Map Variable Value

--------- Parser ---------

-- The Lexer

lexer = P.makeTokenParser (emptyDef {
  P.identStart = letter,
  P.identLetter = alphaNum,
  P.reservedOpNames = ["+", "-", "*", "!", ">", "=", "==", "<"],
  P.reservedNames = ["true", "false", "if", "in", "then", "else", "while", "end", "to", "do", "for"]
})

-- The Parser

-- Number literals
numberParser :: Parser Value
numberParser = (IntVal . fromIntegral) <$> P.natural lexer

-- Boolean literals
boolParser :: Parser Value
boolParser =  (P.reserved lexer "true" >> return (BoolVal True))
          <|> (P.reserved lexer "false" >> return (BoolVal False))

-- Literals and Variables
valueParser :: Parser Expression
valueParser =  Val <$> (numberParser <|> boolParser)
           <|> Var <$> P.identifier lexer

-- -- Expressions
exprParser :: Parser Expression
exprParser = liftA2 Assignment
                    (try (P.identifier lexer >>= (\v ->
                          P.reservedOp lexer "=" >> return v)))
                    exprParser
          <|> buildExpressionParser table valueParser
    where table = [[Infix (op "*" (BinOp Times)) AssocLeft]
                  ,[Infix (op "+" (BinOp Plus)) AssocLeft]
                  ,[Infix (op "-" (BinOp Minus)) AssocLeft]
                  ,[Infix (op ">" (BinOp GreaterThan)) AssocLeft]
                  ,[Infix (op "==" (BinOp Equals)) AssocLeft]
                  ,[Infix (op "<" (BinOp LessThan)) AssocLeft]]
          op name node = (P.reservedOp lexer name) >> return node

-- Sequence of statements
stmtParser :: Parser Statement
stmtParser = stmtParser1 `chainl1` (P.semi lexer >> return Sequence)

-- Single statements
stmtParser1 :: Parser Statement
stmtParser1 = (Expr <$> exprParser)
          <|> do
              P.reserved lexer "if"
              cond <- exprParser
              P.reserved lexer "then"
              the <- stmtParser
              P.reserved lexer "else"
              els <- stmtParser
              P.reserved lexer "end"
              return (If cond the els)
          <|> do
              P.reserved lexer "while"
              cond <- exprParser
              P.reserved lexer "do"
              body <- stmtParser
              P.reserved lexer "end"
              return (While cond body)

-------- Helper functions --------

-- Lift primitive operations on IntVal and BoolVal values
liftIII :: (Int -> Int -> Int) -> Value -> Value -> Value
liftIII f (IntVal x) (IntVal y) = IntVal $ f x y
liftIIB :: (Int -> Int -> Bool) -> Value -> Value -> Value
liftIIB f (IntVal x) (IntVal y) = BoolVal $ f x y

-- Apply the correct primitive operator for the given Op value
applyOp :: Op -> Value -> Value -> Value
applyOp Plus        = liftIII (+)
applyOp Minus       = liftIII (-)
applyOp Times       = liftIII (*)
applyOp GreaterThan = liftIIB (>)
applyOp Equals      = liftIIB (==)
applyOp LessThan    = liftIIB (<)

-- Parse and print (pp) the given WHILE programs
pp :: String -> IO ()
pp input = case (parse stmtParser "" input) of
    Left err -> print err
    Right x  -> print x

-- Parse and run the given WHILE programs
run :: (Show v) => (Parser n) -> String -> (n -> Store -> v) -> IO ()
run parser input eval = case (parse parser "" input) of
    Left err -> print err
    Right x  -> print (eval x empty)

2 个答案:

答案 0 :(得分:6)

回答你的问题有点困难,因为你实际上并没有问过一个问题。让我先简单介绍一下你所说的一些内容,以便给你一些线索。

  

我不确定是否需要在此问题中使用evalE。我把它写在以前的问题中。 evalE的签名为evalE :: Expression -> Store -> (Value, Store)

     

evalS (Expr e) s = ... Not sure what to do, here.

执行包含表达式的语句意味着什么?如果它与评估表达式有关,那么你有一个表达式评估器的事实可能有助于“做什么,在这里”。

接下来,比较你为“while”提供的代码(顺便说一下,它包含一个与表达式有关的合理例子)......

evalS w@(While e s1) s = case (evalE e s) of`
  (BoolVal True,s')  -> let s'' = evalS s1 s' in evalS w s''
  (BoolVal False,s') -> s'
  _                  -> error "Condition must be a BoolVal"

...并将其与“if”

的代码进行比较
evalS (If e s1 s2) s     = do
   x <- evalE e
   case x of
      BoolVal True -> evalS s1
      BoolVal False -> evalS s2

你的代码风格迥异 - “monadic”风格。你从哪里得到的?如果评估者的类型类似于

,那将是有意义的
evalE :: Expression -> State Store Value
evalS :: Statement  -> State Store ()

monadic风格是一种非常好的方式,可以通过评估过程来处理变异商店,而不会过多地谈论它。例如,您的x <- evalE e表示“让x成为评估e(静静地接收初始商店并传递最终商店)的结果”。这是一种很好的工作方式,我希望你能在适当的时候进行探索。

但那些不是你给出的类型,而monadic风格并不合适。

evalE :: Expression -> Store -> (Value, Store)
evalS :: Statement  -> Store ->         Store

并且示例代码明确地对商店进行线程化。再看看

evalS w@(While e s1) s = case (evalE e s) of`
  (BoolVal True,s')  -> let s'' = evalS s1 s' in evalS w s''
  (BoolVal False,s') -> s'
  _                  -> error "Condition must be a BoolVal"

请参阅? evalS明确地接收其初始商店s,并在evalE e s中明确使用它。生成的新商店在s'个分支中都称为case。如果循环结束,则s'将作为最终存储返回。否则,s'被用作通过循环体s1的一次传递的存储,并且由此产生的存储s''用于下一次循环,{{1 }}

您的代码在命名和使用商店的每个评估阶段的方式都需要同样明确。让我们一起来看看。

w

你认为不正确。 evalS Skip s = show s -- I am assuming that since Skip returns an empty String, I just need to return an empty String. 函数不返回evalS,空或其他:它返回String。现在,哪Store?您的初始商店为Store:“跳过”后商店与s的关系如何?

s

同样,这是一种不符合这个问题的monadic方法。您需要通过按顺序评估语句evalS (Sequence s1 s2) s = evalS s1 >> evalS s2 -- sequence1 then sequence2. I am not quite sure what to do with the s. s的过程(最终s1)对商店进行线程化。 “while”案例就是如何做到这一点的好例子。

s2

同样,“while”示例显示了一种通过计算表达式来提取值和更新的商店的方法。值得深思的是,不是吗?

evalS (Expr e) s         = ... Not sure what to do, here.

现在“if”通过评估条件开始,而不是像“while”那样,不是吗?

所以,我的建议相当于:

  • 暂时删除monadic样式代码,但稍后在适当的时候再回来;
  • 阅读“while”的示例实现,并理解它如何处理语句的表达式和序列,明确地传递商店;
  • 部署类似的技术来实现其他构造。

设置问题的人非常友好地为您提供代码,其中提供了您需要的所有内容的示例。请通过理解并接受提示来回报这种善意!

答案 1 :(得分:0)

因为这看起来像是作业,所以我只提供一些小提示,为你留下真正的工作。

  

我不确定是否需要在此问题中使用evalE。

是的,你必须这样做。在您的语言中,表达式e会修改商店并返回一个值:您可以从evalE返回一对(Value,Store)来判断 相比之下,语句Expr e修改商店而不返回值。要从前者(表达式)获得后者(陈述评估),你需要做的就是扔掉你不需要的东西。

关于您的尝试:

evalS Skip s             = show s -- I am assuming that since Skip returns an empty String, I just need to return an empty String.

为什么是字符串? evalS会返回字符串吗?如果没有,它返回什么?在这里,你所做的工作远远超过你所做的工作。

evalS (Sequence s1 s2) s = evalS s1 >> evalS s2 -- sequence1 then sequence2. I am not quite sure what to do with the s.

好的,这个想法是对的,但代码不是。忘掉monad和>>,想想商店吧。您进行了两次递归调用evalS s1evalS s2:这些看起来不对,因为evalS需要两个参数(语句和存储),而您只提供一个。

并且 - 在你尝试之前 - 不,将s传递给他们两个仍然是错的。在哪个商店评估的第一个声明?第二个怎么样?

evalS (Expr e) s         = ... Not sure what to do, here.

见上面的讨论。

evalS (If e s1 s2) s     = do
   x <- evalE e
   case x of
      BoolVal True -> evalS s1
      BoolVal False -> evalS s2

避免与monad相关的操作do<-。可能有一种方法可以使用它们来解决这个问题,但是我不建议尝试那个初学者的路径。 如果要命名中间结果,可以使用let

evalE有两个参数,而不是一个。请注意,它返回一对,而不是一个值。 evalS有两个论点。