Как заставить парсер строки прекратить - PullRequest
2 голосов
/ 17 января 2020

У меня есть синтаксический анализатор для строк в двойных кавычках, который в основном работает нормально, но когда конечная кавычка отсутствует, он зацикливается навсегда и вылетает приложение.

Это часть веб-приложения, написанного на Elm, и использует elm / parser .

Он основан на примере из Elm Github.

Вот минимальный пример ( run это в Ell ie; измените наParse на "\", и он вылетит на вкладке.)

module Main exposing (main)

import Html
import Parser as P exposing ((|.), (|=))
import Debug


stringP : P.Parser String                                             
stringP =
    P.succeed identity 
        |. P.token "\""                                               
        |= P.loop [] stringHelp


stringHelp : List String -> P.Parser (P.Step (List String) String)    
stringHelp revChunks =                                                
    P.oneOf        
        [ P.token "\""                                                
            |> P.map (\_ -> P.Done (String.join "" (List.reverse revChunks)))  
        , P.chompWhile isUninteresting                                
            |> P.getChompedString                                     
            |> P.map (\chunk -> P.Loop (chunk :: revChunks))          
        ]                                                             


isUninteresting : Char -> Bool
isUninteresting char =
    char /= '\\' && char /= '"'


toParse =
    "\""


main =
    Html.text <| Debug.toString <| P.run stringP toParse

Я вижу, что не так в пути - бит chompWhile успешен, даже если достигнут конец ввода. Мне нужно что-то вроде this , но я не могу понять, как это сделать в этом случае.

1 Ответ

2 голосов
/ 17 января 2020

Я обнаружил, что решение состоит в том, чтобы отследить позицию разбора и выйти из l oop, если позиция не увеличилась. Это не очень красиво, хотя; может быть, кто-то придумает что-нибудь получше.

Код приведен в качестве примера в вопросе, но с этими изменениями ( новый Ell ie):

stringP : P.Parser String                                             
stringP =
    P.succeed identity 
        |. P.token "\""                                               
        |= P.loop ([], 0) stringHelp
        |. P.token "\""


stringHelp : (List String, Int) -> P.Parser (P.Step (List String, Int) String)
stringHelp (revChunks, offset) =
    P.succeed (stepHelp offset)
        |= stringHelp2 revChunks
        |= P.getOffset


stepHelp : Int -> (P.Step (List String) String) -> Int -> P.Step (List String, Int) String
stepHelp oldOffset step newOffset =
    case step of
        P.Done str ->
            P.Done str

        P.Loop revChunks ->
            if newOffset > oldOffset then                              
                P.Loop (revChunks, newOffset)
            else
                P.Done <| String.join "" <| List.reverse revChunks    



stringHelp2 : List String -> P.Parser (P.Step (List String) String)    
stringHelp2 revChunks =                                                
    P.chompWhile isUninteresting                                
        |> P.getChompedString                                     
        |> P.map (\chunk -> P.Loop (chunk :: revChunks))          

Это заканчивается желаемой ошибкой синтаксического анализатора в связи с отсутствующей кавычкой.

...