【问题标题】:Why is this lexer not parsing this input?为什么这个词法分析器不解析这个输入?
【发布时间】:2021-04-21 16:24:08
【问题描述】:

我想对以下代码示例进行 lex:

prop levelBasedAlerter uni { a b } \I -> 
  levelBasedAlerter a                           
    | a > I ->                                   
      b: "ALERT: %a"                            

这应该是

Prop
Var "levelBasedAlerter"
Uni
PortSpecS " { a b }"
Lam
Var "I"
PatternMatchEnd
Indent 2
Var "levelBasedAlerter"
Var "a"
Indent 4
PatternGuard
Var "a"
Var ">"
Var "I" 
PatternMatchEnd
Indent 6
Var "b"
DefinedByCol
StringLit "Alert: %a"

但是,我的 alex 词法分析器在第一行遇到 \ 时出现错误(\ 后面有和没有空格)。

为什么会这样? 词法分析器:

{
{-# LANGUAGE DeriveDataTypeable #-}
module Lexer where
import Data.Typeable
import Data.Data
import Data.List
import Data.List.Split
import Data.Char
import Debug.Trace
import Prelude hiding (lex)
import Control.Monad (liftM)
}

%wrapper "posn"

$digit            = 0-9
@string           = (. # [\" \\] )

$alpha            = [a-zA-Z]
@real             = ($digit+ \. | $digit * \. $digit +)
@boolLit          = ("True"|"False")
@alphaNum         = ($alpha|$digit)+
$bracketsOpen     = [\(\[\{]
$bracketsClose    = [\)\]\}]
$brackets         = [ $bracketsOpen $bracketsClose]
@identifier       = [^ : ! = \\ \ " $brackets]+
@commaOrSpace     = (\,\ * | \ +)
@scopedIdentifier = @identifier(\.@identifier)+
@globalKeyword    = (prop|mesh|let|omni|uni|let|using|module|import|where)
@port             = (@identifier:\ *)?@identifier
@portSpec         = ((@identifier|@scopedIdentifier):)?
                    " "*
                        \{\ * @port
                            (@commaOrSpace @port)*
                        " "*\} 
@deepPortSpec     = ((@identifier|@scopedIdentifier):)?
                    " "*
                        \{\ * @identifier: (. # \})+ \} 
@indent           = \n[\t\ ]+

tokens :-
    @indent         { \_ s -> Indent $ length s }
    $white+         ;
    "--".*          ;
    @globalKeyword  { \_ keyword -> getTokenOf keyword }
    $digit+         { \_ s -> IntL (read s) }
    @real+          { \_ s -> DoubleL (read s) }
    @boolLit        { \_ s -> BoolL (read s) }
    \" @string \"   { \_ s -> StringLit (tail . init $ s) }
    @portSpec       { \_ s -> PortSpecS s } 
    @deepPortSpec   { \_ s -> DeepPortSpecS s }
    ":"             { \_ s -> DefinedByCol }
    ","             { \_ s -> Comma }
    "!"             { \_ s -> Negate }
    "=="            { \_ s -> Eq }
    "="             { \_ s -> LetAssOp }
    "~>"            { \_ s -> Wire }
    "->"            { \_ s -> PatternMatchEnd }
    $bracketsOpen   { \_ s -> BracO s}
    $bracketsClose  { \_ s -> BracC s}
    "||"            { \_ s -> Or }
    "|"             { \_ s -> PatternGuard}
    "!!"            { \_ s -> AccessPort }
    "\\"            { \_ s -> Lam }

    @scopedIdentifier {\_ s -> ScopedVar s }
    @identifier     { \_ s -> Var s }

{

clean :: String -> String
clean s = reverse $ rmWs $ reverse $ rmWs s
    where rmWs = dropWhile (\c -> c ==' ' || c == '\t')

traceThis :: (Show a) => a -> a
traceThis a = trace ("DEBUG: " ++ show a) a

data Token
           = Prop
           | Mesh
           | Module
           | Import
           | Where
           | Var String
           | BracO String
           | BracC String
           | Comma
           | Eq
           | PatternGuard
           | Or
           | ScopedVar String 
           | Omni
           | Uni
           | PortSpecS String
           | DeepPortSpecS String
           | DefinedByCol                       -- ':' after definitions
           | Indent Int
           | PatternMatchEnd                    -- '->' after PM
           | Negate    
           | Let 
           | LetAssOp                           -- '=' in let x = ...
           | Wire
           | AccessPort
           | Using
           | Lam
           | StringLit String
           | IntL Int
           | DoubleL Double
           | BoolL Bool
           | EOF 
    deriving (Eq,Show,Data)

getTokenOf :: String -> Token
getTokenOf s = fromConstr 
             $ head $ filter ((==s) . map toLower . showConstr) 
             $ dataTypeConstrs $ dataTypeOf $ Prop



}

我认为这与我如何匹配\ 令牌有关。 但是,我尝试过像这样匹配它

'\' '\\' "\" "\\" \\ \ 以及正则表达式,但似乎没有任何效果。

alex 中的\ 是否有一些奇怪的行为?还是我看不到的其他一些小错误?

更新

我现在尝试将@identifier 更改为:

@identifier       = (. # [ : ! = \\ \ " $brackets])+

以 alexy 方式进行“除 x 之外的任何事情”匹配,但这并没有改变输出中的任何内容。

【问题讨论】:

    标签: haskell alex


    【解决方案1】:

    很遗憾,很难阅读您的 lex 规则。但是您的标记定义中有两个错误。

    首先,如下:

     "\\"   {\_ s -> Lam}
    

    应该是:

      "\"   {\_ s -> Lam}
    

    (请注意,我们确实 转义反斜杠。)这确实违反直觉,但这是 Alex 规则的语法,因此您不应该引用反斜杠-斜线那里。 (否则,它将匹配两个背对背的反斜杠。)

    第二个是你的规则:

        \" @string \"  { \_ s -> StringLit (tail . init $ s) }
    

    应该是:

        \" @string* \"  { \_ s -> StringLit (tail . init $ s) }
    

    (注意@string后面的星号。)也就是说,你的字符串需要接受0个或多个字符。

    如果您进行上述两项更改,您将看到您的输入现在可以顺利通过。

    但是,您似乎试图在词法分析器中做太多事情:词法分析器确实应该非常简单;它绝对不应该包含您拥有的复杂规则,例如portSpec。相反,您应该简单地标记为基本成分(或多或少由空格分隔,字符串除外),然后您应该使用像 Happy 这样的适当解析器生成器来对您的语言进行实际解析。这是标准方法。

    【讨论】:

    • 但标识符规则中的[^ 不应该指定任何字符都可以是标识符的一部分,除了[^ .. ] 中的字符和类;我知道这至少是这种语义的正则表达式语法
    • 关于词法分析器包含多少东西:我知道这一点,我已经开始慢慢改变它;如果我确定问题与这些部分无关,我就不会发布它们;我主要想了解为什么在这种情况下它会失败
    • 你说的很对;一开始我误读了你的定义。我更新了答案以反映实际问题。但最后的信息保持不变;让您的词法分析器更简单,并使用 Happy 进行实际解析。
    • 有趣的是不需要转义,这是我唯一没有尝试过的组合;非常感谢;我现在实际上正在简化它;从一些代码示例中得到了错误的印象,即其中应该有多少。
    猜你喜欢
    • 2016-08-07
    • 1970-01-01
    • 2013-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多