带注释的递归数据类型,在AST中具有不同类型的注释

时间:2013-09-12 21:56:05

标签: haskell compiler-construction abstract-syntax-tree generic-programming

在我正在编写的玩具编译器中,我希望使用通用递归数据类型来表示抽象语法树(AST),可能使用某些属性进行注释。

解析器为表达式构建AST,并在源代码中用位置注释。

语义分析器使用带有位置的AST进行注释,并生成一个monad,在运行时,返回一个带有类型信息注释的相应AST。

语义分析器的目的是键入检查表达式,报告在进程中发现的任何错误。计算出的表达式类型应该用作原始树中的注释,以便最后每个树节点都将使用其类型进行注释。

在完全实现语义分析器的情况下,将使用RWS monad,因为它需要一个用于编译let表达式和变量的环境,将生成找到的错误的日志,并且表达式语言中的一些新结构将需要状态待编译。

在尝试编写语义分析器时,我遇到了Haskell类型系统的问题。以下代码演示了我遇到的问题:

{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveTraversable #-}

module Lang0 where

import Prelude hiding (foldr1,mapM,exp)
import Data.Foldable (Foldable)
import Data.Traversable (Traversable)
import Control.Monad.RWS (runRWS)

newtype Fix f = In { out :: f (Fix f) }

deriving instance Show (f (Fix f)) => Show (Fix f)


data Ann x f a = Ann { attr  :: x    -- ^ the annotation
                     , unAnn :: f a  -- ^ the original functor
                     }
                 deriving (Eq,Ord,Show,Functor,Foldable,Traversable)

data Range = Range Int Int

instance Show Range where
  show (Range a b) = show a ++ "-" ++ show b

type Name = String

data BinOp
  = Add | Sub | Mul | Div
  | Eq | Ne | Gt | Ge | Lt | Le
  | Con | Dis
  deriving (Eq,Show)

data ExpF r
  = Log Bool
  | Num Double
  | Var Name
  | Neg r
  | Bin BinOp r r
  | Let Name r r
  deriving (Eq,Show,Functor,Foldable,Traversable)

data Type = NUMERIC | LOGIC deriving (Eq,Show)

newtype Exp = Exp { runExp :: Fix ExpF } deriving (Show)
newtype ExpPos = ExpPos { runExpPos :: Fix (Ann Range ExpF) } deriving (Show)
newtype ExpType = ExpType { runExpType :: Fix (Ann ExpType ExpF) } deriving (Show)

type Env = [(Name, Type)]
type Log = [(Range, String)]

semantExp :: Monad m => Fix (Ann Range ExpF) -> m (Fix (Ann Type ExpF))
semantExp (In (Ann pos exp)) =
  case exp of
    Num _ -> return (In (Ann NUMERIC exp))
    _     -> error "unimplemented"

e1 :: ExpPos
e1 = ExpPos (In (Ann (Range 1 2) (Num 8)))

main :: IO ()
main = print (runRWS (semantExp (runExpPos e1)) [] ())

将此代码提供给ghc编译器时,我得到以下内容:

$ ghc --make Lang0
[1 of 1] Compiling Lang0            ( Lang0.hs, Lang0.o )

Lang0.hs:60:38:
    Couldn't match type `Range' with `Type'
    Expected type: ExpF (Fix (Ann Type ExpF))
      Actual type: ExpF (Fix (Ann Range ExpF))
    In the second argument of `Ann', namely `exp'
    In the first argument of `In', namely `(Ann NUMERIC exp)'
    In the first argument of `return', namely `(In (Ann NUMERIC exp))'

为什么编译器希望参数和结果中的注释具有相同的类型?

有关如何解决此问题的任何线索?

其他观察:如果没有semantExp的类型注释,编译器会为其推断出以下类型:

semantExp
  :: Monad m => Fix (Ann Type ExpF) -> m (Fix (Ann Type ExpF))

为什么推断类型的注释类型与结果monad中的参数都相同?

1 个答案:

答案 0 :(得分:4)

所以你有这个大的旧术语,注释范围无处不在。你破坏它,它给你一个名为pos的范围注释和一个名为exp的子项(仍在整个地方注释范围)。你抛出pos并用一个类型注释替换它,这是值得称赞的,但后来有勇气宣称exp是用类型注释的。但这显然不是真的 - 你必须遍历整个子项并删除所有旧注释!

这个错误也说明了这一点,但它的语言又枯燥无味。