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\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.