diff --git a/Eval.hs b/Eval.hs index a081f997b22b268a621178fcc8651fa5dae7a597..5e1e941b3c27ea0b03b2e1d35438d86aeb20ecf4 100644 --- a/Eval.hs +++ b/Eval.hs @@ -34,12 +34,18 @@ eval env expr = let (eval', evalFull') = (eval env, evalFull env) in case expr o [Set l1, Set l2] -> Set $ [ x `concatRecord` y | x <- l1, y <- l2] _ -> error "X product takes in two sets" + (ComparisonFunc op) -> case args' of + [e1, e2] -> Boolean $ e1 `op` e2 + _ -> error "Comparison must take in two arguments" + (BooleanFunc op) -> case args' of [Boolean e1, Boolean e2] -> Boolean (e1 `op` e2) - _ -> error "Boolean function takes in two arguments." + _ -> error "Boolean function AND/OR takes in two arguments." - RecordIndex -> eval' $ case args' of + RecordIndex -> eval' $ case args' of --TODO rename [Record recordData,Int index] -> recordData `listAtIndex` index + [String stringData,Int index] -> String [stringData `listAtIndex` index] + RecordSelect -> case args' of (Record recordData: indexes ) -> Record filteredList where @@ -48,31 +54,52 @@ eval env expr = let (eval', evalFull') = (eval env, evalFull env) in case expr o filteredList = map (eval'.listAtIndex recordData) indexesRaw checkInt (Int i) = i checkInt _ = error "Arguments to index record must be integers" + + (String stringData: indexes ) -> String filteredList + where + indexesRaw = map checkInt indexes + --filteredList = map (eval'.(stringData !!)) indexesRaw + filteredList = map (listAtIndex stringData) indexesRaw + checkInt (Int i) = i + checkInt _ = error "Arguments to index list must be integers" - + {-numberedElems = zip [1..] recordData :: [(Int, Expr)] filtered = filter ((`elem` indexesRaw).fst) numberedElems :: [(Int, Expr)] filteredList = map snd filtered :: [Expr]-} + + IsEmpty -> case head args' of (String a) -> Boolean $ null a - _ -> error "IsEmpty operates on a string" + _ -> error "'isEmpty' operates on a string" NotEmpty -> case head args' of (String a) -> Boolean $ (not.null) a - _ -> error "notEmpty operates on a string" + _ -> error "'notEmpty' operates on a string" Contains -> case args' of [String a, String b] -> Boolean $ b `isSubList` a - _ -> error "Arguments to 'Contains' must be two strings." + _ -> error "Arguments to 'contains' must be two strings." Plus -> case args' of [String a, String b] -> String (a ++ b) [Record a, Record b] -> Record (a ++ b) _ -> error "Arguments to '+' must either be two strings or two records" + Split -> case args' of + [String a, Int i] -> if (i <= length a) then splitRecord else error "Splitat: index outside of range" + where + splitRecord = let (s1, s2) = splitAt i a in Record [String s1, String s2] + _ -> error "'split' argument error" + Not -> case args' of [Boolean exp1] -> Boolean $ not exp1 + + Length -> case args' of + [String a] -> Int $ length a + _ -> error "'length' only operates on a String" + (FuncDef setParams argParams body) -> eval newEnv body where newEnv = let (setEnv, argsEnv) = (zip setParams inputSets, zip argParams args) in setEnv ++ argsEnv ++ env diff --git a/Interpreter.hs b/Interpreter.hs index 23e478635e9e769b3b48abd758cfefbe5b4ca3bf..e090b509adce88c21d92576d9d5ea9d84fd4f6eb 100644 --- a/Interpreter.hs +++ b/Interpreter.hs @@ -1,13 +1,12 @@ +module Interpreter where import Types import Debug import Eval import System.IO -import System.Environment import CSV import Lexer import Parser import Debug.Trace -import Control.Exception printErr s = hPutStrLn stderr ("[ERROR] " ++ s) @@ -15,16 +14,15 @@ parse :: String -> Program parse = parseSource.alexScanTokens --outline -main = do - args <- getArgs - case args of - --("default":_) -> catch (interpret "/home/patryk/dev/plc/extra-problems/ex1.cql") (errorFunc) - (srcname:_) -> catch (interpret srcname) (errorFunc) +--main = do +-- args <- getArgs +-- case args of +-- --("default":_) -> catch (interpret "/home/patryk/dev/plc/extra-problems/ex1.cql") (errorFunc) +-- (srcname:_) -> catch (interpret srcname) (errorFunc) --_ -> printErr "No source file specified." - _ -> catch (interpret "/home/patryk/dev/plc/extra-problems/ex1.cql") (errorFunc) +-- _ -> catch (interpret "/home/patryk/dev/plc/extra-problems/ex1.cql") (errorFunc) + -errorFunc :: ErrorCall -> IO () -errorFunc = printErr.show interpret :: FilePath -> IO () -- the main function, takes in file name, prints out result @@ -55,7 +53,7 @@ showFinal :: Expr -> IO () showFinal = (print2DList.sort2DListLex.setTo2DList) setTo2DList :: Expr -> [[String]] -setTo2DList (Set records) = traceShow records $ map (map (\(String s) -> s).(\(Record list) -> list)) records +setTo2DList (Set records) = map (map (\(String s) -> s).(\(Record list) -> list)) records -------------------------------------------- diff --git a/Lexer.x b/Lexer.x index 1cab7571e7ce16a73be0c93358d4a0f99d13ab28..e07ab8da2079029895e5f4a87c9a1944423201f1 100644 --- a/Lexer.x +++ b/Lexer.x @@ -26,6 +26,8 @@ let {\p s -> TokenLet p } if {\p s -> TokenIf p } else {\p s -> TokenElse p } then {\p s -> TokenThen p } +and {\p s -> TokenBoolAND p } +or {\p s -> TokenBoolOR p } \.in {\p s -> TokenInSet p } \.out {\p s -> TokenOutSet p } \[ {\p s -> TokenLeftSqBracket p } @@ -34,9 +36,9 @@ then {\p s -> TokenThen p } \} {\p s -> TokenRightBrace p } "->" {\p s -> TokenArrow p } "==" {\p s -> TokenisEqual p } -"/=" {\p s -> TokenisNotEqual p } +"!=" {\p s -> TokenisNotEqual p } "+" {\p s -> TokenPlus p } -\= {\p s -> TokenEqual p } +\= {\p s -> TokenEqual p } \( {\p s -> TokenLeftBracket p } \) {\p s -> TokenRightBracket p } \: {\p s -> TokenCol p } @@ -47,13 +49,23 @@ then {\p s -> TokenThen p } x {\p s -> TokenXProduct p } xx {\p s -> TokenXXProduct p } map {\p s -> TokenMap p } +length {\p s -> TokenLen p } +\! {\p s -> TokenWithout p } +split {\p s -> TokenSplit p } +reverse {\p s -> TokenReverse p } +\> {\p s -> TokenGthan p } +\< {\p s -> TokenLthan p } $lower [$lower $digit \_ \']* {\p s -> TokenVarName p s } $upper[$alpha $digit \_ \']* {\p s -> TokenSetName p s } --$posDigit$digit* {\p s -> TokenPosNat p (read s) } $digit+ {\p s -> TokenNat p (read s) } \"[$alpha $digit]+\" {\p s -> TokenString p (init.tail $ s) } -AND {\p s -> TokenBoolAND p } -OR {\p s -> TokenBoolOR p } +isSublist {\p s -> TokenIsSubList p } +startswith {\p s -> TokenStartsWith p } +union {\p s -> TokenUnion p } +i'section {\p s -> TokenISection p } +difference {\p s -> TokenDifference p } + { @@ -96,7 +108,20 @@ data Token = TokenThen AlexPosn | TokenEqual AlexPosn | TokenBoolAND AlexPosn | - TokenBoolOR AlexPosn + TokenBoolOR AlexPosn | + TokenLen AlexPosn | + TokenWithout AlexPosn | + TokenSplit AlexPosn | + TokenReverse AlexPosn | + TokenGthan AlexPosn | + TokenLthan AlexPosn | + TokenIsSubList AlexPosn | + TokenConcat AlexPosn | + TokenStartsWith AlexPosn | + TokenUnion AlexPosn | + TokenISection AlexPosn | + TokenDifference AlexPosn + deriving (Eq, Show) @@ -138,4 +163,17 @@ pos token = case token of (TokenIf p) -> p (TokenThen p) -> p (TokenEqual p) -> p + (TokenLen p) -> p + (TokenWithout p) -> p + (TokenSplit p) -> p + (TokenReverse p) -> p + (TokenGthan p) -> p + (TokenLthan p) -> p + (TokenIsSubList p) -> p + (TokenStartsWith p) -> p + (TokenConcat p) -> p + (TokenUnion p) -> p + (TokenISection p) -> p + (TokenDifference p) -> p + } \ No newline at end of file diff --git a/Main.hs b/Main.hs new file mode 100644 index 0000000000000000000000000000000000000000..bee36a843f7b36930fccdd94facd5c303607c98d --- /dev/null +++ b/Main.hs @@ -0,0 +1,20 @@ +module Main where + +import Interpreter +import Control.Exception +import System.Environment + + + +main :: IO () + + +main = do + args <- getArgs + case args of + --("default":_) -> catch (interpret "/home/patryk/dev/plc/extra-problems/ex1.cql") (errorFunc) + (srcname:_) -> catch (interpret srcname) (errorFunc) + _ -> printErr "No source file specified." + +errorFunc :: ErrorCall -> IO () +errorFunc = printErr.show \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..fef60a0feab7c8c58800eb3a0fbd49c31bc9ff4d --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +build: + alex Lexer.x + happy Parser.y + ghc -dynamic Main.hs + mv Main csvql diff --git a/Parser.y b/Parser.y index 39c3dafaac67d43faa0971b7b4828fbd9c2ac65a..29a8cf840eccf61cc3fd0145b6b1259480635ff5 100644 --- a/Parser.y +++ b/Parser.y @@ -27,7 +27,7 @@ import CSV ']' { TokenRightSqBracket _ } "->" { TokenArrow _ } "==" { TokenisEqual _ } - "/=" { TokenisNotEqual _ } + "!=" { TokenisNotEqual _ } '(' { TokenLeftBracket _ } ')' { TokenRightBracket _ } ';' { TokenSemiCol _ } @@ -50,18 +50,33 @@ import CSV else { TokenElse _} then { TokenThen _} '=' { TokenEqual _} - or { TokenBoolAND _ } - and { TokenBoolOR _ } + and { TokenBoolAND _ } + or { TokenBoolOR _ } + length { TokenLen _} + '!' { TokenWithout _} + split {TokenSplit _} + reverse {TokenReverse _} + '>' {TokenGthan _} + '<' {TokenLthan _} + isSublist {TokenIsSubList _ } + startswith {TokenStartsWith _} + union {TokenUnion _ } + --i'section {TokenISection _ } + difference {TokenDifference _ } + -%left FUNCCALL -%right INDEX -%right map filter FUNCCALL -%left '+' -%left '==' +%left "->" %left or %left and +%nonassoc '<' '>'"==" +%left '+' +%left Str +%left FUNCCALL +%left PAREN +%left '[' '(' '{' +%nonassoc ';' %% @@ -72,21 +87,25 @@ SetDecls : SetDecl {[$1]} | SetDecls','SetDecl {$3:$1} Instructions : Expr ';' {[$1]} - | Expr';' Instructions {$1:$3} + | Expr ';' Instructions {$1:$3} Expr : - Expr '('Exprs')' %prec FUNCCALL {FuncCall $1 [] $3} - | Expr '{' Exprs '}' '('Exprs')' {FuncCall $1 $3 $6} - | Expr "==" Expr {FuncCall (PredefFunc IsEqual) [] [$1, $3]} + Expr '('Exprs')' %prec FUNCCALL {FuncCall $1 [] $3} + | Expr '{' Exprs '}' '('Exprs')' %prec FUNCCALL {FuncCall $1 $3 $6} + | Expr "==" Expr {compareExpr (==) $1 $3} + | Expr "!=" Expr {compareExpr (/=) $1 $3} + | Expr '>' Expr {compareExpr (>) $1 $3} + | Expr '<' Expr {compareExpr (<) $1 $3} -- | Expr "/=" Expr {FuncCall (PredefFunc IsNotEqual) [] [$1, $3]} | Expr x Expr {FuncCall (PredefFunc XProduct) [$1, $3] []} - | Expr '+' Expr {FuncCall (PredefFunc Plus) [$1, $3] []} - | Expr'['Expr']' %prec INDEX {FuncCall (PredefFunc RecordIndex) [] [$1, $3]} - | Expr'['Expr','Exprs']' {FuncCall (PredefFunc RecordSelect) [] ($1:$3:$5) } - | '['Exprs']' {Record $2} - | Str {Types.String $ stripWhitespace $1} - | '\\' '(' VarNames ')' "->" Expr { FuncDef [] $3 $6 } - | if '('Expr')' then Expr else Expr {If $3 $6 $8} + | Expr '+' Expr {FuncCall (PredefFunc Plus) [] [$1, $3]} + | Expr'['Expr']' {FuncCall (PredefFunc RecordIndex) [] [$1, $3]} + | Expr'['Expr','Exprs']' {FuncCall (PredefFunc RecordSelect) [] ($1:$3:$5) } + | '['Exprs']' {Record $2} + | '('Expr')' %prec PAREN {$2} + | Str {Types.String $ stripWhitespace $1} + | '\\' '(' VarNames ')' "->" Expr { FuncDef [] $3 $6 } + | if '('Expr')' then Expr else Expr {If $3 $6 $8} | let SetName '=' Expr {Let True $2 $4} | let VarName '=' Expr {Let False $2 $4} | VarName {Var $1} @@ -96,14 +115,21 @@ Expr : | PredefFunc {PredefFunc $1} | Expr or Expr {booleanExpr (||) $1 $3} | Expr and Expr {booleanExpr (&&) $1 $3} +-- | length Expr {FuncCall (PredefFunc length) [] [$2]} +-- | Expr contains Expr {FuncCall (PredefFunc contains) [] [$1, $3]} +-- | Expr '!' Expr {FuncCall (PredefFunc without) [] [1, $3]} +-- | split Expr Expr PredefFunc : isEmpty {IsEmpty} | filter {Filter} | contains {Contains} | isEmpty {IsEmpty} + | length {Length} + | split {Split} -- | nEmpty {IsEmpty} | map {Map} | not {Not} +-- | without {Without} -- | and {And} -- | or {Or} -- | zip {Zip} diff --git a/Types.hs b/Types.hs index 9e5d4bc63b6fbfdda69415d9187cc54b7e1e0103..473898f9046ec79ad4ac2c6c652ee600f6e69920 100644 --- a/Types.hs +++ b/Types.hs @@ -12,9 +12,11 @@ data PredefFunc = XProduct | XXProduct | IsEqual | IsNotEqual | Plus --operators | RecordIndex -- [] operator | RecordSelect | IsEmpty | NotEmpty | Contains -- string functions + | Split | Length | Not | BooleanFunc (Bool -> Bool -> Bool) + | ComparisonFunc (Expr -> Expr -> Bool) --TODO others:reverse instance Show PredefFunc where show XProduct = "XProduct" @@ -32,6 +34,8 @@ instance Show PredefFunc where show Length = "Length" show Not = "Not" show (BooleanFunc _) = "BooleanFunc" + show (ComparisonFunc _) = "CompFunc" + show (_) = "Unknown" --n.b. these definitions do not enforce type checking! The use of any function or operator will be associated with a FuncCall. @@ -58,6 +62,7 @@ data Expr = Control Expr [Expr] -- result of last computation, sequence of instr -- | Nat Int deriving (Show) booleanExpr func e1 e2 = FuncCall (PredefFunc $ BooleanFunc func) [] [e1,e2] +compareExpr func e1 e2 = FuncCall (PredefFunc $ ComparisonFunc func) [] [e1,e2] instance Eq Expr where --(Record r1) == (Record r2) = and (zipWith (==) r1 r2) @@ -67,4 +72,15 @@ instance Eq Expr where (Boolean b1) == (Boolean b2) = b1 == b2 (Int v1) == (Int v2) = v1 == v2 _ == _ = error "Comparison of incompatible types" + +instance Ord Expr where + --(String i) > (String j) = i > j + (String i) <= (String j) = i <= j + + --(Int i) > (Int j) = i > j + (Int i) <= (Int j) = i <= j + + --_ < _ = error "Invalid comparison" + --_ > _ = error "Invalid comparison" + data Parameter = NamedParam SymbolName | TupleMatch [SymbolName] \ No newline at end of file diff --git a/pr1.cql b/pr1.cql new file mode 100644 index 0000000000000000000000000000000000000000..f9bfb751ab7fe0abaacc7b74c0c67cd81b33533a --- /dev/null +++ b/pr1.cql @@ -0,0 +1,6 @@ +.in +A:2, # declare input files and their numbers of cols in .in section +B:2 + +.out # statements for the query are in .out section +A x B; # returns the cartesian product of the two sets (conjunction) \ No newline at end of file diff --git a/pr10.cql b/pr10.cql new file mode 100644 index 0000000000000000000000000000000000000000..bb4e262d6b40a215124257ed9b5b0a787064e9e9 --- /dev/null +++ b/pr10.cql @@ -0,0 +1,5 @@ +.in +B:1 + +.out +B; \ No newline at end of file diff --git a/pr2.cql b/pr2.cql new file mode 100644 index 0000000000000000000000000000000000000000..7411d3213e48552103ca4e9a2e5b7cda555ae982 --- /dev/null +++ b/pr2.cql @@ -0,0 +1,6 @@ +.in +A:3 + +.out +filter( \(r) -> r[1] == r[2]); +map (\(r) -> r[3,1]); \ No newline at end of file diff --git a/pr3.cql b/pr3.cql new file mode 100644 index 0000000000000000000000000000000000000000..5dc784d5397ce53d453b08192bebe247592cdfa1 --- /dev/null +++ b/pr3.cql @@ -0,0 +1,9 @@ +.in +P: 4, +Q: 4 + +.out +P x Q; +filter(\(r) -> r[1] == r[5]); +let f = \(a,y) -> if (isEmpty(a)) then y else a; +map (\(r) -> [r[1], f(r[2], r[6]), f(r[3], r[7]), f(r[4], r[8])]); \ No newline at end of file diff --git a/pr4.cql b/pr4.cql new file mode 100644 index 0000000000000000000000000000000000000000..28e6b58cf18931120cabf181158fe53d7006a72b --- /dev/null +++ b/pr4.cql @@ -0,0 +1,6 @@ +.in +A:2 + +.out +filter (\(r) -> not(isEmpty(r[2]) ) ); +#filter (\(r) -> notEmpty(r[2]) ); \ No newline at end of file diff --git a/pr5.cql b/pr5.cql new file mode 100644 index 0000000000000000000000000000000000000000..f34636ec2f22bd74a8f39567878a1dec46232c78 --- /dev/null +++ b/pr5.cql @@ -0,0 +1,5 @@ +.in +A:1 + +.out +map{A}(\(r) -> [r[1],"0",r[1]] ); \ No newline at end of file diff --git a/pr6.cql b/pr6.cql new file mode 100644 index 0000000000000000000000000000000000000000..3938ff99fa26642d6acd3f3c3d66b6645c4d5d96 --- /dev/null +++ b/pr6.cql @@ -0,0 +1,9 @@ +.in +P:3, +Q:3, +R:1, +S:1, +T:4 + +.out +P x Q x R x S x T; \ No newline at end of file diff --git a/pr7.cql b/pr7.cql new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/pr8.cql b/pr8.cql new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/pr9.cql b/pr9.cql new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391