From 0098d2139364ba9d210fd3d5d3b363aed921fd36 Mon Sep 17 00:00:00 2001
From: p9malino26 <pm3g19@soton.ac.uk>
Date: Wed, 5 May 2021 21:38:56 +0100
Subject: [PATCH] Before first submission

---
 Eval.hs        | 39 +++++++++++++++++++++++++-----
 Interpreter.hs | 20 +++++++---------
 Lexer.x        | 48 +++++++++++++++++++++++++++++++++----
 Main.hs        | 20 ++++++++++++++++
 Makefile       |  5 ++++
 Parser.y       | 64 +++++++++++++++++++++++++++++++++++---------------
 Types.hs       | 16 +++++++++++++
 pr1.cql        |  6 +++++
 pr10.cql       |  5 ++++
 pr2.cql        |  6 +++++
 pr3.cql        |  9 +++++++
 pr4.cql        |  6 +++++
 pr5.cql        |  5 ++++
 pr6.cql        |  9 +++++++
 pr7.cql        |  0
 pr8.cql        |  0
 pr9.cql        |  0
 17 files changed, 217 insertions(+), 41 deletions(-)
 create mode 100644 Main.hs
 create mode 100644 Makefile
 create mode 100644 pr1.cql
 create mode 100644 pr10.cql
 create mode 100644 pr2.cql
 create mode 100644 pr3.cql
 create mode 100644 pr4.cql
 create mode 100644 pr5.cql
 create mode 100644 pr6.cql
 create mode 100644 pr7.cql
 create mode 100644 pr8.cql
 create mode 100644 pr9.cql

diff --git a/Eval.hs b/Eval.hs
index a081f99..5e1e941 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 23e4786..e090b50 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 1cab757..e07ab8d 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 0000000..bee36a8
--- /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 0000000..fef60a0
--- /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 39c3daf..29a8cf8 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 9e5d4bc..473898f 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 0000000..f9bfb75
--- /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 0000000..bb4e262
--- /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 0000000..7411d32
--- /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 0000000..5dc784d
--- /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 0000000..28e6b58
--- /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 0000000..f34636e
--- /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 0000000..3938ff9
--- /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 0000000..e69de29
diff --git a/pr8.cql b/pr8.cql
new file mode 100644
index 0000000..e69de29
diff --git a/pr9.cql b/pr9.cql
new file mode 100644
index 0000000..e69de29
-- 
GitLab