2

我想使用以下代码示例:

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 lexer 在第一行遇到错误时停止\(在 后面有和没有空格\)。

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

{
{-# 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



}

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

'\' '\\' "\" "\\" \\ \还有一个正则表达式,但似乎没有任何效果。

\亚历克斯有什么奇怪的行为吗?还是我看不到的其他一些微不足道的错误?

更新

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

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

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

4

1 回答 1

2

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

首先,以下内容:

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

应该:

  "\"   {\_ s -> Lam}

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

第二个是你的规则:

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

应该:

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

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

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

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

于 2021-04-22T21:42:54.260 回答