module Parser
( consult, consultString, parseQuery
, program, whitespace, comment, clause, terms, term, bottom, vname
) where
import Text.Parsec
import Text.Parsec.Expr hiding (Assoc(..))
import qualified Text.Parsec.Expr as Parsec
import qualified Text.Parsec.Token as P
import Text.Parsec.Language (emptyDef)
import Control.Applicative ((<$>),(<*>),(<$),(<*))
import Syntax
consult = fmap consultString . readFile
consultString :: String -> Either ParseError Program
consultString = parse (whitespace >> program <* eof) "(input)"
parseQuery = parse (whitespace >> terms <* eof) "(query)"
program = many (clause <* char '.' <* whitespace)
whitespace = skipMany (comment <|> skip space <?> "")
comment = skip $ choice
[ string "/*" >> (manyTill anyChar $ try $ string "*/")
, char '%' >> (manyTill anyChar $ try $ skip newline <|> eof)
]
skip = (>> return ())
clause = do t <- struct <* whitespace
dcg t <|> normal t
where
normal t = do
ts <- option [] $ do string ":-" <* whitespace
terms
return (Clause t ts)
dcg t = do
string "-->" <* whitespace
ts <- terms
return (translate (t,ts))
translate ((Struct a ts), rhs) =
let lhs' = Struct a (arguments ts (head vars) (last vars))
vars = map (var.("d_"++).(a++).show) [0..length rhs]
rhs' = zipWith3 translate' rhs vars (tail vars)
in Clause lhs' rhs'
translate' t s s0 | isList t = Struct "=" [ s, foldr_pl cons s0 t ]
translate' t@(Struct "{}" ts) s s0 = foldr and (Struct "=" [ s, s0 ]) ts
translate' (Struct a ts) s s0 = Struct a (arguments ts s s0)
and x y = Struct "," [x,y]
isList (Struct "." [_,_]) = True
isList (Struct "[]" []) = True
isList _ = False
terms = sepBy1 termWithoutConjunction (charWs ',')
term = term' False
termWithoutConjunction = term' True
term' ignoreConjunction = buildExpressionParser (reverse $ map (map toParser) $ hierarchy ignoreConjunction) (bottom <* whitespace)
bottom = variable
<|> struct
<|> list
<|> stringLiteral
<|> cut <$ char '!'
<|> Struct "{}" <$> between (charWs '{') (char '}') terms
<|> between (charWs '(') (char ')') term
toParser (PrefixOp name) = Prefix (reservedOp name >> return (\t -> Struct name [t]))
toParser (InfixOp assoc name) = Infix (reservedOp name >> return (\t1 t2 -> Struct name [t1, t2]))
(case assoc of AssocLeft -> Parsec.AssocLeft
AssocRight -> Parsec.AssocRight)
reservedOp = P.reservedOp $ P.makeTokenParser $ emptyDef
{ P.opStart = oneOf ";,<=>\\i*+m@"
, P.opLetter = oneOf "=.:<+"
, P.reservedOpNames = operatorNames
, P.caseSensitive = True
}
charWs c = char c <* whitespace
operatorNames = [ ";", ",", "<", "=..", "=:=", "=<", "=", ">=", ">", "\\=", "is", "*", "+", "-", "\\", "mod", "div", "\\+" ]
variable = (Wildcard <$ try (char '_' <* notFollowedBy (alphaNum <|> char '_')))
<|> Var <$> vname
<?> "variable"
vname = VariableName 0 <$> ((:) <$> upper <*> many (alphaNum <|> char '_') <|>
(:) <$> char '_' <*> many1 (alphaNum <|> char '_'))
atom = (:) <$> lower <*> many (alphaNum <|> char '_')
<|> many1 digit
<|> choice (map string operatorNames)
<|> many1 (oneOf "#$&*+/.<=>\\^~")
<|> between (char '\'') (char '\'') (many (noneOf "'"))
<?> "atom"
struct = do a <- atom
ts <- option [] $ between (charWs '(') (char ')') terms
return (Struct a ts)
list = between (charWs '[') (char ']') $
flip (foldr cons) <$> option [] terms
<*> option nil (charWs '|' >> term)
stringLiteral = foldr cons nil . map representChar <$> between (char '"') (char '"') (try (many (noneOf "\"")))
representChar c = Struct (show (fromEnum c)) []