들여 쓰기 기반 문법에 대한 렉서를 쓰려고하는데 들여 쓰기에 문제가 있습니다.하스켈 알렉스 - 정규식이 잘못된 문자열과 일치합니까?
여기 내 코드입니다 :
{
module Lexer (main) where
import System.IO.Unsafe
}
%wrapper "monadUserState"
$whitespace = [\ \t\b]
$digit = 0-9 -- digits
$alpha = [A-Za-z]
$letter = [a-zA-Z] -- alphabetic characters
$ident = [$letter $digit _] -- identifier character
$indent = [\ \t]
@number = [$digit]+
@identifier = $alpha($alpha|_|$digit)*
error:-
@identifier { mkL LVarId }
\n $whitespace* \n { skip }
\n $whitespace* { setIndent }
$whitespace+ { skip }
{
data Lexeme = Lexeme AlexPosn LexemeClass (Maybe String)
instance Show Lexeme where
show (Lexeme _ LEOF _) = " Lexeme EOF"
show (Lexeme p cl mbs) = " Lexeme class=" ++ show cl ++ showap p ++ showst mbs
where
showap pp = " posn=" ++ showPosn pp
showst Nothing = ""
showst (Just s) = " string=" ++ show s
instance Eq Lexeme where
(Lexeme _ cls1 _) == (Lexeme _ cls2 _) = cls1 == cls2
showPosn :: AlexPosn -> String
showPosn (AlexPn _ line col) = show line ++ ':': show col
tokPosn :: Lexeme -> AlexPosn
tokPosn (Lexeme p _ _) = p
data LexemeClass
= LVarId
| LTIndent Int
| LTDedent Int
| LIndent
| LDedent
| LEOF
deriving (Show, Eq)
mkL :: LexemeClass -> AlexInput -> Int -> Alex Lexeme
mkL c (p, _, _, str) len = return (Lexeme p c (Just (take len str)))
data AlexUserState = AlexUserState { indent :: Int }
alexInitUserState :: AlexUserState
alexInitUserState = AlexUserState 0
type Action = AlexInput -> Int -> Alex Lexeme
getLexerIndentLevel :: Alex Int
getLexerIndentLevel = Alex $ \[email protected]{alex_ust=ust} -> Right (s, indent ust)
setLexerIndentLevel :: Int -> Alex()
setLexerIndentLevel i = Alex $ \[email protected]{alex_ust=ust} -> Right (s{alex_ust=(AlexUserState i)},())
setIndent :: Action
setIndent [email protected](p, _, _, str) i = do
--let !x = unsafePerformIO $ putStrLn $ "|matched string: " ++ str ++ "|"
lastIndent <- getLexerIndentLevel
currIndent <- countIndent (drop 1 str) 0 -- first char is always \n
if (lastIndent < currIndent) then
do setLexerIndentLevel currIndent
mkL (LTIndent (currIndent - lastIndent)) input i
else if (lastIndent > currIndent) then
do setLexerIndentLevel currIndent
mkL (LTDedent (lastIndent - currIndent)) input i
else alexMonadScan
where
countIndent str total
| take 1 str == "\t" = do skip input 1
countIndent (drop 1 str) (total+1)
| take 4 str == " " = do skip input 4
countIndent (drop 4 str) (total+1)
| otherwise = return total
alexEOF :: Alex Lexeme
alexEOF = return (Lexeme undefined LEOF Nothing)
scanner :: String -> Either String [Lexeme]
scanner str =
let loop = do
[email protected](Lexeme _ cl _) <- alexMonadScan
if (cl == LEOF)
then return [tok]
else do toks <- loop
return (tok:toks)
in runAlex str loop
addIndentations :: [Lexeme] -> [Lexeme]
addIndentations ([email protected](Lexeme pos (LTIndent c) _):ls) =
concat [iter lex c, addIndentations ls]
where iter lex c = if c == 0 then []
else (Lexeme pos LIndent Nothing):(iter lex (c-1))
addIndentations ([email protected](Lexeme pos (LTDedent c) _):ls) =
concat [iter lex c, addIndentations ls]
where iter lex c = if c == 0 then []
else (Lexeme pos LDedent Nothing):(iter lex (c-1))
addIndentations (l:ls) = l:(addIndentations ls)
addIndentations [] = []
main = do
s <- getContents
return()
print $ fmap addIndentations (scanner s)
}
문제는 라인 \n $whitespace* { setIndent }
에서, 정규식이 잘못 문자열을 일치하고이 잘못된 문자열 setIndent
를 호출합니다.
begin
first indent
|matched string:
first indent
second indent
second indent
dedent
dedent
|
|matched string:
second indent
dedent
|
|matched string:
dedent
|
|matched string:
|
Right [ Lexeme class=LVarId posn=1:1 string="begin", Lexeme class=LIndent posn=1:6, Lexeme class=LVarId posn=2:15 string="indent", Lexeme class=LIndent posn=2:21, Lexeme class=LDedent posn=3:30, Lexeme class=LDedent posn=3:30, Lexeme class=LVarId posn=4:1 string="dedent", Lexeme EOF]
그래서 setIndent
단지 공백보다 더 불려 : 디버깅 목적을 위해, 나는 여기에 프로그램의 실행 예이야, setIndent
기능에 unsafePerformIO
을 추가했습니다. 들여 쓰기를위한 어휘를 반환 한 후에 문자열의 다른 부분은 생략됩니다.
Alex의 버그입니까? 아니면 내가 뭘 잘못 했니?
답장을 보내 주셔서 감사합니다. 코드를 변경 했으므로 이제는 'str take str'을 사용하고 있지만 아무 것도 변경되지 않았습니다. 'setIndent'가 들여 쓰기 토큰을 반환 한 후에, Alex의 action 함수는 입력 문자열의 다음 부분이 무엇인지 결정하지 않기 때문에,'setIndent'와 관련이 없다고 생각합니다. 함수) – sinan
글쎄, 당신의 정규식은 0 개 이상의 공백 문자가 뒤 따르는 개행 문자와 일치하며 디버그 출력에서 일어나는 것과 똑같다고 말합니다. –
공백 문자와 일치하지만 공백 문자 다음의 추가 문자와 일치하며이 문자는 개행 후에 공백 문자를 맺는 정규식 다음에 정규식과 일치하지 않습니다. 실제로 코드를 실행하고 들여 쓰기 된 줄을 입력하면 (들여 쓰기가 탭 문자의 4 공백 임) 들여 쓰기 토큰 뒤에 토큰이 어떻게 든 생략되는 것을 볼 수 있습니다. – sinan