(* Carsten Schuermann *) (* A SQL interpreter *) (* Fri Feb 9 23:12:11 2007 *) datatype Data = Int of int | String of string | Real of real | Pair of Data * Data type Table = Data list fun filter T P = foldr (fn (x, l) => if P x then x :: l else l) nil T fun join T1 T2 = foldr (fn (x, t1) => t1 @ (foldr (fn (y, t2) => (Pair (x, y)) :: t2) nil T2)) nil T1 fun load (s, toData) = let val f = TextIO.openIn s fun load' () = case (TextIO.inputLine f) of SOME s => toData s :: load' () | NONE => nil val result = load' () val _ = TextIO.closeIn f in result end fun save T (s, fromData)= let val f = TextIO.openOut s fun save' nil = () | save' (H :: T) = (TextIO.output (f, fromData H); save' T) val result = save' T val _ = TextIO.closeOut f in () end datatype Ctx = Null | Cons of Ctx * Table datatype Exp = From of Table * Exp | Dup of Exp | Join of Exp | Where of (Data -> bool) * Exp | Iterate of (Data * Table -> Table) * Exp | Select of (Data -> Data) * Exp | Load of string * (string -> Data) * Exp | Save of string * (Data -> string) * Exp | End fun run S (From (T, E)) = run (Cons (S, T)) E | run (Cons (S, T)) (Dup E) = run (Cons (Cons (S, T), T)) E | run (Cons (Cons (S, T1), T2)) (Join E) = run (Cons (S, (join T1 T2))) E | run (Cons (S, T)) (Where (P, E)) = run (Cons (S, (filter T P))) E | run (Cons (S, T)) (Iterate (F, E)) = run (Cons (S, foldr F nil T)) E | run (Cons (S, T)) (Select (F, E)) = run (Cons (S, map F T)) E | run S (Load (s, f, E)) = run (Cons (S, load (s, f))) E | run (Cons (S, T)) (Save (s, f, E)) = (save T (s, f); run (Cons (S, T)) E) | run (Cons (S, T)) End = T (* Table 1: A table full of numbers *) val T0 : Table = map (fn x => Int x) [1,2,3,4,5,6,7,8,9] (* Example 1: Compute the sum of all numbers in T0 *) val E1 : Table = run Null (From (T0, Iterate (fn (Int x, []) => [Int x] | (Int x, [Int r]) => ([Int (x+r)]), End))) (* Example 2: Compute the sum of all even numbers in T0 *) val E2 = run Null (From (T0, Where (fn (Int x) => x mod 2 = 0, Iterate (fn (Int x, []) => [Int x] | (Int x, [Int r]) => [Int (x + r)], End)))) (* Example 3: Compute the list of all squares that are odd *) val E3 = run Null (From (T0, Iterate (fn (Int x, r) => if x * x mod 2 = 0 then r else Int x :: r, End))) (* Example 4: Compute the entries of a multiplication table *) val E4 = run Null (From (T0, Dup ( Join ( Iterate (fn (Pair (Int x, Int y), r) => Pair (Pair (Int x, Int y), Int (x * y)) :: r, End))))) (* Example 5: Compute the entries of a multiplication table and save it in the file "mult-table" *) val E5 = run Null (From (T0, Dup ( Join ( Iterate (fn (Pair (Int x, Int y), r) => Pair (Pair (Int x, Int y), Int (x * y)) :: r, Save ("mult-table", fn (Pair (Pair (Int x, Int y), Int z)) => Int.toString x ^ " * " ^ Int.toString y ^ " = " ^ Int.toString z ^ "\n", End)))))) (* Table 2: Contains parent relationships. (Child, (Father, Mother)) *) val r1 = Pair (String "Mary", Pair (String "George", String "Alice")) val r2 = Pair (String "Bob", Pair (String "George", String "Alice")) val r3 = Pair (String "George", Pair (String "Robert", String "Hillary")) val r4 = Pair (String "Alice", Pair (String "Max", String "Lena")) val r5 = Pair (String "Peter", Pair (String "Max", String "Lena")) val r6 = Pair (String "Lena", Pair (String "Rudolph", String "Magret")) val t : Table = [r1, r2, r3, r4, r5, r6] (* Example 6: Compute the grandparent relationship from the parent relationship Table of records of the form (c, ((fm, ff), (mf, mm)) *) val E6 = run Null (From (t, Dup ( Dup ( Join ( Join ( Where (fn (Pair (Pair (String c1, Pair (String f1, String m1)), Pair (Pair (String c2, Pair (String f2, String m2)), Pair (String c3, Pair (String f3, String m3))))) => f1 = c2 andalso m1 = c3, Select (fn (Pair (Pair (c1, Pair (String f1, String m1)), Pair (Pair (String c2, Pair (f2, m2)), Pair (String c3, Pair (f3, m3))))) => Pair (c1, Pair (Pair (f2, m2), Pair (f3, m3))), Save ("grandparents", fn (Pair (String c1, Pair (Pair (String f2, String m2), Pair (String f3, String m3)))) => c1 ^ " grandparents are " ^ f2 ^ ", " ^ m2 ^ ", " ^ f3 ^ ", " ^ m3 ^ ".\n", End))))))))) (* Layouts: Layouts are functions that convert strings (read from a file) into meaning full data*) val LayoutExchangeRate = fn s => Pair (String (String.substring (s, 0, 10)), Real (valOf (Real.fromString (String.substring (s, 20, 10))))) val LayoutExchangeRateInverse = fn (Pair (String s, Real r)) => s ^ " " ^ String.substring (Real.toString (r) ^ "0000000000", 0, 10) ^ "\n"; (* Example 7: Read two files which contain the exchange rate between dkk and dollars and dollars with euros. Create a new table which contains the exchange rate between dkk and euros. *) val E7 = run Null (Load ("dkk-dollar", LayoutExchangeRate, Load ("dollar-euro", LayoutExchangeRate, Join ( Where (fn (Pair (Pair (String d1, _), Pair (String d2, _))) => d1 = d2, Select (fn (Pair (Pair (d1, Real r1), Pair (_, Real r2))) => Pair (d1, Real (r1 * r2)), Save ("my-result", LayoutExchangeRateInverse, End))))))) (* Example 8: Do the same thing as above, but this time, in such a way that we also take into account the exchange rate between euro and dkk *) val E7 = run Null (Load ("dkk-dollar", LayoutExchangeRate, Load ("dollar-euro", LayoutExchangeRate, Join ( Where (fn (Pair (Pair (String d1, _), Pair (String d2, _))) => d1 = d2, Select (fn (Pair (Pair (d1, Real r1), Pair (_, Real r2))) => Pair (d1, Real (r1 * r2)), Load ("euro-dkk", LayoutExchangeRate, Join ( Where (fn (Pair (Pair (String d1, _), Pair (String d2, _))) => d1 = d2, Select (fn (Pair (Pair (d1, Real r1), Pair (_, Real r2))) => Pair (d1, Real (r1 * r2)), Save ("exchangerate-study", LayoutExchangeRateInverse, End)))))))))))