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