module Narc (
NarcTerm,
narcTermToSQL,
unit, Const, primApp, abs, app, ifthenelse, singleton,
nil, union, record, project, foreach, having
) where
import Prelude hiding (abs, catch)
import Control.Exception (catch, throwIO, evaluate, SomeException)
import Control.Monad.State hiding (when, join)
import Control.Monad.Error (throwError, runErrorT, Error(..))
import Data.List (nub, (\\), sort, sortBy, groupBy, intersperse)
import Data.Maybe (fromJust, isJust, fromMaybe)
import Control.Applicative ((<$>), (<*>))
import Foreign (unsafePerformIO)
import Test.QuickCheck hiding (promote, Failure)
import QCUtils
import Test.HUnit hiding (State, assert)
import Debug.Trace
import Gensym
import Narc.AST
import Narc.Common
import Narc.Compile
import Narc.Debug
import Narc.Eval
import Narc.Failure
import Narc.Pretty
import Narc.AST.Pretty
import Narc.SQL.Pretty
import qualified Narc.SQL as SQL
import Narc.Type as Type
import Narc.TypeInfer
import Narc.Util
import Narc.HDBC
typeCheckAndCompile :: Term a -> SQL.Query
typeCheckAndCompile = compile [] . runTyCheck []
example_dull = (Comp "x" (Table "foo" [("a", TBool)], ())
(If (Project (Var "x", ()) "a", ())
(Singleton (Var "x", ()), ())
(Nil, ()), ()), ())
type NarcTerm = Gensym (Term ())
narcTermToSQLString :: NarcTerm -> String
narcTermToSQLString = SQL.serialize . narcTermToSQL
narcTermToSQL :: NarcTerm -> SQL.Query
narcTermToSQL = typeCheckAndCompile . realize
realize :: NarcTerm -> Term ()
realize = runGensym
unit :: NarcTerm
unit = return $ (!) Unit
class Const' a where cnst' :: a -> NarcTerm
instance Const' Bool where cnst' b = return ((!)(Bool b))
instance Const' Integer where cnst' n = return ((!)(Num n))
primApp :: String -> [NarcTerm] -> NarcTerm
primApp f args = (!) . PrimApp f <$> sequence args
abs :: (String -> NarcTerm) -> NarcTerm
abs fn = do
n <- gensym
let x = '_' : show n
body <- fn x
return $ (!) $ Abs x body
app :: NarcTerm -> NarcTerm -> NarcTerm
app l m = (!) <$> (App <$> l <*> m)
table :: Tabname -> [(Field, Type)] -> NarcTerm
table tbl ty = return $ (!) $ Table tbl ty
ifthenelse :: NarcTerm -> NarcTerm -> NarcTerm -> NarcTerm
ifthenelse c t f = (!) <$> (If <$> c <*> t <*> f)
singleton :: NarcTerm -> NarcTerm
singleton x = (!) . Singleton <$> x
nil :: NarcTerm
nil = return $ (!) $ Nil
union :: NarcTerm -> NarcTerm -> NarcTerm
union l r = (!) <$> (Union <$> l <*> r)
record :: [(String, NarcTerm)] -> NarcTerm
record fields = (!) <$> (Record <$> sequence [do expr' <- expr ; return (lbl, expr') | (lbl, expr) <- fields])
project :: NarcTerm -> String -> NarcTerm
project expr field = (!) <$> (Project <$> expr <*> return field)
foreach :: NarcTerm -> (NarcTerm -> NarcTerm) -> NarcTerm
foreach src k = do
src' <- src
n <- gensym
let x = '_' : show n
body' <- k (return (var_ x))
return $ (!)(Comp x src' body')
having :: NarcTerm -> NarcTerm -> NarcTerm
having cond body = ifthenelse cond body nil
example' = let t = (table "foo" [("a", TBool)]) in
foreach t $ \x ->
(having (project x "a")
(singleton x))
example2' = let t = (table "foo" [("a", TNum)]) in
let s = (table "bar" [("a", TNum)]) in
foreach t $ \x ->
foreach s $ \y ->
ifthenelse (primApp "<" [project x "a", project y "a"])
(singleton x)
(singleton y)
example3' =
let t = table "employees" [("name", TString), ("salary", TNum)] in
foreach t $ \emp ->
having (primApp "<" [cnst' (20000::Integer), project emp "salary"]) $
singleton (record [("nom", project emp "name")])
test_example =
TestList [
SQL.serialize (typeCheckAndCompile (realize example'))
~?= "select _0.a as a from foo as _0 where _0.a"
,
SQL.serialize (typeCheckAndCompile (realize example2'))
~?= "(select _0.a as a from foo as _0, bar as _1 where _0.a < _1.a) union (select _1.a as a from foo as _0, bar as _1 where not(_0.a < _1.a))"
,
SQL.serialize (typeCheckAndCompile (realize example3'))
~?= "select _0.name as nom from employees as _0 where 20000 < _0.salary"
]