在我正在编写的玩具编译器中,我希望使用通用递归数据类型来表示抽象语法树(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中的参数都相同?
答案 0 :(得分:4)
所以你有这个大的旧术语,注释范围无处不在。你破坏它,它给你一个名为pos
的范围注释和一个名为exp
的子项(仍在整个地方注释范围)。你抛出pos
并用一个类型注释替换它,这是值得称赞的,但后来有勇气宣称exp
是用类型注释的。但这显然不是真的 - 你必须遍历整个子项并删除所有旧注释!
这个错误也说明了这一点,但它的语言又枯燥无味。