Stabel

Check-in [3dd9b18af1]
Login
Overview
Comment:Error handling for string parser.
Timelines: family | ancestors | descendants | both | strings
Files: files | file ages | folders
SHA3-256: 3dd9b18af19eff3fc99bb4b660b44b3d64bb4541c90a1918212c485e07047d92
User & Date: robin.hansen on 2021-09-04 11:41:29
Other Links: branch diff | manifest | tags
Context
2021-09-05
09:36
Implemented multiline strings except for indentation handling. check-in: f0c47e6da7 user: robin.hansen tags: strings
2021-09-04
11:41
Error handling for string parser. check-in: 3dd9b18af1 user: robin.hansen tags: strings
10:40
String parser now understands escape sequences. check-in: a54cd89678 user: robin.hansen tags: strings
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Modified src/Stabel/Parser.elm from [046d437afe] to [0e9fb1f9a4].

268
269
270
271
272
273
274
275
276
277
278
279






280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297












298
299
300
301
302
303
304
305
306
textParser =
    Parser.chompWhile (\c -> not <| Set.member c whitespaceChars)
        |> Parser.getChompedString


stringParser : Parser String
stringParser =
    Parser.loop "" stringParserLoop


stringParserLoop : String -> Parser (Parser.Step String String)
stringParserLoop str =






    Parser.oneOf
        [ Parser.succeed (Parser.Done str)
            |. Parser.symbol (Token "\"" UnknownError)
        , Parser.succeed (\char -> Parser.Loop <| str ++ String.fromChar char)
            |. Parser.symbol (Token "\\" UnknownError)
            |= Parser.oneOf
                [ Parser.succeed '\n'
                    |. Parser.symbol (Token "n" UnknownError)
                , Parser.succeed '\t'
                    |. Parser.symbol (Token "t" UnknownError)
                , Parser.succeed '"'
                    |. Parser.symbol (Token "\"" UnknownError)
                , Parser.succeed '\\'
                    |. Parser.symbol (Token "\\" UnknownError)
                ]
        , Parser.succeed ()
            |. Parser.chompIf (always True) UnknownError
            |> Parser.getChompedString












            |> Parser.map (\chompedStr -> Parser.Loop <| str ++ chompedStr)
        ]


genericParser : Parser String
genericParser =
    Parser.oneOf
        [ Parser.succeed identity
            |. Parser.symbol (Token "-" UnknownError)







|


|
|
>
>
>
>
>
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
|
|







268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299

300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
textParser =
    Parser.chompWhile (\c -> not <| Set.member c whitespaceChars)
        |> Parser.getChompedString


stringParser : Parser String
stringParser =
    Parser.loop (Just "") stringParserLoop


stringParserLoop : Maybe String -> Parser (Parser.Step (Maybe String) String)
stringParserLoop maybeStr =
    case maybeStr of
        Nothing ->
            -- Work around bug in elm/parser
            Parser.problem StringNotTerminated

        Just str ->
            Parser.oneOf
                [ Parser.succeed (Parser.Done str)
                    |. Parser.symbol (Token "\"" UnknownError)
                , Parser.succeed (\char -> Parser.Loop <| Just <| str ++ String.fromChar char)
                    |. Parser.symbol (Token "\\" UnknownError)
                    |= Parser.oneOf
                        [ Parser.succeed '\n'
                            |. Parser.symbol (Token "n" UnknownError)
                        , Parser.succeed '\t'
                            |. Parser.symbol (Token "t" UnknownError)
                        , Parser.succeed '"'
                            |. Parser.symbol (Token "\"" UnknownError)
                        , Parser.succeed '\\'
                            |. Parser.symbol (Token "\\" UnknownError)

                        , Parser.succeed ()
                            |. Parser.chompIf (always True) UnknownError
                            |> Parser.getChompedString
                            |> Parser.andThen (\seq -> Parser.problem (UnknownEscapeSequence <| "\\" ++ seq))
                        ]

                -- Couldn't get the below to work in any other way :(
                , Parser.succeed (Parser.Loop Nothing)
                    |. Parser.end StringNotTerminated
                , Parser.succeed (Parser.Done str)
                    |. Parser.symbol (Token "\n" UnknownError)
                    |> Parser.andThen (\_ -> Parser.problem StringNotTerminated)
                , Parser.succeed ()
                    |. Parser.chompWhile (\c -> c /= '\n' && c /= '"' && c /= '\\')
                    |> Parser.getChompedString
                    |> Parser.map (\chompedStr -> Parser.Loop <| Just <| str ++ chompedStr)
                ]


genericParser : Parser String
genericParser =
    Parser.oneOf
        [ Parser.succeed identity
            |. Parser.symbol (Token "-" UnknownError)

Modified src/Stabel/Parser/Problem.elm from [11e01b8df9] to [54e9eb3992].

48
49
50
51
52
53
54


55
56
57
58
59
60
61
...
262
263
264
265
266
267
268








    | ExpectedRightCurly
    | FunctionAlreadyDefined String (Maybe SourceLocationRange)
    | TypeAlreadyDefined String SourceLocationRange
    | UnknownMetadata String
    | InvalidModulePath String
    | ModuleIsEmpty
    | BadDefinition String




toString : String -> String -> DeadEnd Context Problem -> String
toString sourceRef source deadEnd =
    let
        contextExplination =
            contextStackExplination deadEnd
................................................................................
        ModuleIsEmpty ->
            "A module is required to contain at least one definition."

        BadDefinition name ->
            "'"
                ++ name
                ++ "' is not a valid definition. Expected either defmodule:, def:, defmulti:, defstruct: or defunion:"















>
>







 







>
>
>
>
>
>
>
>
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
...
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
    | ExpectedRightCurly
    | FunctionAlreadyDefined String (Maybe SourceLocationRange)
    | TypeAlreadyDefined String SourceLocationRange
    | UnknownMetadata String
    | InvalidModulePath String
    | ModuleIsEmpty
    | BadDefinition String
    | UnknownEscapeSequence String
    | StringNotTerminated


toString : String -> String -> DeadEnd Context Problem -> String
toString sourceRef source deadEnd =
    let
        contextExplination =
            contextStackExplination deadEnd
................................................................................
        ModuleIsEmpty ->
            "A module is required to contain at least one definition."

        BadDefinition name ->
            "'"
                ++ name
                ++ "' is not a valid definition. Expected either defmodule:, def:, defmulti:, defstruct: or defunion:"

        UnknownEscapeSequence seq ->
            "'"
                ++ seq
                ++ "' is not a valid escape sequence. Expected either \\n, \\t, \\\\ or \\\"."

        StringNotTerminated ->
            "This string never terminates. Expected to find a closing \"."

Modified tests/Test/Parser/Errors.elm from [02d836c7d4] to [91a4bff812].

263
264
265
266
267
268
269












































270
271
272
273
274
275
276
                        source =
                            """
                            test
                            """
                    in
                    checkForError (badDefinition "test") source
            ]












































        ]


checkForError : (Problem -> Bool) -> String -> Expectation
checkForError fn source =
    case compile source of
        Err errors ->







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
                        source =
                            """
                            test
                            """
                    in
                    checkForError (badDefinition "test") source
            ]
        , describe "Strings" <|
            let
                unknownEscapeSequence expected problem =
                    case problem of
                        UnknownEscapeSequence actual ->
                            actual == expected

                        _ ->
                            False

                stringNotTerminated problem =
                    problem == StringNotTerminated
            in
            [ test "Unknown escape sequence" <|
                \_ ->
                    let
                        source =
                            """
                            def: src
                            : "bad \\u"
                            """
                    in
                    checkForError (unknownEscapeSequence "\\u") source
            , test "Newline before terminating string" <|
                \_ ->
                    let
                        source =
                            """
                            def: src
                            : "bad 
                              string"
                            """
                    in
                    checkForError stringNotTerminated source
            , test "String never terminates" <|
                \_ ->
                    let
                        source =
                            """
                           def: src
                           : "bad string"""
                    in
                    checkForError stringNotTerminated source
            ]
        ]


checkForError : (Problem -> Bool) -> String -> Expectation
checkForError fn source =
    case compile source of
        Err errors ->