Ver código fonte

Find a proper way to parse dot separated values

jherve 1 ano atrás
pai
commit
4332d84aa6
2 arquivos alterados com 55 adições e 18 exclusões
  1. 38 12
      src/LinkedIn/UIElements/Parser.purs
  2. 17 6
      test/UIStringParser.purs

+ 38 - 12
src/LinkedIn/UIElements/Parser.purs

@@ -5,19 +5,23 @@ import Prelude
 import Control.Alt ((<|>))
 import Data.Array as A
 import Data.Date (Month(..), Year)
-import Data.Either (Either(..))
+import Data.Either (hush)
 import Data.Enum (toEnum)
 import Data.Int (fromNumber)
 import Data.List (List(..), (:))
+import Data.List as L
+import Data.List.NonEmpty as NEL
+import Data.List.Types (NonEmptyList)
 import Data.Map (Map)
 import Data.Map as M
 import Data.Maybe (Maybe(..))
+import Data.String as S
 import Data.String.CodePoints (codePointFromChar)
 import Data.Tuple (Tuple(..))
 import LinkedIn.UIElements.Types (Duration(..), MonthYear(..), MonthYearOrToday(..), TimeSpan(..), UIString(..))
-import Parsing (Parser, fail, runParser)
+import Parsing (Parser, ParserT, fail, liftMaybe, runParser)
 import Parsing.Combinators (choice, try)
-import Parsing.String (string, char, rest)
+import Parsing.String (char, rest, string)
 import Parsing.String.Basic (intDecimal, number, space, takeWhile)
 
 monthStrToMonth :: Map String Month
@@ -131,17 +135,39 @@ stringWithoutMedianDotP = takeWhile (\c -> c /= codePointFromChar '·' && c /= c
 uiStringP :: Parser String UIString
 uiStringP = (try uiStringdotSeparatedP) <|> uiStringSingleP
 
+uiStringWithoutMedianDotP ∷ Parser String UIString
+uiStringWithoutMedianDotP = do
+  s <- rest
+  liftMaybe (\_ -> "nope") $ hush $ runParser s uiStringSingleP
+
 uiStringdotSeparatedP ∷ Parser String UIString
 uiStringdotSeparatedP = do
-  subStr <- stringWithoutMedianDotP
-  _ <- medianDotP
-  _ <- space
-  sub2Str <- rest
-  case runParser subStr uiStringSingleP of
-    Right sub -> case runParser sub2Str uiStringSingleP of
-      Right sub2 -> pure $ UIStringDotSeparated sub sub2
-      Left _ -> fail "not a sub"
-    Left _ -> fail "not a sub"
+  Tuple s1 s2 <- medianDotSeparated
+
+  let
+    intoUiElement :: String -> Parser String UIString
+    intoUiElement s = liftMaybe (\_ -> "could not convert to ui element") $ hush $ runParser s uiStringSingleP
+
+  s1' <- intoUiElement s1
+  s2' <- intoUiElement s2
+
+  pure $ UIStringDotSeparated s1' s2'
+
+sepBy2 :: forall m s a sep. ParserT s m a -> ParserT s m sep -> ParserT s m (NonEmptyList a)
+sepBy2 p sep = do
+  a0 <- p
+  a1 <- sep *> p
+  as <- L.manyRec $ sep *> p
+  pure $ NEL.cons a0 $ NEL.cons' a1 as
+
+commaSeparated ∷ Parser String (NonEmptyList String)
+commaSeparated = stringWithoutCommaP `sepBy2` commaP
+
+medianDotSeparated ∷ Parser String (Tuple String String)
+medianDotSeparated = do
+  a0 <- stringWithoutMedianDotP
+  a1 <- medianDotP *> rest
+  pure $ Tuple (S.trim a0) (S.trim a1)
 
 uiStringSingleP ∷ Parser String UIString
 uiStringSingleP = (try uiStringDurationP) <|> (try uiStringTimeSpanP) <|> uiStringPlainP

+ 17 - 6
test/UIStringParser.purs

@@ -3,13 +3,14 @@ module Test.UIStringParser
   )
   where
 
+import Prelude
+
 import Data.Date (Month(..))
 import Data.Either (Either(..))
 import Effect (Effect)
 import LinkedIn.UIElements.Parser (durationP, monthYearP, timeSpanP, uiStringDurationP, uiStringdotSeparatedP)
 import LinkedIn.UIElements.Types (Duration(..), TimeSpan(..), UIString(..))
-import Parsing (runParser)
-import Prelude (Unit, discard)
+import Parsing (ParseError(..), Position(..), runParser)
 import Test.Assert (assertEqual)
 import Test.Utils (toMonthYear')
 
@@ -76,13 +77,23 @@ testUIParserDuration = do
 testUIParserDotSeparated ∷ Effect Unit
 testUIParserDotSeparated = do
   assertEqual {
-    actual: run "2 ans 3 mois · some text",
-    expected: Right(UIStringDotSeparated (UIStringDuration (YearsMonth 2 3)) (UIStringPlain "some text"))
+    actual: run "some text 1 · some text 2",
+    expected: Right(UIStringDotSeparated (UIStringPlain "some text 1") (UIStringPlain "some text 2"))
+  }
+
+  assertEqual {
+    actual: run "· some text after a dot",
+    expected: Right(UIStringDotSeparated (UIStringPlain "") (UIStringPlain "some text after a dot"))
+  }
+
+  assertEqual {
+    actual: run "some text before a dot ·",
+    expected: Right(UIStringDotSeparated (UIStringPlain "some text before a dot") (UIStringPlain ""))
   }
 
   assertEqual {
-    actual: run "· Boulogne-Billancourt, Île-de-France, France",
-    expected: Right(UIStringDotSeparated (UIStringPlain "") (UIStringPlain "Boulogne-Billancourt, Île-de-France, France"))
+    actual: run "string with no dot",
+    expected: (Left (ParseError "Expected '•'" (Position { column: 19, index: 18, line: 1 })))
   }
 
   where run s = runParser s uiStringdotSeparatedP