Parsing 如何有效地使用Alex的开始代码功能?

Parsing 如何有效地使用Alex的开始代码功能?,parsing,haskell,Parsing,Haskell,我开始学习并相信我已经达到了有状态上下文会有所帮助的程度,但我不完全确定如何去做。我正试图将一个有限的子集lex。使用以下lexer: { module Main (main, Token(..), AlexPosn(..), alexScanTokens, token_posn) where } %wrapper "posn" $digit = 0-9 -- digits

我开始学习并相信我已经达到了有状态上下文会有所帮助的程度,但我不完全确定如何去做。我正试图将一个有限的子集lex。使用以下lexer:

{
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)
}
{
模块Main(Main,Token(..),AlexPosn(..),alexScanTokens,Token_posn),其中
}
%包装器“posn”
$digit=0-9——数字
$alpha=[a-zA-Z]--字母字符
$dbl\u报价=\”
代币:-
$white+;
“,”{tok(\ps->逗号p)}
“>”{tok(\p s->BinaryClose p)}
$dbl_quote[^$dbl_quote]*$dbl_quote{tok(\p s->erlstrp(init(tail s))}
$digit+{tok(\p s->ErlInt p(read s))}
{
--行动助手:
tok::(AlexPosn->String->Token)->AlexPosn->String->Token
tok f p s=f p s
数据令牌=
逗号AlexPosn|
二进制开放AlexPosn|
二进制关闭AlexPosn|
ErlInt-AlexPosn整数|
ErlStr AlexPosn字符串
推导(等式,显示)
token\u posn::token->AlexPosn
标记(逗号p)=p
令牌\u posn(二进制开放p)=p
令牌\u posn(二进制关闭p)=p
令牌posn(ErlInt p)=p
令牌posn(erlstrp)=p
main::IO()
main=do
s alex so_erlang_lexer.x&&ghc--make-o erlexer so_erlang_lexer.hs&&echo“”。/erlexer
[1/1]编译Main(so_erlang_lexer.hs,so_erlang_lexer.o)
链接erlexer。。。
[BinaryOpen(AlexPn 0 1 1)、ErlStr(AlexPn 2 1 3)“100”、逗号(AlexPn 7 1 8)、ErlInt(AlexPn 9 1 10)1、BinaryClose(AlexPn 10 1 11)]
我更希望lexed return等价于
二进制[ErlStr“100”,ErlInt 1]
,但我还没有找到一个lexer,它使用在我脑海中点击的开始代码

  • GHC的lexer不使用任何Alex包装
  • Alex自己关于monad和monadUserState包装的文档让我无法确定应该选择哪种包装,以及如何使用这两种包装
  • 亚历克斯的例子是最有希望的,但它太大了,我很难澄清我的无知
  • 这使用了monad解析器,但似乎没有使用它的状态特性

有人能给我一点指导吗?

我不太清楚你想用lexers做什么,并且有足够的知识来指导你(但是如果你所需要的只是过滤无用的代币,alex的一元界面似乎有点过头了)总之,这里有一个示例代码,可以使用
AlexUserState
使用“monadUserState”包装器累积所选令牌

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

对于这一点,因为这里似乎有很多关于单子的好问题和答案,所以我只提到(关于分析的章节对我特别有帮助)

但是如果你碰巧已经有了一些范畴理论的知识,那么学习单子的最快方法可能是直接阅读一些相关的论文(请记住,范畴理论中的单子是动作的一种概括,就像组的动作一样,它自然适合编程环境。) 关于这一点,请参阅,其中包括一些技术背景人员的介绍性论文和教程


顺便说一句,我刚刚开始学习Erlang。你能给我一点指导吗?缺乏静态输入不是让你感到痛苦吗?你有没有尝试过cloud haskell并将其与Erlang进行比较?在分布式编程的环境中,你觉得哪种语言最有效率?

更重要的是缺少制作心智模型的文档。什么我本来希望看到的是一个使用启动代码的演示——这只适用于一元包装器。我对简化发送到解析器的令牌流很感兴趣,但我本应该更清楚地了解我希望看到的内容。有三本关于Erlang的书非常棒,列出了。我发现缺少键入并不是那么简单重要的是,与我的Haskell工作相比,我花在处理类型上的时间可能多了5%。在Erlang中,在运行时管理错误是常态,因此提前正确性就不那么重要了。Cloud Haskell对我来说很有趣,但仍然不成熟;我使用它的时间还不多。Erlang是我长期使用的服务器进程的首选,Haskell用于客户端。
> 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)]


{
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

}