我开始学习Alex并且相信我已经达到有状态背景会有所帮助的程度,但我并不完全确定如何去做。我试图使用erlang binaries的有限子集。使用以下词法分析器:
{
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]
,但是我找不到一个词法分析器,它使用了点击我头脑的开始代码。
有人会如此善良地指导我一点吗?
答案 0 :(得分:3)
我不确定你要用词法分析器做什么,并且知识足以指导你这个(但如果你需要的只是过滤无用的令牌,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是行动的概括,就像群体的行动一样,自然适合编程环境。) 为此,请参阅this list of papers about monads and arrows,其中包括具有一定技术背景的人员的介绍性文章和教程。
顺便说一句,我刚刚开始学习Erlang。你能好好指导一下吗?有没有缺乏静态打字咬你?您是否尝试过cloud-haskell并将其与Erlang进行比较?在分布式编程的上下文中,您觉得哪种语言最有效?