[英]Why is this lexer not parsing this input?
我想使用以下代碼示例:
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
}
我認為這與我如何匹配\
令牌有關。 但是,我嘗試過匹配它
'\'
'\\'
"\"
"\\"
\\
\
以及正則表達式,但似乎沒有任何效果。
關於 alex 中的\
是否有一些奇怪的行為? 還是我看不到的其他一些微不足道的錯誤?
更新
我現在嘗試將@identifier
更改為此:
@identifier = (. # [ : ! = \\ \ " $brackets])+
以 alexy 的方式進行“除了 x 之外的任何事情”匹配,但這並沒有改變 output 中的任何內容。
不幸的是,很難閱讀您的 lex 規則。 但是您的標記定義中有兩個錯誤。
首先,以下內容:
"\\" {\_ s -> Lam}
應該:
"\" {\_ s -> Lam}
(請注意,我們不會轉義反斜杠。)這確實違反直覺,但這是 Alex 規則的語法,因此您不應該在此處引用反斜杠。 (否則,它將匹配兩個背對背的反斜杠。)
第二個是你的規則:
\" @string \" { \_ s -> StringLit (tail . init $ s) }
應該:
\" @string* \" { \_ s -> StringLit (tail . init $ s) }
(注意@string
的星號。)也就是說,您的字符串需要接受 0 個或更多字符。
如果您進行上述兩項更改,您將看到您的輸入現在可以順利通過。
但是,您似乎試圖在您的詞法分析器中做太多事情:詞法分析器應該非常簡單; 它絕對不應該包含您擁有的復雜規則,例如portSpec
。 相反,您應該簡單地標記為基本成分(或多或少由空格分隔,字符串除外),然后您應該使用像 Happy 這樣的適當解析器生成器來對您的語言進行實際解析。 這是標准方法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.