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\""
200_success
145k22 gold badges190 silver badges478 bronze badges
asked Jun 6, 2022 at 14:04
\$\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
answered Jun 7, 2022 at 11:59
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.