所以我必须为一个简单的语言构建一个简单的编译器,我使用Haskell的Alex和Happy来构建解析器,并且它已经打印了正确的AST,所以下一步我必须做是将该数据结构转换为另一个数据结构,代表三地址代码中的程序。
我对如何做到这一点有点迷失,因此,使用Haskell数据结构,我如何将AST转换为它的三个地址代码?真的很感激一些帮助:)
提前感谢!
答案 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 ()))
上面的一些定义可能看起来很奇怪。 ExpF
和Exp
在递归方案样式中定义,并使用recursion-schemes
包。 More info. TAC
是根据Free
定义的,因为顾名思义,您可以免费获得monad语法。 a >> b
为TAC
执行的唯一操作是创建包含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.Set
或Data.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) = ""