为什么这个词法分析器不解析这个输入?
Why is this lexer not parsing this input?
我想对以下代码示例进行 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 词法分析器在第一行遇到 \
时出错(在 \
后面有和没有 space)。
为什么会这样?
词法分析器:
{
{-# 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])+
以一种灵活的方式进行“除 x 之外的任何操作”匹配,但这并没有改变输出中的任何内容。
不幸的是,很难阅读您的 lex 规则。但是你的令牌定义有两个错误。
首先是以下内容:
"\" {\_ s -> Lam}
应该是:
"\" {\_ s -> Lam}
(请注意,我们 不是 转义反斜杠。)这确实违反直觉,但这是 Alex 规则的语法,因此你不应该t 在那里引用反斜杠。 (否则,它将连续匹配两个反斜杠。)
第二个是你的规则:
\" @string \" { \_ s -> StringLit (tail . init $ s) }
应该是:
\" @string* \" { \_ s -> StringLit (tail . init $ s) }
(注意 @string
后的星号。)也就是说,您的字符串需要接受 0 个或更多字符。
如果您进行以上两项更改,您会发现您的输入现在可以顺利通过。
但是,您似乎试图在词法分析器中做太多事情:词法分析器应该非常简单;它绝对不应该包含像 portSpec
这样复杂的规则。相反,您应该简单地标记为基本成分(或多或少由 white-space 分隔,字符串除外),然后您应该使用像 Happy 这样的合适的解析器生成器来对您的语言进行实际解析。这是标准方法。
我想对以下代码示例进行 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 词法分析器在第一行遇到 \
时出错(在 \
后面有和没有 space)。
为什么会这样? 词法分析器:
{
{-# 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])+
以一种灵活的方式进行“除 x 之外的任何操作”匹配,但这并没有改变输出中的任何内容。
不幸的是,很难阅读您的 lex 规则。但是你的令牌定义有两个错误。
首先是以下内容:
"\" {\_ s -> Lam}
应该是:
"\" {\_ s -> Lam}
(请注意,我们 不是 转义反斜杠。)这确实违反直觉,但这是 Alex 规则的语法,因此你不应该t 在那里引用反斜杠。 (否则,它将连续匹配两个反斜杠。)
第二个是你的规则:
\" @string \" { \_ s -> StringLit (tail . init $ s) }
应该是:
\" @string* \" { \_ s -> StringLit (tail . init $ s) }
(注意 @string
后的星号。)也就是说,您的字符串需要接受 0 个或更多字符。
如果您进行以上两项更改,您会发现您的输入现在可以顺利通过。
但是,您似乎试图在词法分析器中做太多事情:词法分析器应该非常简单;它绝对不应该包含像 portSpec
这样复杂的规则。相反,您应该简单地标记为基本成分(或多或少由 white-space 分隔,字符串除外),然后您应该使用像 Happy 这样的合适的解析器生成器来对您的语言进行实际解析。这是标准方法。