Stabel

Check-in [df946f0acc]
Login
Overview
Comment:Allow underscores to group large integers.
Timelines: family | ancestors | descendants | both | integer-syntax-improvements
Files: files | file ages | folders
SHA3-256: df946f0accaf63ad36fef4fb94a4c62ada7ad1f7ee780883018177441869d2ac
User & Date: robin.hansen on 2021-09-13 19:20:49
Other Links: branch diff | manifest | tags
Context
2021-09-16
13:05
Check that integers are within range of 32-bit signed. check-in: db0d5f7a64 user: robin.hansen tags: integer-syntax-improvements
2021-09-13
19:20
Allow underscores to group large integers. check-in: df946f0acc user: robin.hansen tags: integer-syntax-improvements
18:37
Add more contexts. check-in: b9aa49ac53 user: robin.hansen tags: integer-syntax-improvements
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Modified src/Stabel/Parser.elm from [f7bb614a2e] to [55df2cef68].

138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179


























180
181
182
183
184
185
186
        ]


{-| The builtin int parser has a bug where it commits when it comes across an 'e'
-}
intParser : Parser Int
intParser =
    let
        helper ( text, isNegative ) =
            case String.toInt text of
                Just num ->
                    Parser.succeed <|
                        if isNegative then
                            num * -1

                        else
                            num

                Nothing ->
                    Parser.problem Problem.ExpectedInt
    in
    Parser.inContext Problem.IntegerLiteral
        (Parser.succeed Tuple.pair
            |= Parser.variable
                { start = Char.isDigit
                , inner = Char.isDigit
                , reserved = Set.empty
                , expecting = ExpectedInt
                }
            |= Parser.oneOf
                [ Parser.succeed True
                    |. Parser.symbol (Token "-" UnknownError)
                , Parser.succeed False
                ]
            |. Parser.oneOf
                [ Parser.chompIf (\c -> Set.member c whitespaceChars) ExpectedWhitespace
                , Parser.succeed ()
                    |. Parser.end ExpectedEndOfFile
                ]
            |> Parser.andThen helper
        )




























sourceLocationParser : Parser SourceLocation
sourceLocationParser =
    Parser.succeed SourceLocation
        |= Parser.getRow
        |= Parser.getCol








<
<
<
<
<
<
<
<
<
<
<
<
<
<




|













|


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







138
139
140
141
142
143
144














145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
        ]


{-| The builtin int parser has a bug where it commits when it comes across an 'e'
-}
intParser : Parser Int
intParser =














    Parser.inContext Problem.IntegerLiteral
        (Parser.succeed Tuple.pair
            |= Parser.variable
                { start = Char.isDigit
                , inner = \c -> Char.isDigit c || c == '_'
                , reserved = Set.empty
                , expecting = ExpectedInt
                }
            |= Parser.oneOf
                [ Parser.succeed True
                    |. Parser.symbol (Token "-" UnknownError)
                , Parser.succeed False
                ]
            |. Parser.oneOf
                [ Parser.chompIf (\c -> Set.member c whitespaceChars) ExpectedWhitespace
                , Parser.succeed ()
                    |. Parser.end ExpectedEndOfFile
                ]
            |> Parser.andThen intParserHelper
        )


intParserHelper : ( String, Bool ) -> Parser Int
intParserHelper ( text, isNegative ) =
    let
        digits =
            String.replace "_" "" text
    in
    if String.endsWith "_" text then
        Parser.problem IntegerTrailingUnderscore

    else if String.length digits > 1 && String.startsWith "0" digits then
        Parser.problem IntegerBadLeadingZero

    else
        case String.toInt digits of
            Just num ->
                Parser.succeed <|
                    if isNegative then
                        num * -1

                    else
                        num

            Nothing ->
                Parser.problem ExpectedInt


sourceLocationParser : Parser SourceLocation
sourceLocationParser =
    Parser.succeed SourceLocation
        |= Parser.getRow
        |= Parser.getCol

Modified src/Stabel/Parser/Problem.elm from [f7612d9509] to [5e7629c919].

54
55
56
57
58
59
60


61
62
63
64
65
66
67
...
292
293
294
295
296
297
298






    | 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.contextStack
................................................................................
        UnknownEscapeSequence seq ->
            "'"
                ++ seq
                ++ "' is not a valid escape sequence. Expected either \\n, \\t, \\\\ or \\\"."

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













>
>







 







>
>
>
>
>
>
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
...
294
295
296
297
298
299
300
301
302
303
304
305
306
    | TypeAlreadyDefined String SourceLocationRange
    | UnknownMetadata String
    | InvalidModulePath String
    | ModuleIsEmpty
    | BadDefinition String
    | UnknownEscapeSequence String
    | StringNotTerminated
    | IntegerBadLeadingZero
    | IntegerTrailingUnderscore


toString : String -> String -> DeadEnd Context Problem -> String
toString sourceRef source deadEnd =
    let
        contextExplination =
            contextStackExplination deadEnd.contextStack
................................................................................
        UnknownEscapeSequence seq ->
            "'"
                ++ seq
                ++ "' is not a valid escape sequence. Expected either \\n, \\t, \\\\ or \\\"."

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

        IntegerBadLeadingZero ->
            "Integers cannot start with 0."

        IntegerTrailingUnderscore ->
            "Integers cannot end with an underscore."

Modified tests/Test/Parser.elm from [a306bec1ba] to [78a2b93463].

1531
1532
1533
1534
1535
1536
1537
1538


1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
            in
            [ fuzz positiveIntFuzzer "Positive int" <|
                \num ->
                    expectParseInt (String.fromInt num) num
            , fuzz negativeIntFuzzer "Negative ints" <|
                \num ->
                    expectParseInt (String.fromInt (num * -1) ++ "-") num
            , Test.todo "can contain underscores as seperators"


            ]
        ]


positiveIntFuzzer : Fuzzer Int
positiveIntFuzzer =
    Fuzz.intRange 0 Random.maxInt


negativeIntFuzzer : Fuzzer Int
negativeIntFuzzer =
    Fuzz.intRange Random.minInt -1







|
>
>












1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
            in
            [ fuzz positiveIntFuzzer "Positive int" <|
                \num ->
                    expectParseInt (String.fromInt num) num
            , fuzz negativeIntFuzzer "Negative ints" <|
                \num ->
                    expectParseInt (String.fromInt (num * -1) ++ "-") num
            , test "can contain underscores as seperators" <|
                \_ ->
                    expectParseInt "10_000" 10000
            ]
        ]


positiveIntFuzzer : Fuzzer Int
positiveIntFuzzer =
    Fuzz.intRange 0 Random.maxInt


negativeIntFuzzer : Fuzzer Int
negativeIntFuzzer =
    Fuzz.intRange Random.minInt -1

Modified tests/Test/Parser/Errors.elm from [64b2513855] to [71430dff1d].

327
328
329
330
331
332
333
334
335








336









337
338
339
340
341
342
343
                        source =
                            """
                            def: src
                            : 43x
                            """
                    in
                    checkForError ((==) ExpectedWhitespace) source
            , Test.todo "cannot start with leading 0"
            , Test.todo "numbers cannot begin with underscores"








            , Test.todo "numbers cannot end with underscores"









            , Test.todo "positive number constant must fit in signed 32-bit int"
            , Test.todo "negative number constant must fit in signed 32-bit int"
            ]
        ]


checkForError : (Problem -> Bool) -> String -> Expectation







|
|
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>







327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
                        source =
                            """
                            def: src
                            : 43x
                            """
                    in
                    checkForError ((==) ExpectedWhitespace) source
            , test "cannot start with leading 0" <|
                \_ ->
                    let
                        source =
                            """
                            def: src
                            : 010
                            """
                    in
                    checkForError ((==) IntegerBadLeadingZero) source
            , test "numbers cannot end with underscores" <|
                \_ ->
                    let
                        source =
                            """
                            def: src
                            : 100_
                            """
                    in
                    checkForError ((==) IntegerTrailingUnderscore) source
            , Test.todo "positive number constant must fit in signed 32-bit int"
            , Test.todo "negative number constant must fit in signed 32-bit int"
            ]
        ]


checkForError : (Problem -> Bool) -> String -> Expectation