packages feed

nymphaea-0.1: src/Parser.hs

module Parser (parseLSystem, ParsedLSystem (ParsedLS), axiom, productions) where

import Text.ParserCombinators.Parsec
import Data.Ratio
-- import Debug.Trace

data ParsedLSystem = ParsedLS { axiom :: [(String, Rational)],
                                productions :: [(Char,[(String,Rational)])] }

parseLSystem :: String -> String -> Either ParseError ParsedLSystem
parseLSystem axm prductions =
    case parse prods "productions" prductions of
        Left err -> Left err
        Right ps -> case parse rvalue "axiom" axm of
                            Left err -> Left err
                            Right a -> Right (ParsedLS a ps)

-- Example:
--
-- Char {
--   Probability String,
--   ...,
--   Probability String
-- };
-- Char { ... }

-- a ruleset is a set of ';'-separated rules
prods :: GenParser Char st [(Char, [([Char], Rational)])]
prods = semicolonSep production

-- a single production is of the form x -> y where x is a turtle graphics command
-- and y is a string of commands, or a weighted set of them.
production :: GenParser Char st (Char, [([Char], Rational)])
production = do
    from <- lvalue
    symbol "->"
    to <- rvalue
    return (from,to)
  <?> "production"

-- alphabet
alphabet :: [Char]
alphabet = "FG+-[]"

-- the "Char" in "Char { Probability String, .. }"
lvalue :: CharParser st Char
lvalue = oneOf alphabet

-- either a weighted set of productions, or a single production
rvalue :: GenParser Char st [([Char], Rational)]
rvalue = weightedSet turtle <|> fmap (\x -> [(x,1)]) turtle

weightedSet :: GenParser Char st t -> GenParser Char st [(t, Rational)]
weightedSet p =
    inCurlyBraces $ commaSep $ do
        prob <- probability
        symbol ":"
        to <- p
        return (to, prob)

turtle :: GenParser Char st [Char]
turtle = between spaces spaces $ many1 (oneOf alphabet)

-- Probability could be a decimal (0.5) or a rational (1/2)
probability :: GenParser Char st Rational
probability = (try decimal) <|> rational <?> "probability"
  where decimal = do a <- many1 digit
                     char '.'
                     b <- many digit
                     return $ approxRational (read $ (a ++ "." ++ b) :: Double) 10^(- length b)
        rational = do a <- many1 digit
                      b <- (do char '/'; many1 digit) <|> return "1"
                      return $ read (a ++ "%" ++ b)

symbol :: String -> GenParser Char st String
symbol s = between spaces spaces (string s)

semicolonSep :: GenParser Char st a -> GenParser Char st [a]
semicolonSep p = p `sepBy` symbol ";"

commaSep :: GenParser Char st a -> GenParser Char st [a]
commaSep p = p `sepBy` symbol ","

inCurlyBraces :: GenParser Char st a -> GenParser Char st a
inCurlyBraces p = between (symbol "{") (symbol "}") p