2
\$\begingroup\$

As a beginner exercise, I made small manual lexer that recognizes three types of inputs:

  • integers: /[-]?[0-9]+/
  • strings, inside double quotes, with backslash escaping
  • nil

using only Data.Char and Data.Maybe.

I would have wanted to do something that looks like this

parse s =  tryParseInteger s
        OR tryParseString s
        OR tryParseNil

where each tryParse* would return a (Maybe MyToken) and a failing case (= Nothing) would continue to the next tryParse*. But I didn't find a clean way to do it.

So here it goes:

import Data.Char;
import Data.Maybe;

data MyToken = MyNil
             | MyNumber Int
             | MyString String
             deriving (Show)

tryParse :: String -> Maybe MyToken
tryParse "nil" = Just MyNil
tryParse (c : t)
  -- Ignoring white space and parse the tail
  | isSpace c = tryParse t
tryParse s = tryParseNumber s


tryParseNumber :: String -> Maybe MyToken
tryParseNumber s = case (parseNum s) of
                     Just v -> Just $ MyNumber v
                     Nothing -> tryParseString s


tryParseString :: String -> Maybe MyToken
tryParseString ('"':t) = fmap MyString (parseInsideString t)
tryParseString _ = Nothing


parseInsideString :: String -> Maybe String
parseInsideString ('\\':'"':cs) = fmap ('"':) (parseInsideString cs)
parseInsideString ('"':_) = Just ""
parseInsideString (c:cs) = fmap (c:) (parseInsideString cs)
parseInsideString _ = Nothing


parseNum :: String -> Maybe Int
parseNum ('-':xs) = fmap (\x -> -x) (parseNum xs)
parseNum cs@(c:_)
  | isDigit c = foldl step Nothing cs
  | otherwise = Nothing
    where step acc c
           | isDigit c = Just ((10 * fromMaybe 0 acc) + (digitToInt c))
           | otherwise = acc


main = print $ tryParse "\"abcd\\\"efgh\""
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

I would have wanted to do something that looks like this

parse s =  tryParseInteger s
        OR tryParseString s
        OR tryParseNil

OR is <|>:

tryParse :: String -> Maybe MyToken
tryParse = tryParse' . dropWhile isSpace
  where
    tryParse' s
      =   MyNumber <$> parseNumber s
      <|> tryParseString s
      <|> tryParseNil s

tryParseNil :: String -> Maybe MyToken
tryParseNil "nil" = Just MyNil
tryParseNil _ = Nothing
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.