G'day all.

Quoting Achim Schneider <[EMAIL PROTECTED]>:

Considering that he's talking about a mud, I figure the grammar is a
quite straightforward

command = l[eft] | r[ight] | ... | t[ake] <item> | c[ast] <spell>

That is, I'd be very surprised if you even need more than two or three
characters lookahead, much less backtracking.

In the case of a command followed by arguments, it would make more
sense to use a keyword recogniser followed by a command-specific parser.

One suggestion follows.

Cheers,
Andrew Bromage
--------8<---CUT HERE---8<--------
module KeywordMatch (keywordMatch) where

import Data.List
import Data.Function
import Control.Arrow

-- Exercise: Why would it be wrong to curry this function?
keywordMatch :: (Ord k) => [([k],v)] -> [k] -> Maybe v
keywordMatch kvs
    = compileTrie . generateTrie . sortBy (compare `on` fst) $ kvs

data Trie k v
    = Trie (Maybe v) (Trie' k v)

data Trie' k v
    = Node0
    | Node1 k (Trie k v)
    | Node2 k (Trie k v) k (Trie k v)
    | Branch k (Trie' k v) (Trie k v) (Trie' k v)

generateTrie :: (Ord k) => [([k],v)] -> Trie k v
generateTrie (([],v):rest)
    = Trie (Just v) (generateTrie' rest)
generateTrie rest
    = Trie Nothing (generateTrie' rest)

generateTrie' :: (Ord k) => [([k],v)] -> Trie' k v
generateTrie' []
    = Node0
generateTrie' [(k:ks,v)]
    = Node1 k $ foldr (\k -> Trie Nothing . Node1 k) (Trie (Just v) Node0) ks
generateTrie' [(k1:ks1,v1),(k2:ks2,v2)]
    = Node2 k1 (generateTrie [(ks1,v1)]) k2 (generateTrie [(ks2,v2)])
generateTrie' kvs
    = gt . map (head.fst.head &&& map (first tail))
            . groupBy ((==) `on` head.fst) $ kvs
    where
        gt [] = Node0
        gt [(k,kvs)] = Node1 k (generateTrie kvs)
        gt [(k1,kvs1),(k2,kvs2)] = Node2 k1 (generateTrie kvs1)
                                         k2 (generateTrie kvs2)
        gt kvs
            = let (l,(k,m):r) = splitAt (length kvs `div` 2) kvs
              in Branch k (gt l) (generateTrie m) (gt r)

compileTrie :: (Ord k) => Trie k v -> [k] -> Maybe v
compileTrie (Trie emptyCase trie')
    = let ctrie' = compileTrie' trie'
      in \key -> case key of
                    [] -> emptyCase
                    (k:ks) -> ctrie' k ks

compileTrie' :: (Ord k) => Trie' k v -> k -> [k] -> Maybe v
compileTrie' Node0
    = \k ks -> Nothing
compileTrie' (Node1 k' t)
    = let t' = compileTrie t
      in \k ks -> if k == k' then t' ks else Nothing
compileTrie' (Node2 k1 t1 k2 t2)
    = let t1' = compileTrie t1
          t2' = compileTrie t2
      in \k ks -> if k == k1 then t1' ks
                  else if k == k2 then t2' ks
                  else Nothing
compileTrie' (Branch k' l m r)
    = let
        cl = compileTrie' l
        cm = compileTrie m
        cr = compileTrie' r
      in
        \k ks -> case compare k k' of
                    LT -> cl k ks
                    EQ -> cm ks
                    GT -> cr k ks

-- vim: ts=4:sts=4:expandtab
_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to