评估 AST 中的变量赋值

时间:2020-12-29 22:12:59

标签: haskell

我正在尝试为一种仅支持算术运算和变量赋值(通过 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 的值,只有另一个表达式。

1 个答案:

答案 0 :(得分:1)

如评论中所述,您想要实现 evalAssignmentExpr 的方式是评估当前状态中的表达式以获得一个值,然后修改状态以反映使用该值的新变量分配。它看起来像:

evalAssignmentExpr :: AssignmentExpr -> Eval ()
evalAssignmentExpr (Assignment var expr) = do
  val <- evalArithmeticExpr expr
  modify ((var, val):)

一些剧透如下...

另外,你会发现你想要:

eval :: Stmt -> Eval ()

() 代替 Void。此外,ArithmeticExprStmt 构造函数和相关案例:

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) []