diff --git a/parsec.html b/parsec.html index 8c88049..f4f53a3 100644 --- a/parsec.html +++ b/parsec.html @@ -308,9 +308,112 @@ expecting digit or Number between 0lexeme parser = whitespaces *> parser <* whitespaces
-

Data Structure

-

Remember from text to data structure

-
data Tree = Node Element [Tree]
+

Scheme

+

Write Yourself a Scheme in 48 hours

+

Remember from text to data structure. Our data structure:

+
data LispVal =    Atom String
+                | List [LispVal]
+                | DottedList [LispVal] LispVal
+                | Number Integer
+                | String String
+                | Bool Bool
+
+
+

Parse String

+
parseString :: Parser LispVal
+parseString = do
+    char '"'
+    x <- many (noneOf "\"")
+    char '"'
+    return (String x)
+
-- parseString on '"toto"'
+(String "toto") :: LispVal
+-- parseString on '" hello"'
+(String " hello") :: LispVal
+
+
+

Parse Atom

+
symbol :: Parser Char
+symbol = oneOf "!#$%&|*+-/:<=>?@^_~"
+
+parseAtom :: Parser LispVal
+parseAtom = do
+    first <- letter <|> symbol
+    rest <- many (letter <|> digit <|> symbol)
+    let atom = first:rest
+    return $ case atom of
+                "#t" -> Bool True
+                "#f" -> Bool False
+                _    -> Atom atom
+
+
+

Test parseAtom

+
-- parseAtom on '#t'
+(Bool True) :: LispVal
+-- parseAtom on '#f'
+(Bool False) :: LispVal
+-- parseAtom on 'some-atom'
+(Atom "some-atom") :: LispVal
+
+
+

Parse Number

+
parseNumber :: Parser LispVal
+parseNumber = Number . read <$> many1 digit
+
-- parseNumber on '18'
+Number 18 :: LispVal
+-- parseNumber on '188930992344321234'
+Number 188930992344321234 :: LispVal
+
+
+

Compose all parsers

+
parseExpr :: Parser LispVal
+parseExpr = parseAtom
+            <||> parseString
+            <||> parseNumber
+
+
+

Test the parser

+
-- parseExpr on '188930992344321234'
+Number 188930992344321234 :: LispVal
+-- parseExpr on '#t'
+Bool True :: LispVal
+-- parseExpr on 'just-some-word'
+Atom "just-some-word" :: LispVal
+-- parseExpr on '%-symbol-start'
+Atom "%-symbol-start" :: LispVal
+-- parseExpr on '"a String"'
+String "a String" :: LispVal
+
+
+

Recursive Parsers

+
parseList :: Parser LispVal
+parseList = List <$>
+    (char '(' *> sepBy parseExpr' spaces <* char ')' )
+
+parseExpr' :: Parser LispVal
+parseExpr' = parseAtom
+             <||> parseString
+             <||> parseNumber
+             <||> parseList
+
+
+

Test Parse List

+
-- parseExpr' on '(foo (bar baz))'
+List [Atom "foo",List [Atom "bar",Atom "baz"]] :: LispVal
+
+-- parseExpr' on '(foo (bar)'
+"parseExpr'" (line 1, column 11):
+unexpected end of input
+expecting white space, letter, "\"", digit, "(" or ")"
+
+-- parseExpr' on '(((foo)) bar)'
+List [List [List [Atom "foo"]],Atom "bar"] :: LispVal
+
+
+

Conclusion

+

So Parser are more powerful than regular expression.
Parsec make it very easy to use.
Easy to read and to manipulate.

+

Notice how you could use parser as any other object in Haskell. You could mapM them for example.

+

Any question?

diff --git a/parsec/content/.030_advanced.md.swp b/parsec/content/.030_advanced.md.swp deleted file mode 100644 index 2fbb2b6..0000000 Binary files a/parsec/content/.030_advanced.md.swp and /dev/null differ diff --git a/parsec/content/030_advanced.md b/parsec/content/030_advanced.md index 177ab69..0167ff6 100644 --- a/parsec/content/030_advanced.md +++ b/parsec/content/030_advanced.md @@ -25,10 +25,141 @@ Let's write a minimal DSL lexeme parser = whitespaces *> parser <* whitespaces ``` -## Data Structure +## Scheme -Remember from text to data structure +[Write Yourself a Scheme in 48 hours](https://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_Hours) + +Remember from text to data structure. Our data structure: ``` haskell -data Tree = Node Element [Tree] +data LispVal = Atom String + | List [LispVal] + | DottedList [LispVal] LispVal + | Number Integer + | String String + | Bool Bool ``` + +## Parse String + +``` haskell +parseString :: Parser LispVal +parseString = do + char '"' + x <- many (noneOf "\"") + char '"' + return (String x) +``` + +``` +-- parseString on '"toto"' +(String "toto") :: LispVal +-- parseString on '" hello"' +(String " hello") :: LispVal +``` + +## Parse Atom + +``` haskell +symbol :: Parser Char +symbol = oneOf "!#$%&|*+-/:<=>?@^_~" + +parseAtom :: Parser LispVal +parseAtom = do + first <- letter <|> symbol + rest <- many (letter <|> digit <|> symbol) + let atom = first:rest + return $ case atom of + "#t" -> Bool True + "#f" -> Bool False + _ -> Atom atom +``` + +## Test `parseAtom` + +``` +-- parseAtom on '#t' +(Bool True) :: LispVal +-- parseAtom on '#f' +(Bool False) :: LispVal +-- parseAtom on 'some-atom' +(Atom "some-atom") :: LispVal +``` + +## Parse Number + +``` haskell +parseNumber :: Parser LispVal +parseNumber = Number . read <$> many1 digit +``` + +``` +-- parseNumber on '18' +Number 18 :: LispVal +-- parseNumber on '188930992344321234' +Number 188930992344321234 :: LispVal +``` + +## Compose all parsers + +``` haskell +parseExpr :: Parser LispVal +parseExpr = parseAtom + <||> parseString + <||> parseNumber +``` + +## Test the parser + +```` +-- parseExpr on '188930992344321234' +Number 188930992344321234 :: LispVal +-- parseExpr on '#t' +Bool True :: LispVal +-- parseExpr on 'just-some-word' +Atom "just-some-word" :: LispVal +-- parseExpr on '%-symbol-start' +Atom "%-symbol-start" :: LispVal +-- parseExpr on '"a String"' +String "a String" :: LispVal +```` + +## Recursive Parsers + +``` haskell +parseList :: Parser LispVal +parseList = List <$> + (char '(' *> sepBy parseExpr' spaces <* char ')' ) + +parseExpr' :: Parser LispVal +parseExpr' = parseAtom + <||> parseString + <||> parseNumber + <||> parseList +``` + +## Test Parse List + +``` +-- parseExpr' on '(foo (bar baz))' +List [Atom "foo",List [Atom "bar",Atom "baz"]] :: LispVal + +-- parseExpr' on '(foo (bar)' +"parseExpr'" (line 1, column 11): +unexpected end of input +expecting white space, letter, "\"", digit, "(" or ")" + +-- parseExpr' on '(((foo)) bar)' +List [List [List [Atom "foo"]],Atom "bar"] :: LispVal +``` + +## Conclusion + +So Parser are more powerful than regular expression. +Parsec make it very easy to use. +Easy to read and to manipulate. + +Notice how you could use parser as any other object in Haskell. +You could `mapM` them for example. + +Any question? diff --git a/parsec/examples/scheme.hs b/parsec/examples/scheme.hs new file mode 100644 index 0000000..0534665 --- /dev/null +++ b/parsec/examples/scheme.hs @@ -0,0 +1,83 @@ +{-# LANGUAGE DeriveDataTypeable #-} +import Control.Applicative hiding (many, (<|>)) +import Text.Parsec +import Data.Typeable +import Control.Monad (guard) + +type Parser a = Parsec String () a + +data LispVal = Atom String + | List [LispVal] + | DottedList [LispVal] LispVal + | Number Integer + | String String + | Bool Bool + deriving (Show, Typeable) + +parseString :: Parser LispVal +parseString = do + char '"' + x <- many (noneOf "\"") + char '"' + return (String x) + +symbol :: Parser Char +symbol = oneOf "!#$%&|*+-/:<=>?@^_~" + +parseAtom :: Parser LispVal +parseAtom = do + first <- letter <|> symbol + rest <- many (letter <|> digit <|> symbol) + let atom = first:rest + return $ case atom of + "#t" -> Bool True + "#f" -> Bool False + _ -> Atom atom + +parseNumber :: Parser LispVal +parseNumber = Number . read <$> many1 digit + +parseExpr :: Parser LispVal +parseExpr = parseAtom + <||> parseString + <||> parseNumber + +(<||>) parser1 parser2 = try parser1 <|> parser2 + + +parseList :: Parser LispVal +parseList = List <$> + (char '(' *> sepBy parseExpr' spaces <* char ')' ) + +parseExpr' :: Parser LispVal +parseExpr' = parseAtom + <||> parseString + <||> parseNumber + <||> parseList +main :: IO () +main = do + -- test parseString "parseString" "\"toto\"" + -- test parseString "parseString" "\" hello\"" + -- test parseAtom "parseAtom" "#t" + -- test parseAtom "parseAtom" "#f" + -- test parseAtom "parseAtom" "some-atom" + -- test parseNumber "parseNumber" "18" + -- test parseNumber "parseNumber" "188930992344321234" + -- test parseExpr "parseExpr" "188930992344321234" + -- test parseExpr "parseExpr" "#t" + -- test parseExpr "parseExpr" "just-some-word" + -- test parseExpr "parseExpr" "%-symbol-start" + -- test parseExpr "parseExpr" "\"a String\"" + test parseExpr' "parseExpr'" "(foo (bar baz))" + test parseExpr' "parseExpr'" "(foo (bar)" + test parseExpr' "parseExpr'" "(((foo)) bar)" + +test :: (Typeable a, Show a) => Parser a -> String -> String -> IO () +test parser description string = do + putStrLn $ "-- " ++ description ++ " on '" ++ string ++ "'" + let res = parse parser description string + case res of + Left err -> print err + Right value -> putStr $ show value ++ " :: " ++ show (typeOf value) + putStrLn "" +