我正在尝试为一种仅支持算术运算和变量赋值(通过 x: ...
)的简单语言编写解释器。解析器工作正常,但我在评估 AST 时遇到了概念上的困难。这是我的语法:
module Grammar where
import Data.Text (Text)
data Stmt = Sequence [Stmt]
| ArithmeticExpr ArithmeticExpr
| AssignmentExpr AssignmentExpr
deriving (Eq, Ord, Show)
data ArithmeticExpr = Constant Integer
| Variable Text
| Negation ArithmeticExpr
| Sum ArithmeticExpr ArithmeticExpr
| Subtraction ArithmeticExpr ArithmeticExpr
| Product ArithmeticExpr ArithmeticExpr
| Division ArithmeticExpr ArithmeticExpr
| Exponentiation ArithmeticExpr ArithmeticExpr
deriving (Eq, Ord, Show)
data AssignmentExpr = Assignment Text ArithmeticExpr
deriving (Eq, Ord, Show)
我的评估代码如下所示:
module Eval where
import Control.Monad.Trans.State.Lazy (State, modify, get)
import Data.Text (Text)
import Data.Void (Void)
import Grammar
type Eval = State [(Text, Integer)]
eval :: Stmt -> Eval Void
eval (Sequence stmts) = undefined -- TODO
eval (ArithmeticExpr expr) = undefined -- TODO
eval (AssignmentExpr expr) = undefined -- TODO
evalArithmeticExpr :: ArithmeticExpr -> Eval Integer
evalArithmeticExpr expr = case expr of
Constant n -> return n
Variable v -> do
env <- get
case lookup v env of
Just n -> return n
Nothing -> error $ "variable '" ++ show v ++ "' not defined"
Negation e -> negate <$> evalArithmeticExpr e
Sum e1 e2 -> (+) <$> evalArithmeticExpr e1 <*> evalArithmeticExpr e2
Subtraction e1 e2 -> (-) <$> evalArithmeticExpr e1 <*> evalArithmeticExpr e2
Product e1 e2 -> (*) <$> evalArithmeticExpr e1 <*> evalArithmeticExpr e2
Division e1 e2 -> div <$> evalArithmeticExpr e1 <*> evalArithmeticExpr e2
Exponentiation e1 e2 -> (^) <$> evalArithmeticExpr e1 <*> evalArithmeticExpr e2
evalAssignmentExpr :: AssignmentExpr -> Eval ()
evalAssignmentExpr (Assignment var expr) = undefined -- TODO
这里我不知道如何实现 evalAssignmentExpr
以便相应地更新变量映射(通过 State
实现)。我知道我应该使用 modify
,但我不太明白如何使用,因为此时我实际上没有分配给 var
的值,只有另一个表达式。
答案 0 :(得分:1)
如评论中所述,您想要实现 evalAssignmentExpr
的方式是评估当前状态中的表达式以获得一个值,然后修改状态以反映使用该值的新变量分配。它看起来像:
evalAssignmentExpr :: AssignmentExpr -> Eval ()
evalAssignmentExpr (Assignment var expr) = do
val <- evalArithmeticExpr expr
modify ((var, val):)
一些剧透如下...
另外,你会发现你想要:
eval :: Stmt -> Eval ()
用 ()
代替 Void
。此外,ArithmeticExpr
的 Stmt
构造函数和相关案例:
eval (ArithmeticExpr expr) = evalArithmeticExpr expr
真的没有意义。语句的求值不能返回任何内容,因此算术表达式作为语句不会做任何事情。 (因为表达式在您的语言中没有副作用,所以您对算术表达式语句所能做的就是评估它并丢弃结果。)
要评估语句列表,您需要执行一元映射以按顺序评估语句:
eval (Sequence stmts) = mapM_ eval stmts
一个可运行的版本:
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad.Trans.State.Lazy
import Data.Text
import Data.Void
data Stmt = Sequence [Stmt]
| AssignmentExpr AssignmentExpr
deriving (Eq, Ord, Show)
data ArithmeticExpr = Constant Integer
| Variable Text
| Negation ArithmeticExpr
| Sum ArithmeticExpr ArithmeticExpr
| Subtraction ArithmeticExpr ArithmeticExpr
| Product ArithmeticExpr ArithmeticExpr
| Division ArithmeticExpr ArithmeticExpr
| Exponentiation ArithmeticExpr ArithmeticExpr
deriving (Eq, Ord, Show)
data AssignmentExpr = Assignment Text ArithmeticExpr
deriving (Eq, Ord, Show)
type Eval = State [(Text, Integer)]
eval :: Stmt -> Eval ()
eval (Sequence stmts) = mapM_ eval stmts
eval (AssignmentExpr expr) = evalAssignmentExpr expr
evalArithmeticExpr :: ArithmeticExpr -> Eval Integer
evalArithmeticExpr expr = case expr of
Constant n -> return n
Variable v -> do
env <- get
case lookup v env of
Just n -> return n
Nothing -> error $ "variable '" ++ show v ++ "' not defined"
Negation e -> negate <$> evalArithmeticExpr e
Sum e1 e2 -> (+) <$> evalArithmeticExpr e1 <*> evalArithmeticExpr e2
Subtraction e1 e2 -> (-) <$> evalArithmeticExpr e1 <*> evalArithmeticExpr e2
Product e1 e2 -> (*) <$> evalArithmeticExpr e1 <*> evalArithmeticExpr e2
Division e1 e2 -> div <$> evalArithmeticExpr e1 <*> evalArithmeticExpr e2
Exponentiation e1 e2 -> (^) <$> evalArithmeticExpr e1 <*> evalArithmeticExpr e2
evalAssignmentExpr :: AssignmentExpr -> Eval ()
evalAssignmentExpr (Assignment var expr) = do
val <- evalArithmeticExpr expr
modify ((var, val):)
main = do
let pgm = Sequence [ AssignmentExpr (Assignment "x" (Constant 5))
, AssignmentExpr (Assignment "y" (Product (Variable "x") (Variable "x")))
]
print $ runState (eval pgm) []