-
Notifications
You must be signed in to change notification settings - Fork 2
/
Ffi.lean
296 lines (225 loc) · 6.9 KB
/
Ffi.lean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
@[extern "lean_mysql_initialize"]
constant initMySQL : BaseIO Unit
builtin_initialize initMySQL
inductive Entry
| str (s : String)
| int (n : Int)
| float (f : Float)
instance : Coe Int Entry where
coe := Entry.int
instance : Coe String Entry where
coe := Entry.str
instance : Coe Float Entry where
coe := Entry.float
abbrev Row := List Entry
namespace Row
private def toStrings (r : Row) : List String :=
r.map λ e => match e with
| Entry.str e => s!"'{e}'"
| Entry.int e => toString e
| Entry.float e => toString e
private def build (r : Row) : String :=
s!"({",".intercalate (r.toStrings)})"
end Row
structure Column where
name : String
type : String
deriving Inhabited
namespace Column
private def build (c : Column) : String :=
s!"{c.name} {c.type}"
end Column
abbrev TableScheme := List Column
namespace TableScheme
private def build (ts : TableScheme) : String :=
s!"({",".intercalate (ts.map λ v => v.build)})"
end TableScheme
--------------------------
abbrev Col := Lean.Name
private inductive ColProp
| EqE (c : Col) (e : Entry)
| NeqE (c : Col) (e : Entry)
| LeE (c : Col) (e : Entry)
| LE (c : Col) (e : Entry)
| GeE (c : Col) (e : Entry)
| GE (c : Col) (e : Entry)
| EqC (c : Col) (c' : Col)
| NeqC (c : Col) (c' : Col)
| LeC (c : Col) (c' : Col)
| LC (c : Col) (c' : Col)
| GeC (c : Col) (c' : Col)
| GC (c : Col) (c' : Col)
| And (cp : ColProp) (cp' : ColProp)
| Or (cp : ColProp) (cp' : ColProp)
infix:50 " = " => ColProp.EqE
infix:50 " ≠ " => ColProp.NeqE
infix:50 " ≤ " => ColProp.LeE
infix:50 " < " => ColProp.LE
infix:50 " ≥ " => ColProp.GeE
infix:50 " > " => ColProp.GE
infix:50 " = " => ColProp.EqC
infix:50 " ≠ " => ColProp.NeqC
infix:50 " ≤ " => ColProp.LeC
infix:50 " < " => ColProp.LC
infix:50 " ≥ " => ColProp.GeC
infix:50 " > " => ColProp.GC
infix:25 " ∧ " => ColProp.And
infix:25 " ∨ " => ColProp.Or
mutual
inductive Query
| mk (name : String) (steps : List QueryStep)
private inductive QueryStep
| select (l : List Col)
| filter (cp : ColProp)
| join (q : Query) (on : ColProp) (how : String)
end
def table (n : String) : Query := ⟨n, []⟩
namespace Query
private def name : Query → String
| mk name _ => name
private def steps : Query → List QueryStep
| mk _ steps => steps
end Query
def select (l : List Col) (q : Query) : Query :=
⟨q.name, q.steps.concat (QueryStep.select l)⟩
def filter (cp : ColProp) (q : Query) : Query :=
⟨q.name, q.steps.concat (QueryStep.filter cp)⟩
def join (q' : Query) (on : ColProp) (how : String) (q : Query) : Query :=
⟨q.name, q.steps.concat (QueryStep.join q' on how)⟩
private def transform (q : Query) (f : Query → Query) : Query := f q
infixl:50 "↠" => transform
namespace Query
/-
#todo
Find a way to transform a Query like
```
table "cars" ↠
select [`id, `name] ↠
join (table "prices") (`l.id = `r.id) "left" ↠
filter (`price = 2)
```
into:
```
select *
from (select id,name from car) as l left join (select * from price) as r on l.id=r.id
where price = 2;
```
-/
private def build (q : Query) : String := sorry
end Query
inductive DType | DInt | DFloat | DString
def DType.toType : DType → Type
| DInt => Int
| DFloat => Float
| DString => String
structure Table where
names : List String
types : List DType
rows : List Row
deriving Inhabited
namespace Table
private def dTypesMap (t : String) : DType :=
if t = "int" then
DType.DInt
else
if t = "float" then
DType.DFloat
else
DType.DString
private def toFloat (s : String) : Float := do
let split := s.splitOn "."
let l := split.head!.splitOn "-"
let r := split.getLast!
let rFloat := r.toNat!.toFloat / (10.0 ^ r.length.toFloat)
if l.length = 1 then
return l.head!.toNat!.toFloat + rFloat
else
return -1.0 * (l.getLast!.toNat!.toFloat + rFloat)
private def parse (s : String) : Table := do
if s.length = 0 then
⟨[], [], []⟩
else
let mut names : List String := []
let mut dTypes : List DType := []
let mut data : List Row := []
let lines : List String := s.splitOn "¨"
let header : String := lines.head!
let headerParts : List String := header.splitOn "~"
for headerPart in headerParts do
let split : List String := headerPart.splitOn " "
names := names.concat (split.head!)
dTypes := dTypes.concat (dTypesMap (split.getLast!))
let mut i : Nat := 0
let maxI : Nat := lines.tail!.length
for row in lines.tail! do
let mut j : Nat := 0
let mut rowData : List Entry := []
let rowSplit := row.splitOn "~"
for dType in dTypes do
let valString : String := rowSplit.get! j
match dType with
| DType.DInt => rowData := rowData.concat (valString.toInt!)
| DType.DFloat => rowData := rowData.concat (toFloat valString)
| DType.DString => rowData := rowData.concat (valString)
j := j + 1
data := data.concat rowData
i := i + 1
if i = maxI - 1 then
break
⟨names, dTypes, data⟩
def toString (t : Table) : String := do
let mut res : String := ""
for n in t.names do
res := res ++ n ++ "|"
res := res ++ "\n"
let mut i : Nat := 0
let maxI : Nat := t.rows.length
for row in t.rows do
let rowStrings := row.toStrings
for s in rowStrings do
res := res ++ s ++ "|"
if i = maxI - 1 then
break
res := res ++ "\n"
i := i + 1
res
end Table
---------------------
constant MySQL : Type
namespace MySQL
@[extern "lean_mysql_mk"]
constant mk (bufferSizeKB : Nat := 8) : IO MySQL
@[extern "lean_mysql_set_buffer_size"]
constant setBufferSizeMB (bufferSizeKB : Nat) : IO Unit
@[extern "lean_mysql_version"]
constant version (m : MySQL) : BaseIO String
@[extern "lean_mysql_login"]
constant login (m : MySQL) (h u p : String) : IO Unit
@[extern "lean_mysql_run"]
private constant run (m : MySQL) (q : String) : IO Unit
def createDB (m : MySQL) (d : String) : IO Unit :=
m.run ("create database " ++ d)
def dropDB (m : MySQL) (d : String) : IO Unit :=
m.run ("drop database " ++ d)
def useDB (m : MySQL) (d : String) : IO Unit :=
m.run ("use " ++ d)
def createTable (m : MySQL) (n : String) (ts : TableScheme) : IO Unit :=
m.run ("create table " ++ (n ++ ts.build))
def dropTable (m : MySQL) (n : String) : IO Unit :=
m.run ("drop table " ++ n)
def insertIntoTable (m : MySQL) (n : String) (r : Row) : IO Unit :=
m.run s!"insert into {n} values{r.build}"
------ querying
@[extern "lean_mysql_query"]
private constant querySQLPriv (m : MySQL) (q : String) : IO Unit
def querySQL (m : MySQL) (q : String) : IO Unit := m.querySQLPriv q
/-# todo
constant query (m : MySQL) (q : Query) : IO Unit := m.querySQL q.build
-/
------ extract query result
@[extern "lean_mysql_get_query_result"]
private constant getQueryResultPriv (m : MySQL) : String
def getQueryResult (m : MySQL) : Table := Table.parse (getQueryResultPriv m)
@[extern "lean_mysql_close"]
constant close (m : MySQL) : BaseIO Unit
end MySQL