我对Haskell相对较新,并且使用它来编写编译器,使用Alex和Happy。我写的语言是空格敏感的:块是由缩进分隔的,并且通过增加两个缩进级别继续行。
虽然我已经使用Alex的monadUserState包装器走了这条路,但是作为一个不熟悉monad的人我发现这种方法非常混乱。因此,我在思考而不是将lexing过程分为两个阶段,一个是扫描令牌,另一个是用缩进,持续和持续令牌替换原始空白(这种方法可能性能较差,但我和#39 ; m在这个阶段并不十分关心表现。)
描述词汇分析两个阶段的GADT如下所示:
{-# Language DataKinds, GADTs, KindSignatures #-}
module Tokens where
data Phase = P0 | Pn
data Token (p :: Phase) where
-- Phase 0
TokenTabs :: Int -> Token 'P0
-- Phase n
TokenIndent :: Token 'Pn
TokenDedent :: Token 'Pn
TokenLineCont :: Token 'Pn
-- Common tokens
TokenIf :: Token p
-- ...
-- Many other data constructors with result Token p, that can appear in either stage
-- ...
TokenName :: String -> Token p
执行第二个lexing阶段的函数如下所示:
postLex :: [Token 'P0] -> [Token 'Pn]
postLex tokens = postLex' 0 tokens where
postLex' :: Int -> [Token 'P0] -> [Token 'Pn]
postLex' depth [] = replicate depth TokenDedent
postLex' depth ((TokenTabs n) : rest)
| n == depth = postLex' depth rest
| n == (depth + 1) = TokenIndent : postLex' n rest
| n >= (depth + 2) = TokenLineCont : postLex' depth rest
| n < depth = replicate (depth - n) TokenDedent ++ postLex' n rest
postLex' depth (t:rest) = t : postLex' depth rest
我遇到的问题是,在匹配标签令牌后,所有剩余的令牌都可以存在于任一阶段,但我不知道使用签名TokenX转换数据构造函数的结果的任何好方法:: Token p从Token P0到Token Pn。
有没有一种很好的方式来执行这样的转换?如果没有,是否有任何替代方法可以保护类型安全?我非常感兴趣,因为我想在这个项目的后期转换AST的各个不同阶段时,类似的方法会很有用。