2015-08-13 3 views
2

Я написал лексер в Alex, и я пытаюсь подключить его к парсеру, написанному на Happy. Я постараюсь изо всех сил суммировать свою проблему, не вставляя огромные куски кода.Что вызывает счастье, чтобы вывести ошибку синтаксического анализа?

Я знаю из моих модульных тестов моего лексере, что строка "\x7" является lexed к:

[TokenNonPrint '\x7', TokenEOF] 

Моего типа лексемы (выплюнуть лексер), является Token. Я определил lexWrap и alexEOF, как описано here, который дает мне следующий заголовок и символические объявления:

%name parseTokens 
%tokentype { Token } 
%lexer { lexWrap } { alexEOF } 
%monad { Alex } 
%error { parseError } 

%token 
    NONPRINT {TokenNonPrint $$} 
    PLAIN { TokenPlain $$ } 

Я призываю парсер + лексический анализатор комб следующее:

parseExpr :: String -> Either String [Expr] 
parseExpr s = runAlex s parseTokens 

А вот мои первые несколько производств:

exprs :: { [Expr] } 
exprs 
    : {- empty -} { trace "exprs 30" [] } 
    | exprs expr { trace "exprs 31" $ $2 : $1 } 

nonprint :: { Cmd } 
    : NONPRINT { NonPrint $ parseNonPrint $1} 

expr :: { Expr } 
expr 
    : nonprint {trace "expr 44" $ Cmd $ $1} 
    | PLAIN { trace "expr 37" $ Plain $1 } 

Я уеду из деклараций типа данных Expr и NonPrint, так как они длинны, и только конструкторы Cmd и NonPrint здесь. Функция parseNonPrint определяется в нижней части parse.y как:

parseNonPrint :: Char -> NonPrint 
parseNonPrint '\x7' = Bell 

Кроме того, моя ошибка функции обработки выглядит следующим образом:

parseError :: Token -> Alex a 
parseError tokens = error ("Error processing token: " ++ show tokens) 

Написано так, я надеюсь, следующий hspec тест пройти:

parseExpr "\x7" `shouldBe` Right [Cmd (NonPrint Bell)] 

Но вместо этого, я вижу "exprs 30" печать раз (хотя я бегу 5 различных уни t) и все мои тесты parseExpr return Right []. Я не понимаю, почему это было бы дело, но я изменил exprs производство, чтобы предотвратить его:

exprs :: { [Expr] } 
exprs 
    : expr { trace "exprs 30" [$1] } 
    | exprs expr { trace "exprs 31" $ $2 : $1 } 

Теперь все мои тесты не на первом маркере они попали --- parseExpr "\x7" терпит неудачу с:

uncaught exception: ErrorCall (Error processing token: TokenNonPrint '\a') 

И я полностью смущен, так как я ожидаю, что синтаксический анализатор примет путь exprs -> expr -> nonprint -> NONPRINT и добьется успеха. Я не понимаю, почему этот ввод поставил бы синтаксический анализатор в состояние ошибки. Ни один из операторов trace не попал (оптимизирован?).

Что я делаю неправильно?

+0

Можете ли вы указать нам на код - т. Е. Репозиторий github? – ErikR

+0

@ user5402 https://github.com/pscollins/ansi-parser имеет весь код. На данный момент это немного небрежно, особенно маркировка тестов. –

ответ

0

Оказывается, причина этой ошибки была безобидная линия

%lexer { lexWrap } { alexEOF } 

который по связанному вопросу рекомендуется об использовании Алекса Happy (к сожалению, один из лучших результатов Google для запросов, как «с помощью Alex . как монадическая лексере с Днем) исправление, чтобы изменить его на следующее:

%lexer { lexWrap } { TokenEOF } 

Я должен был вырыть, чтобы генерируемый код, чтобы раскрыть этот вопрос.Это вызвано тем кодом, полученный из %tokens директивы, которая выглядит следующим образом (я закомментировать все мои лексемы деклараций за TokenNonPrint исключением, пытаясь отследить ошибку):

happyNewToken action sts stk 
    = lexWrap(\tk -> 
    let cont i = happyDoAction i tk action sts stk in 
    case tk of { 
    alexEOF -> happyDoAction 2# tk action sts stk; -- !!!! 
    TokenNonPrint happy_dollar_dollar -> cont 1#; 
    _ -> happyError' tk 
    }) 

Очевидно, Happy трансформирует каждую строку директивы %tokens в одну ветвь совпадения шаблонов. Он также вставляет ветку для того, что было идентифицировано как маркер EOF в директиве %lexer.

Добавляя имя значения, alexEOF, а не конструктор данных, TokenEOF, эта ветвь саза имеет эффект повторного связывание имени alexEOF к тому, что маркер было принято, чтобы lexWrap, слежкой оригинала связывание и короткое замыкание оператора case, чтобы он каждый раз попадал в правило EOF, что как-то приводит к тому, что Happy вводит состояние ошибки.

Ошибка не попадает в систему типов, так как идентификатор alexEOF (или TokenEOF) не появляется нигде в сгенерированном коде. Неправильное использование директивы %lexer приведет к тому, что GHC выдаст предупреждение, но, поскольку предупреждение появляется в сгенерированном коде, невозможно отличить его от всех других безобидных предупреждений, которые выдает код.

Смежные вопросы