|
| 1 | +open Core |
| 2 | +open Crud_ast |
| 3 | +open Ir_xform_hashes |
| 4 | + |
| 5 | +include Ir_xform_types |
| 6 | + |
| 7 | +let check_duplicate_strings strings = |
| 8 | + let seen = StringHash.create () in |
| 9 | + List.iter strings ~f:( |
| 10 | + fun { Annotate.node = node; loc } -> |
| 11 | + match StringHash.find seen node with |
| 12 | + | Some prev_loc -> raise @@ Ir_error.Exn (Duplicate ("value", prev_loc, loc)) |
| 13 | + | None -> StringHash.set seen node loc |
| 14 | + ) |
| 15 | + |
| 16 | +let rec find_and_xform_field defs t model field = |
| 17 | + match Ir_xform_defs.find_field defs (!model).Model.name field with |
| 18 | + | Field field -> xform_field defs t model field |
| 19 | + | Rel rel -> xform_rel defs t model rel |
| 20 | + |
| 21 | +and find_and_xform_model defs t model = |
| 22 | + xform_model defs t (Ir_xform_defs.find_model defs model) |
| 23 | + |
| 24 | +and xform_model defs t model = |
| 25 | + let { Syntax.Model.name = { node = name }; entries } = model in |
| 26 | + |
| 27 | + let model = lazy begin |
| 28 | + let entries_set = Ir_xform_dupes.create "model entry" in |
| 29 | + let fields_set = Ir_xform_dupes.create "field" in |
| 30 | + |
| 31 | + let model = ref |
| 32 | + { Model.name = name |
| 33 | + ; fields = StringHash.create () |
| 34 | + ; table = None |
| 35 | + ; key = [] |
| 36 | + ; unique = [] |
| 37 | + ; index = [] |
| 38 | + ; cruds = [] |
| 39 | + } in |
| 40 | + StringHash.set t.models name model; |
| 41 | + |
| 42 | + List.iter entries ~f:( |
| 43 | + fun { Annotate.loc; node } -> |
| 44 | + match node with |
| 45 | + | Syntax.Model.Table table -> |
| 46 | + Ir_xform_dupes.check entries_set "table" loc; |
| 47 | + model := { !model with table = Some table.node } |
| 48 | + |
| 49 | + | Key key -> |
| 50 | + Ir_xform_dupes.check entries_set "key" loc; |
| 51 | + check_duplicate_strings key; |
| 52 | + let fields = List.map key (find_and_xform_field defs t model) in |
| 53 | + model := { !model with key = fields } |
| 54 | + |
| 55 | + | Unique unique -> |
| 56 | + check_duplicate_strings unique; |
| 57 | + let fields = List.map unique (find_and_xform_field defs t model) in |
| 58 | + model := { !model with unique = fields :: !model.unique } |
| 59 | + |
| 60 | + | Index index -> |
| 61 | + check_duplicate_strings index; |
| 62 | + let fields = List.map index (find_and_xform_field defs t model) in |
| 63 | + model := { !model with index = fields :: !model.index } |
| 64 | + |
| 65 | + | Field ({ name = { node = field_name } } as field) -> |
| 66 | + Ir_xform_dupes.check fields_set field_name loc; |
| 67 | + let field = xform_field defs t model field in |
| 68 | + StringHash.set !model.fields field_name field |
| 69 | + |
| 70 | + | Rel ({ name = { node = rel_name } } as rel) -> |
| 71 | + Ir_xform_dupes.check fields_set rel_name loc; |
| 72 | + let rel = xform_rel defs t model rel in |
| 73 | + StringHash.set !model.fields rel_name rel |
| 74 | + ); |
| 75 | + |
| 76 | + model |
| 77 | + end in |
| 78 | + |
| 79 | + match StringHash.find t.models name with |
| 80 | + | Some model -> model |
| 81 | + | None -> force model |
| 82 | + |
| 83 | +and xform_field defs t parent field = |
| 84 | + let { Syntax.Field.name = { node = name }; type_ = { node = type_ }; attrs } = field in |
| 85 | + |
| 86 | + let field = lazy begin |
| 87 | + let entries_set = Ir_xform_dupes.create "field attribute" in |
| 88 | + |
| 89 | + let field_ = ref |
| 90 | + { Field.parent = parent |
| 91 | + ; name |
| 92 | + ; type_ |
| 93 | + ; column = None |
| 94 | + ; nullable = false |
| 95 | + ; updatable = false |
| 96 | + ; autoinsert = false |
| 97 | + ; autoupdate = false |
| 98 | + ; length = None |
| 99 | + } in |
| 100 | + let field = ref @@ Model.Field !field_ in |
| 101 | + FieldHash.set t.fields (!parent.name, name) field; |
| 102 | + |
| 103 | + List.iter attrs ~f:( |
| 104 | + fun { Annotate.loc; node } -> |
| 105 | + begin match node with |
| 106 | + | Syntax.Field.Column { node = col } -> |
| 107 | + Ir_xform_dupes.check entries_set "column" loc; |
| 108 | + field_ := { !field_ with column = Some col } |
| 109 | + |
| 110 | + | Nullable -> |
| 111 | + Ir_xform_dupes.check entries_set "nullable" loc; |
| 112 | + field_ := { !field_ with nullable = true } |
| 113 | + |
| 114 | + | Updatable -> |
| 115 | + Ir_xform_dupes.check entries_set "updatable" loc; |
| 116 | + field_ := { !field_ with updatable = true } |
| 117 | + |
| 118 | + | Autoinsert -> |
| 119 | + Ir_xform_dupes.check entries_set "autoinsert" loc; |
| 120 | + field_ := { !field_ with autoinsert = true } |
| 121 | + |
| 122 | + | Autoupdate -> |
| 123 | + Ir_xform_dupes.check entries_set "autoupdate" loc; |
| 124 | + field_ := { !field_ with autoupdate = true } |
| 125 | + |
| 126 | + | Length { node = length; loc } -> |
| 127 | + Ir_xform_dupes.check entries_set "length" loc; |
| 128 | + match Int.of_string length with |
| 129 | + | length -> field_ := { !field_ with length = Some length } |
| 130 | + | exception _ -> raise @@ Ir_error.Exn (Invalid loc) |
| 131 | + end; |
| 132 | + |
| 133 | + field := Field !field_; |
| 134 | + ); |
| 135 | + |
| 136 | + field |
| 137 | + end in |
| 138 | + |
| 139 | + match FieldHash.find t.fields (!parent.name, name) with |
| 140 | + | Some field -> field |
| 141 | + | None -> force field |
| 142 | + |
| 143 | +and xform_rel defs t parent rel = |
| 144 | + let { Syntax.Rel.name = { node = name }; model; field; kind = { node = kind }; attrs } = rel in |
| 145 | + |
| 146 | + let rel = lazy begin |
| 147 | + let entries_set = Ir_xform_dupes.create "relation attribute" in |
| 148 | + let model = find_and_xform_model defs t model in |
| 149 | + let field = find_and_xform_field defs t model field in |
| 150 | + |
| 151 | + let rel_ = ref |
| 152 | + { Rel.parent = parent |
| 153 | + ; name |
| 154 | + ; model |
| 155 | + ; field |
| 156 | + ; kind |
| 157 | + ; column = None |
| 158 | + ; nullable = false |
| 159 | + ; updatable = false |
| 160 | + } in |
| 161 | + let rel = ref @@ Model.Rel !rel_ in |
| 162 | + FieldHash.set t.fields (!parent.name, name) rel; |
| 163 | + |
| 164 | + List.iter attrs ~f:( |
| 165 | + fun { Annotate.loc; node } -> |
| 166 | + begin match node with |
| 167 | + | Syntax.Rel.Column { node = col } -> |
| 168 | + Ir_xform_dupes.check entries_set "column" loc; |
| 169 | + rel_ := { !rel_ with column = Some col } |
| 170 | + |
| 171 | + | Nullable -> |
| 172 | + (Ir_xform_dupes.check entries_set "nullable" loc; |
| 173 | + rel_ := { !rel_ with nullable = true }) |
| 174 | + |
| 175 | + | Updatable -> |
| 176 | + (Ir_xform_dupes.check entries_set "updatable" loc; |
| 177 | + rel_ := { !rel_ with updatable = true }) |
| 178 | + end; |
| 179 | + |
| 180 | + rel := Rel !rel_; |
| 181 | + ); |
| 182 | + |
| 183 | + rel |
| 184 | + end in |
| 185 | + |
| 186 | + match FieldHash.find t.fields (!parent.name, name) with |
| 187 | + | Some rel -> rel |
| 188 | + | None -> force rel |
| 189 | + |
| 190 | +and xform_crud defs t crud = |
| 191 | + let { Syntax.Crud.model; entries } = crud in |
| 192 | + let model = find_and_xform_model defs t model in |
| 193 | + |
| 194 | + let crud = lazy begin |
| 195 | + let crud = ref |
| 196 | + { Crud.model |
| 197 | + ; entries = [] |
| 198 | + } in |
| 199 | + StringHash.set t.cruds !model.name crud; |
| 200 | + |
| 201 | + List.iter entries ~f:( |
| 202 | + fun { Annotate.loc; node } -> |
| 203 | + match node with |
| 204 | + | Syntax.Crud.Create create -> |
| 205 | + let create = xform_create defs t crud create in |
| 206 | + crud := { !crud with entries = create :: !crud.entries } |
| 207 | + |
| 208 | + | Read read -> |
| 209 | + let read = xform_read defs t crud read in |
| 210 | + crud := { !crud with entries = read :: !crud.entries } |
| 211 | + |
| 212 | + | Update update -> |
| 213 | + let update = xform_update defs t crud update in |
| 214 | + crud := { !crud with entries = update :: !crud.entries } |
| 215 | + |
| 216 | + | Delete delete -> |
| 217 | + let delete = xform_delete defs t crud delete in |
| 218 | + crud := { !crud with entries = delete :: !crud.entries } |
| 219 | + ); |
| 220 | + |
| 221 | + crud |
| 222 | + end in |
| 223 | + |
| 224 | + match StringHash.find t.cruds !model.name with |
| 225 | + | Some crud -> crud |
| 226 | + | None -> force crud |
| 227 | + |
| 228 | +and xform_query defs t model = function |
| 229 | + | Syntax.Query.Term term -> xform_query_term defs t model term |
| 230 | + |
| 231 | + | And { left_query = { node = left_query }; right_query = { node = right_query } } -> |
| 232 | + Query.And { b_left = xform_query defs t model left_query |
| 233 | + ; b_right = xform_query defs t model right_query |
| 234 | + } |
| 235 | + |
| 236 | + | Or { left_query = { node = left_query }; right_query = { node = right_query } } -> |
| 237 | + Query.Or { b_left = xform_query defs t model left_query |
| 238 | + ; b_right = xform_query defs t model right_query |
| 239 | + } |
| 240 | + |
| 241 | +and xform_query_term defs t model term = |
| 242 | + let { Syntax.Query.left_val = { node = left_val } |
| 243 | + ; op = { node = op } |
| 244 | + ; right_val = { node = right_val } |
| 245 | + } = term in |
| 246 | + let left_val = xform_query_value defs t model left_val in |
| 247 | + let right_val = xform_query_value defs t model right_val in |
| 248 | + Query.Term { t_left = left_val |
| 249 | + ; t_op = op |
| 250 | + ; t_right = right_val |
| 251 | + } |
| 252 | + |
| 253 | +and xform_query_value defs t model = function |
| 254 | + | Syntax.Query.Placeholder -> |
| 255 | + Query.Placeholder |
| 256 | + |
| 257 | + | Literal { node = literal } -> |
| 258 | + Literal literal |
| 259 | + |
| 260 | + | Call ({ node = fn }, { node = value }) -> |
| 261 | + Call (fn, xform_query_value defs t model value) |
| 262 | + |
| 263 | + | Field field -> |
| 264 | + let field = find_and_xform_field defs t model field in |
| 265 | + Field field |
| 266 | + |
| 267 | + | Join (model, { node = query }, field) -> |
| 268 | + let model = find_and_xform_model defs t model in |
| 269 | + let field = find_and_xform_field defs t model field in |
| 270 | + Join (model, xform_query defs t model query, field) |
| 271 | + |
| 272 | +and xform_create defs t parent create = |
| 273 | + let { Syntax.Create.attrs } = create in |
| 274 | + let attrs_set = Ir_xform_dupes.create "attribute" in |
| 275 | + |
| 276 | + let create = ref { Create.parent |
| 277 | + ; raw = false |
| 278 | + ; suffix = None |
| 279 | + } in |
| 280 | + |
| 281 | + List.iter attrs ~f:( |
| 282 | + fun { Annotate.loc; node } -> |
| 283 | + match node with |
| 284 | + | Syntax.Create.Raw -> |
| 285 | + Ir_xform_dupes.check attrs_set "raw" loc; |
| 286 | + create := { !create with raw = true } |
| 287 | + |
| 288 | + | Suffix { node = suffix } -> |
| 289 | + Ir_xform_dupes.check attrs_set "suffix" loc; |
| 290 | + create := { !create with suffix = Some suffix } |
| 291 | + ); |
| 292 | + |
| 293 | + Crud.Create !create |
| 294 | + |
| 295 | +and xform_read defs t parent read = |
| 296 | + let { Syntax.Read.kind = { node = kind }; query; attrs } = read in |
| 297 | + let attrs_set = Ir_xform_dupes.create "attribute" in |
| 298 | + let query = Option.map query ~f:( |
| 299 | + fun query -> xform_query defs t !parent.model query.node |
| 300 | + ) in |
| 301 | + |
| 302 | + let read = ref { Read.parent |
| 303 | + ; kind |
| 304 | + ; query |
| 305 | + ; suffix = None |
| 306 | + ; order_by = None |
| 307 | + } in |
| 308 | + |
| 309 | + List.iter attrs ~f:( |
| 310 | + fun { Annotate.loc; node } -> |
| 311 | + match node with |
| 312 | + | Syntax.Read.Suffix { node = suffix } -> |
| 313 | + Ir_xform_dupes.check attrs_set "suffix" loc; |
| 314 | + read := { !read with suffix = Some suffix } |
| 315 | + |
| 316 | + | OrderBy direction -> |
| 317 | + Ir_xform_dupes.check attrs_set "orderby" loc; |
| 318 | + read := { !read with order_by = Some direction } |
| 319 | + ); |
| 320 | + |
| 321 | + Crud.Read !read |
| 322 | + |
| 323 | +and xform_update defs t parent update = |
| 324 | + let { Syntax.Update.attrs; query = { node = query } } = update in |
| 325 | + let attrs_set = Ir_xform_dupes.create "attribute" in |
| 326 | + let query = xform_query defs t !parent.model query in |
| 327 | + |
| 328 | + let update = ref { Update.parent |
| 329 | + ; query |
| 330 | + ; suffix = None |
| 331 | + } in |
| 332 | + |
| 333 | + List.iter attrs ~f:( |
| 334 | + fun { Annotate.loc; node } -> |
| 335 | + match node with |
| 336 | + | Syntax.Update.Suffix { node = suffix } -> |
| 337 | + Ir_xform_dupes.check attrs_set "suffix" loc; |
| 338 | + update := { !update with suffix = Some suffix } |
| 339 | + ); |
| 340 | + |
| 341 | + Crud.Update !update |
| 342 | + |
| 343 | +and xform_delete defs t parent delete = |
| 344 | + let { Syntax.Delete.attrs; query = { node = query } } = delete in |
| 345 | + let attrs_set = Ir_xform_dupes.create "attribute" in |
| 346 | + let query = xform_query defs t !parent.model query in |
| 347 | + |
| 348 | + let delete = ref { Delete.parent |
| 349 | + ; query |
| 350 | + ; suffix = None |
| 351 | + } in |
| 352 | + |
| 353 | + List.iter attrs ~f:( |
| 354 | + fun { Annotate.loc; node } -> |
| 355 | + match node with |
| 356 | + | Syntax.Delete.Suffix { node = suffix } -> |
| 357 | + Ir_xform_dupes.check attrs_set "suffix" loc; |
| 358 | + delete := { !delete with suffix = Some suffix } |
| 359 | + ); |
| 360 | + |
| 361 | + Crud.Delete !delete |
| 362 | + |
| 363 | + |
| 364 | +let xform_def defs t { Annotate.node = def } = |
| 365 | + match def with |
| 366 | + | Syntax.Model model -> ignore @@ xform_model defs t model |
| 367 | + | Syntax.Crud crud -> ignore @@ xform_crud defs t crud |
| 368 | + |
| 369 | +let xform_defs defs = |
| 370 | + let t = |
| 371 | + { models = StringHash.create () |
| 372 | + ; fields = FieldHash.create () |
| 373 | + ; cruds = StringHash.create () |
| 374 | + } in |
| 375 | + |
| 376 | + match List.iter defs ~f:(xform_def (Ir_xform_defs.create defs) t) with |
| 377 | + | () -> Ok t |
| 378 | + | exception Ir_error.Exn err -> Error err |
0 commit comments