Personal tools

Embedding context free grammars

From HaskellWiki

(Difference between revisions)
Jump to: navigation, search
Line 1: Line 1:
 
Here's how to embed a context free grammar parser into haskell:
 
Here's how to embed a context free grammar parser into haskell:
 
<haskell>
 
<haskell>
data Grammar b where
+
Ref :: Grammar c
+
Tok :: Char -> Grammar c
+
import Maybe
Check :: (Char -> Bool) -> Grammar c
+
data Grammar a b where
Fix :: Grammar a -> Grammar c
+
NullParser :: Grammar a b
(:|) :: Grammar b -> Grammar b -> Grammar b
+
Check :: (a -> Bool) -> Grammar a a
(:&) :: Grammar b -> Grammar b -> Grammar b
+
(:|) :: (Grammar a b) -> (Grammar a b) -> Grammar a b
Push :: Char -> Grammar a -> Grammar a
+
Push :: a -> (Grammar a b) -> Grammar a b
NullParser :: Grammar b
+
(:&) :: (Grammar a b) -> (Grammar a c) -> Grammar a (b,c)
  +
FMap :: Grammar a c -> (c -> b) -> Grammar a b
  +
  +
 
infixl 6 :|
 
infixl 6 :|
infixl 7 :&
+
infixl 6 :&
   
   
parse:: [Char] -> Grammar b -> Grammar b -> Bool
 
parse x Ref t = parse x t t
 
parse [c] (Tok c') _ = c == c'
 
parse [c] (Check y) _ = y c
 
parse _ (Tok _) _ = False
 
parse _ (Check _) _ = False
 
parse x (Fix g) _ = parse x g (Fix g)
 
parse x (g :| g') t = parse x g t || parse x g' t --cool little trick!
 
parse (x:xs) (g :& g') t = parse xs ((Push x g) :& g') t || (parse [] g t && parse (x:xs) g' t)
 
parse x (Push c g) t = parse (c:x) g t
 
parse _ NullParser _ = False
 
parse [] _ _ = False
 
   
parse' x g = parse x g NullParser
+
tok x = Check (x==)
</haskell>
 
   
and here's a lambda calculus parser written in this embedded language
+
parse :: [a] -> Grammar a b -> Maybe b
<haskell>
+
parse [c] (Check y) = if y c then Just c else Nothing
var = Check (\x -> x <= 'z' && x >= 'a')
+
parse x (g :| g') =
app = term :& term
+
let
term = var :| abstraction :| parenedTerm
+
r1 = parse x g
parenedTerm = Tok '(' :& term :& Tok ')'
+
r2 = parse x g'
abstraction = Tok '\\' :& var :& Tok '.' :& term
+
in
top = term :& Tok ';'
+
if isJust r1 then r1 else r2
</haskell>
 
   
let's see the results
+
parse (x:xs) (g :& g') =
<haskell>
+
let
print $ parse' "\\x.x;" top
+
r1 = parse xs ((Push x g) :& g')
> True
+
r2 = parse [] g
</haskell>
+
r3 = parse (x:xs) g'
  +
in
  +
if isJust r1
  +
then r1
  +
else
  +
if (isJust r2) && (isJust r3)
  +
then Just (fromJust r2, fromJust r3)
  +
else Nothing
  +
parse x (Push c g) = parse (c:x) g
  +
parse x (FMap y f) =
  +
let
  +
r1 = parse x y
  +
in
  +
if isJust r1 then
  +
let r2 = fromJust r1
  +
in
  +
Just (f r2) else Nothing
  +
parse _ _ = Nothing
   
Let's check out a recursive grammar of all strings containing only c's of length at least 1
 
   
<haskell>
+
top' = Fix ((Tok 'c' :& Ref) :| Tok 'c')
+
print $ parse' "cccccc" top'
+
> True
+
  +
  +
infixl 7 ~&
  +
infixl 7 ~&&
  +
infixl 7 ~&&&
  +
infixl 7 ~&&&&
  +
  +
(~&) = (:&)
  +
  +
a ~&& b = FMap
  +
(a :& b)
  +
(\((a,b),c) -> (a,b,c))
  +
  +
a ~&&& b = FMap
  +
(a :& b)
  +
(\((a,b,c),d) -> (a,b,c,d))
  +
  +
a ~&&&& b = FMap
  +
(a :& b)
  +
(\((a,b,c,d),e) -> (a,b,c,d,e))
  +
  +
a ~&&&&& b = FMap
  +
(a :& b)
  +
(\((a,b,c,d,e),f) -> (a,b,c,d,e,f))
  +
  +
  +
  +
data Term = Var Char | App Term Term | Abs Char Term deriving Show
  +
  +
var =
  +
(Check (\x -> x <= 'z' && x >= 'a'))
  +
  +
app = term ~& term
  +
 
</haskell>
 
</haskell>
   
Grammar for even parentheses surrounding the letter c
+
And here's a lambda calculus parser:
   
 
<haskell>
 
<haskell>
top' = Fix (Tok '(' :& Ref :& Tok ')' :| Tok 'c')
+
print $ parse' "(((c)))" top'
+
term = FMap var Var :| abstraction :| parenedTerm
> True
+
  +
parenedTerm = FMap
  +
(tok '(' ~& term ~&& tok ')')
  +
(\(a,b,c) -> b)
  +
  +
abstraction = FMap
  +
(tok '\\' ~& var ~&& tok '.' ~&&& term)
  +
(\(a,b,c,d) -> Abs b d)
  +
top = FMap
  +
(term ~& tok ';')
  +
fst
  +
  +
main = print $ parse "\\x.x;" top
  +
 
</haskell>
 
</haskell>

Revision as of 19:49, 21 November 2006

Here's how to embed a context free grammar parser into haskell:

import Maybe
data Grammar a b where
    NullParser ::  Grammar a b
    Check :: (a -> Bool) -> Grammar a a
    (:|) :: (Grammar  a b) -> (Grammar  a b) -> Grammar a b
    Push :: a ->  (Grammar  a b) -> Grammar a b
    (:&) :: (Grammar  a b) -> (Grammar  a c) -> Grammar a (b,c)
    FMap :: Grammar  a c -> (c -> b) -> Grammar  a b
 
 
infixl 6 :|
infixl 6 :&
 
 
 
tok x = Check (x==)
 
parse :: [a] -> Grammar a b -> Maybe b
parse [c] (Check y) = if y c then Just c else Nothing
parse x (g :| g') = 
    let 
        r1 = parse x g 
        r2 = parse x g' 
    in
        if isJust r1 then r1 else r2
 
parse (x:xs) (g :& g') = 
     let 
         r1 = parse xs ((Push x g) :& g') 
         r2 = parse [] g 
         r3 = parse (x:xs) g'
     in
       if isJust r1 
       then r1 
       else 
           if (isJust r2) && (isJust r3) 
           then Just (fromJust r2, fromJust r3)
           else Nothing
parse x (Push c g) = parse (c:x) g
parse x (FMap y f) = 
    let
        r1 = parse x y
    in
      if isJust r1 then 
          let r2 = fromJust r1
          in
            Just (f r2) else Nothing
parse _ _ = Nothing
 
 
 
 
 
 
 
infixl 7 ~&
infixl 7 ~&&
infixl 7 ~&&&
infixl 7 ~&&&&
 
(~&) = (:&)
 
a ~&& b = FMap 
          (a :& b)
          (\((a,b),c) -> (a,b,c))
 
a ~&&& b = FMap 
           (a :& b)
           (\((a,b,c),d) -> (a,b,c,d))
 
a ~&&&& b = FMap 
            (a :& b)
            (\((a,b,c,d),e) -> (a,b,c,d,e))
 
a ~&&&&& b = FMap 
             (a :& b)
             (\((a,b,c,d,e),f) -> (a,b,c,d,e,f))
 
 
 
data Term = Var Char | App Term Term | Abs Char Term deriving Show
 
var = 
    (Check (\x -> x <= 'z' && x >= 'a'))
 
app = term ~& term

And here's a lambda calculus parser:

term = FMap var Var :| abstraction :| parenedTerm  
 
parenedTerm = FMap 
              (tok '(' ~& term ~&& tok ')')
              (\(a,b,c) -> b)
 
abstraction = FMap 
              (tok '\\' ~& var ~&& tok '.' ~&&& term)
              (\(a,b,c,d) -> Abs b d)
top = FMap 
      (term ~& tok ';')
      fst
 
main = print $ parse "\\x.x;" top