aboutsummaryrefslogtreecommitdiff
path: root/js/src/Parser.purs
blob: b378e96564d3318347a28b6151d85e553d6f82e4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
module Parser
  ( TextWithNumber
  , textWithNumber
  , number
  ) where

import Control.Alt ((<|>))
import Data.Array as Array
import Data.Char as Char
import Data.Either (Either(Right))
import Data.Int as Int
import Data.Maybe (fromMaybe) as Maybe
import Data.Maybe (Maybe(Just, Nothing))
import Data.String.CodeUnits as String
import Prelude
import Text.Parsing.Parser (Parser)
import Text.Parsing.Parser (runParser) as Parser
import Text.Parsing.Parser.Combinators (optionMaybe) as Parser
import Text.Parsing.Parser.String (satisfy, anyChar, string, eof) as Parser

type TextWithNumber =
  { begin :: String
  , number :: Number
  , end :: String
  }

textWithNumber :: String -> Maybe TextWithNumber
textWithNumber input =
  case Parser.runParser input textWithNumberParser of
    Right x -> Just x
    _ -> Nothing

number :: String -> Maybe Number
number input =
  case Parser.runParser input (numberParser <* Parser.eof) of
    Right x -> Just x
    _ -> Nothing

textWithNumberParser :: Parser String TextWithNumber
textWithNumberParser = do
  begin <- String.fromCharArray <$> Array.many notDigit
  num <- numberParser
  end <- String.fromCharArray <$> Array.many Parser.anyChar
  pure { begin: begin, number: num, end: end }

notDigit :: Parser String Char
notDigit = Parser.satisfy (not <<< isDigit)

numberParser :: Parser String Number
numberParser = do
  whole <- numberFromIntArray <$> Array.some digit
  decimal <- Parser.optionMaybe $ do
    _ <- Parser.string "," <|> Parser.string "."
    digits <- Array.some digit
    let decimals = numberFromIntArray digits
    pure $ Int.toNumber decimals / Int.toNumber (Int.pow 10 (Array.length digits))
  pure (Int.toNumber whole + Maybe.fromMaybe 0.0 decimal)

numberFromIntArray :: Array Int -> Int
numberFromIntArray xs =
  Array.range 0 (Array.length xs - 1)
    # map (Int.pow 10)
    # Array.reverse
    # Array.zipWith (*) xs
    # Array.foldl (+) 0

digit :: Parser String Int
digit = map (\c -> Char.toCharCode c - zeroCode) $ Parser.satisfy isDigit

isDigit :: Char -> Boolean
isDigit char =
  let code = Char.toCharCode char
  in  code >= zeroCode && code <= zeroCode + 9

zeroCode :: Int
zeroCode = 48