8

我开始学习Alex并相信我已经到了有状态上下文会有所帮助的地步,但我不完全确定如何去做。我正在尝试对erlang binaries的有限子集进行 lex 。使用以下词法分析器:

{
module Main (main, Token(..), AlexPosn(..), alexScanTokens, token_posn) where
}

%wrapper "posn"

$digit = 0-9      -- digits                                                                            
$alpha = [a-zA-Z] -- alphabetic characters                                                             
$dbl_quote = \"

tokens :-

  $white+                        ;
  ","                            { tok (\p s -> Comma p) }
  "<<"                           { tok (\p s -> BinaryOpen p) }
  ">>"                           { tok (\p s -> BinaryClose p) }
  $dbl_quote [^$dbl_quote]* $dbl_quote { tok (\p s -> ErlStr p (init (tail s))) }
  $digit+                        { tok (\p s -> ErlInt p (read s)) }

{
-- action helpers:                                                                                     
tok :: (AlexPosn -> String -> Token) -> AlexPosn -> String -> Token
tok f p s = f p s

data Token =
  Comma       AlexPosn |
  BinaryOpen  AlexPosn |
  BinaryClose AlexPosn |
  ErlInt   AlexPosn Integer |
  ErlStr   AlexPosn String
  deriving (Eq, Show)

token_posn :: Token -> AlexPosn
token_posn (Comma    p) = p
token_posn (BinaryOpen  p) = p
token_posn (BinaryClose p) = p
token_posn (ErlInt   p _) = p
token_posn (ErlStr   p _) = p

main :: IO ()
main = do
  s <- getContents
  print (alexScanTokens s)
}

我做得很好。例如,

> alex so_erlang_lexer.x  && ghc --make -o erlexer so_erlang_lexer.hs && echo '<<"100", 1>>' | ./erlexer 
[1 of 1] Compiling Main             ( so_erlang_lexer.hs, so_erlang_lexer.o )
Linking erlexer ...
[BinaryOpen (AlexPn 0 1 1),ErlStr (AlexPn 2 1 3) "100",Comma (AlexPn 7 1 8),ErlInt (AlexPn 9 1 10) 1,BinaryClose (AlexPn 10 1 11)]

我希望lexed返回等效于Binary [ErlStr "100", ErlInt 1],但我无法找到一个使用在我脑海中点击的起始代码的词法分析器。

  • 此处引用的 GHC 词法分析器不使用任何 Alex 包装器。
  • Alex 自己的关于其 monad 和 monadUserState 包装器的文档我不确定应该选择哪个以及如何使用它们。
  • 亚历克斯关于老虎的例子是最有希望的,但它太大了,我很难澄清我的无知。
  • 这个问题使用了 monad 解析器,但似乎没有使用它的状态特性。

有人会好心指导我吗?

4

1 回答 1

4

我不完全确定你想用词法分析器做什么,并且知识渊博可以指导你(但如果你只需要过滤无用的标记,alex 的 monadic 接口似乎有点矫枉过正),无论如何这里有一个示例代码用于AlexUserState积累带有“monadUserState”包装器的选定标记。



{
module Main (main) where
}

%wrapper "monadUserState"

$digit = 0-9      -- digits                                                                            
$alpha = [a-zA-Z] -- alphabetic characters                                                             
$dbl_quote = \"

tokens :-

  $white+                              ;
  ","                                  { ignoreToken  }
  ">"                                 { ignoreToken }
  $dbl_quote [^$dbl_quote]* $dbl_quote { pushToken  $ ErlStr . init . tail  }
  $digit+                              { pushToken  $ ErlInt . read }

{

alexEOF :: Alex ()
alexEOF = return ()

-- some useful interaces to the Alex monad (which is naturally an instance of state monad)
modifyUserState :: (AlexUserState -> AlexUserState) -> Alex ()
modifyUserState f = Alex (\s -> let st = alex_ust s in Right (s {alex_ust = f st},()))

getUserState ::  Alex AlexUserState
getUserState = Alex (\s -> Right (s,alex_ust s))

-- Token definition minus position information for simplicity
data Token =
  Comma        |
  BinaryOpen   |
  BinaryClose  |
  ErlInt    Integer |
  ErlStr    String
  deriving (Eq, Show)

newtype AlexUserState = Binary [Token]
  deriving (Eq, Show)

alexInitUserState :: AlexUserState
alexInitUserState = Binary []


-- action helpers:                                                                                     
pushToken :: (String -> Token) -> AlexAction ()
pushToken tokenizer = 
  \(posn,prevChar,pending,s) len -> modifyUserState (push $ take len s) >> alexMonadScan
    where
       -- Here tokens are accumulated in reverse order for efficiency.
       -- You need a more powerful data structure like Data.Sequence to preserve the order.
       push :: String -> AlexUserState -> AlexUserState
       push s (Binary ts) = Binary (tokenizer s : ts) 

ignoreToken :: AlexAction ()
ignoreToken _ _   = alexMonadScan


runAlexScan :: String -> Either String AlexUserState
runAlexScan s = runAlex s $ alexMonadScan >> getUserState


main :: IO ()
main = getContents >>= print . runAlexScan

}

但我想主要问题是你似乎对 Haskell 中 monad 的概念和用法还不够熟悉。Alex 的 monadic 接口实际上是非常自然且典型的状态 monad,一旦您有一些编码经验,您只需浏览生成的代码就很容易猜到。(如果你猜错了,类型检查器很可能会发现相关错误。)

为此,由于这里似乎有很多关于 monad 的好问题和答案,所以我只参考Real World Haskell(其关于分析的章节对我特别有帮助。)

但是,如果你碰巧已经对范畴论有所了解,那么学习 monad 的最快方法可能是直接阅读一些相关论文(请记住,范畴论中的 monad 是对动作的概括,就像在组的动作中一样,它自然适合编程环境。)为此,请参阅 有关 monads 和 arrows 的论文列表,其中包括为具有一定技术背景的人提供的介绍性论文和教程。

顺便说一句,我刚刚开始学习 Erlang。你能指导我一些吗?没有静态类型的缺乏让你感到痛苦吗?您是否尝试过 cloud-haskell 并将其与 Erlang 进行比较?在分布式编程的背景下,您觉得哪种语言效率最高?

于 2012-10-06T09:17:21.903 回答