将AST转换为三个地址代码

时间:2014-05-20 20:13:21

标签: haskell abstract-syntax-tree

所以我必须为一个简单的语言构建一个简单的编译器,我使用Haskell的Alex和Happy来构建解析器,并且它已经打印了正确的AST,所以下一步我必须做是将该数据结构转换为另一个数据结构,代表三地址代码中的程序。

我对如何做到这一点有点迷失,因此,使用Haskell数据结构,我如何将AST转换为它的三个地址代码?真的很感激一些帮助:)

提前感谢!

1 个答案:

答案 0 :(得分:2)

你的解析器与这个问题完全无关 - 似乎是,如何将一个AST翻译成另一个。为了解决这个问题,我将使用简化的语言。此外,下面的代码并不简单,但易于扩展和维护。

{-# LANGUAGE DeriveFunctor #-}

import Control.Monad.State 
import Control.Monad.Reader 

import Control.Monad.Free 
import Data.Functor.Foldable 
import qualified Data.Set as S

data Ident = Ident Int deriving (Eq)

data ExpF a = IntLitF Int
           | PlusF a a
           | IntVarF Ident  deriving (Eq, Functor)

data Exp = IntLit Int
           | Plus Exp Exp
           | IntVar Ident  deriving (Eq)

data Cmd
  = CmdAtrib Ident Exp 
  | CmdSeq Cmd Cmd 
  | CmdNone deriving (Eq)

data TAC_F r = Assign Ident (ExpF Ident) r deriving (Eq, Show, Functor)

type TAC = Free TAC_F ()

(=:) :: Ident -> ExpF Ident -> TAC 
(=:) i e = Free (Assign i e (Pure ()))

上面的一些定义可能看起来很奇怪。 ExpFExp在递归方案样式中定义,并使用recursion-schemes包。 More info. TAC是根据Free定义的,因为顾名思义,您可以免费获得monad语法。 a >> bTAC执行的唯一操作是创建包含a后跟b的ast。

您需要一种方法来生成新变量:

freshVar :: Monad m => StateT [Ident] m Ident 
freshVar = do
  s <- get
  case s of 
    [] -> put [Ident (-1)] >> return (Ident (-1))
    (Ident x:xs) -> put (Ident (x-1) : xs) >> return (Ident (x-1))

我使用列表是因为它很简单,但您可能希望在标识符中附加更多信息,在这种情况下,您应该使用Data.Set.SetData.Map.Map。按照惯例,新变量是负数,而量化变量是正数。不是一个非常复杂的方法,但它的工作原理。

现在这就是魔术发生的地方。由于递归方案,树上的递归非常简单:

translateExp :: Exp -> State [Ident] (TAC, Ident)
translateExp = cata go where 
  go (PlusF a b) = do 
    (ae,av) <- a
    (be,bv) <- b
    t <- freshVar
    return (ae >> be >> t =: PlusF av bv, t)
  go (IntLitF i) = do 
    t <- freshVar 
    return (t =: IntLitF i, t)
  go (IntVarF a) = return (return (), a)

translateCmd :: Cmd -> State [Ident] TAC
translateCmd (CmdAtrib ident exp) = do
  (e,v) <- translateExp exp 
  return (e >> ident =: IntVarF v)
translateCmd (CmdSeq a b) = do
  x <- translateCmd a 
  y <- translateCmd b 
  return (x >> y)
translateCmd CmdNone = return (return ())

然后是一个例子:

test0 = CmdSeq (CmdAtrib (Ident 1) (IntLit 10 `Plus` IntVar (Ident 2)))
               (CmdAtrib (Ident 3) (IntVar (Ident 1) `Plus` IntVar (Ident 1) `Plus` IntVar (Ident 2)))

>putStrLn $ showTAC $ fst $ runState (translateCmd test0) []
t1 =: 10
t2 =: t1 + v2
v1 =: t2
t3 =: v1 + v1
t4 =: t3 + v2
v3 =: t4

请注意,由CmdAtrib的LHS约束的变量绝不会与RHS中发现的变量发生碰撞。


Boilerplate / show instances:

instance Show Ident where 
  show (Ident i) | i < 0 = "t" ++ show (abs i)
                 | otherwise = "v" ++ show i

instance Show a => Show (ExpF a) where 
  show (IntLitF i) = show i
  show (PlusF a b) = show a ++ " + " ++ show b 
  show (IntVarF i) = show i 

type instance Base Exp = ExpF  
instance Foldable Exp where 
  project (IntLit i) = IntLitF i
  project (Plus a b) = PlusF a b
  project (IntVar b) = IntVarF b

instance Show Cmd where 
  show (CmdAtrib i e) = show i ++ " <- " ++ show e 
  show (CmdSeq a b) = show a ++ " ;\n " ++ show b  
  show (CmdNone) = ""

instance Show Exp where 
  show (IntLit i) = show i
  show (Plus a b) = show a ++ " + " ++ show b 
  show (IntVar i) = show i 

showTAC (Free (Assign i exp xs)) = show i ++ " =: " ++ show exp ++ "\n" ++ showTAC xs 
showTAC (Pure a) = ""