v / vlib / v2 / transformer / orm.v
433 lines · 405 sloc · 9.42 KB · b831b0eec9b5b2756784b5dabf3808d47d6a39ae
Raw
1// Copyright (c) 2026 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4
5module transformer
6
7import v2.ast
8import v2.token
9import v2.types
10
11fn (mut t Transformer) transform_sql_expr(expr ast.SqlExpr) ast.Expr {
12 if !expr.is_create {
13 return ast.Expr(ast.SqlExpr{
14 expr: t.transform_expr(expr.expr)
15 table_name: expr.table_name
16 is_count: expr.is_count
17 is_create: expr.is_create
18 pos: expr.pos
19 })
20 }
21 if lowered := t.lower_sql_create_expr(expr) {
22 return lowered
23 }
24 return ast.Expr(ast.SqlExpr{
25 expr: t.transform_expr(expr.expr)
26 table_name: expr.table_name
27 is_count: expr.is_count
28 is_create: expr.is_create
29 pos: expr.pos
30 })
31}
32
33fn (mut t Transformer) lower_sql_create_expr(expr ast.SqlExpr) ?ast.Expr {
34 table := t.lookup_sql_table_struct(expr.table_name) or { return none }
35 call_pos := t.next_synth_pos()
36 call := ast.Expr(ast.CallExpr{
37 lhs: ast.Expr(ast.SelectorExpr{
38 lhs: expr.expr
39 rhs: ast.Ident{
40 name: 'create'
41 pos: call_pos
42 }
43 pos: call_pos
44 })
45 args: [
46 t.sql_orm_table_expr(table.name, expr.pos),
47 t.sql_orm_table_fields_expr(table.fields, expr.pos),
48 ]
49 pos: call_pos
50 })
51 t.register_synth_type(call_pos, types.Type(types.ResultType{
52 base_type: types.Type(types.void_)
53 }))
54 return t.transform_expr(call)
55}
56
57fn (t &Transformer) lookup_sql_table_struct(name string) ?types.Struct {
58 if typ := t.lookup_type(name) {
59 base := t.sql_unwrap_alias_pointer_type(typ)
60 if base is types.Struct {
61 return base
62 }
63 }
64 return t.lookup_struct_type_any_module(name)
65}
66
67fn (t &Transformer) sql_unwrap_alias_pointer_type(typ types.Type) types.Type {
68 mut cur := typ
69 for {
70 match cur {
71 types.Alias {
72 cur = cur.base_type
73 continue
74 }
75 types.Pointer {
76 cur = cur.base_type
77 continue
78 }
79 else {}
80 }
81
82 break
83 }
84 return cur
85}
86
87fn (mut t Transformer) sql_orm_table_expr(type_name string, pos token.Pos) ast.Expr {
88 init_pos := t.next_synth_pos()
89 return ast.Expr(ast.InitExpr{
90 typ: t.sql_orm_type_expr('Table', pos)
91 fields: [
92 ast.FieldInit{
93 name: 'name'
94 value: t.sql_string_expr(type_name.to_lower(), pos)
95 },
96 ast.FieldInit{
97 name: 'attrs'
98 value: t.sql_vattribute_array_expr([], pos)
99 },
100 ]
101 pos: init_pos
102 })
103}
104
105fn (mut t Transformer) sql_orm_table_fields_expr(fields []types.Field, pos token.Pos) ast.Expr {
106 mut exprs := []ast.Expr{cap: fields.len}
107 for field in fields {
108 if field_expr := t.sql_orm_table_field_expr(field, pos) {
109 exprs << field_expr
110 }
111 }
112 array_pos := t.next_synth_pos()
113 return ast.Expr(ast.ArrayInitExpr{
114 typ: ast.Expr(ast.Type(ast.ArrayType{
115 elem_type: t.sql_orm_type_expr('TableField', pos)
116 }))
117 exprs: exprs
118 pos: array_pos
119 })
120}
121
122fn (mut t Transformer) sql_orm_table_field_expr(field types.Field, pos token.Pos) ?ast.Expr {
123 if sql_field_is_skipped(field.attributes) {
124 return none
125 }
126 field_type, nullable, is_arr := t.sql_field_storage_type(field.typ)
127 init_pos := t.next_synth_pos()
128 return ast.Expr(ast.InitExpr{
129 typ: t.sql_orm_type_expr('TableField', pos)
130 fields: [
131 ast.FieldInit{
132 name: 'name'
133 value: t.sql_string_expr(field.name, pos)
134 },
135 ast.FieldInit{
136 name: 'typ'
137 value: t.sql_orm_type_idx_expr(field_type, pos)
138 },
139 ast.FieldInit{
140 name: 'nullable'
141 value: t.sql_bool_expr(nullable, pos)
142 },
143 ast.FieldInit{
144 name: 'default_val'
145 value: t.sql_string_expr('', pos)
146 },
147 ast.FieldInit{
148 name: 'attrs'
149 value: t.sql_vattribute_array_expr(field.attributes, pos)
150 },
151 ast.FieldInit{
152 name: 'is_arr'
153 value: t.sql_bool_expr(is_arr, pos)
154 },
155 ]
156 pos: init_pos
157 })
158}
159
160fn (t &Transformer) sql_field_storage_type(typ types.Type) (types.Type, bool, bool) {
161 mut cur := typ
162 mut nullable := false
163 mut is_arr := false
164 for {
165 match cur {
166 types.OptionType {
167 nullable = true
168 cur = cur.base_type
169 continue
170 }
171 types.Alias {
172 cur = cur.base_type
173 continue
174 }
175 types.Pointer {
176 cur = cur.base_type
177 continue
178 }
179 types.Array {
180 is_arr = true
181 cur = cur.elem_type
182 continue
183 }
184 else {}
185 }
186
187 break
188 }
189 return cur, nullable, is_arr
190}
191
192fn (mut t Transformer) sql_orm_type_idx_expr(typ types.Type, pos token.Pos) ast.Expr {
193 type_name := t.type_to_c_name(typ)
194 if type_name == 'time__Time' || type_name == 'time.Time' {
195 return t.sql_int_expr(-2, pos)
196 }
197 match typ {
198 types.Enum {
199 return t.sql_int_expr(-3, pos)
200 }
201 types.String {
202 return t.sql_int_expr(typeof_type_idx('string'), pos)
203 }
204 types.Primitive {
205 return t.sql_int_expr(typeof_type_idx(type_name), pos)
206 }
207 else {}
208 }
209
210 if type_name == 'string' {
211 return t.sql_int_expr(typeof_type_idx('string'), pos)
212 }
213 return t.sql_int_expr(typeof_type_idx(type_name), pos)
214}
215
216fn sql_field_is_skipped(attrs []ast.Attribute) bool {
217 if attrs.has('skip') {
218 return true
219 }
220 for attr in attrs {
221 if attr.name == 'sql' && sql_attribute_value_string(attr.value) == '-' {
222 return true
223 }
224 }
225 return false
226}
227
228fn (mut t Transformer) sql_vattribute_array_expr(attrs []ast.Attribute, pos token.Pos) ast.Expr {
229 mut exprs := []ast.Expr{cap: attrs.len}
230 for attr in attrs {
231 if attr_expr := t.sql_vattribute_expr(attr, pos) {
232 exprs << attr_expr
233 }
234 }
235 array_pos := t.next_synth_pos()
236 elem_pos := t.next_synth_pos()
237 return ast.Expr(ast.ArrayInitExpr{
238 typ: ast.Expr(ast.Type(ast.ArrayType{
239 elem_type: ast.Expr(ast.Ident{
240 name: 'VAttribute'
241 pos: elem_pos
242 })
243 }))
244 exprs: exprs
245 pos: array_pos
246 })
247}
248
249fn (mut t Transformer) sql_vattribute_expr(attr ast.Attribute, pos token.Pos) ?ast.Expr {
250 mut name := attr.name
251 mut has_arg := attr.name != ''
252 mut arg := ''
253 mut kind := 'AttributeKind__plain'
254 if name == '' {
255 name = sql_attribute_value_string(attr.value)
256 has_arg = false
257 _, kind = sql_attribute_arg_and_kind(attr.value)
258 if kind != 'AttributeKind__string' {
259 kind = 'AttributeKind__plain'
260 }
261 } else {
262 arg, kind = sql_attribute_arg_and_kind(attr.value)
263 }
264 if name == '' {
265 return none
266 }
267 init_pos := t.next_synth_pos()
268 typ_pos := t.next_synth_pos()
269 kind_pos := t.next_synth_pos()
270 return ast.Expr(ast.InitExpr{
271 typ: ast.Expr(ast.Ident{
272 name: 'VAttribute'
273 pos: typ_pos
274 })
275 fields: [
276 ast.FieldInit{
277 name: 'name'
278 value: t.sql_string_expr(name, pos)
279 },
280 ast.FieldInit{
281 name: 'has_arg'
282 value: t.sql_bool_expr(has_arg, pos)
283 },
284 ast.FieldInit{
285 name: 'arg'
286 value: t.sql_string_expr(arg, pos)
287 },
288 ast.FieldInit{
289 name: 'kind'
290 value: ast.Expr(ast.Ident{
291 name: kind
292 pos: kind_pos
293 })
294 },
295 ]
296 pos: init_pos
297 })
298}
299
300fn sql_attribute_arg_and_kind(expr ast.Expr) (string, string) {
301 match expr {
302 ast.StringLiteral {
303 return sql_unquote_literal(expr.value), 'AttributeKind__string'
304 }
305 ast.BasicLiteral {
306 if expr.kind in [.key_true, .key_false] {
307 return expr.value, 'AttributeKind__bool'
308 }
309 return expr.value, 'AttributeKind__number'
310 }
311 ast.Ident {
312 kind := if expr.name in sql_plain_attribute_args {
313 'AttributeKind__plain'
314 } else {
315 'AttributeKind__string'
316 }
317 return expr.name, kind
318 }
319 ast.PrefixExpr {
320 if expr.op == .minus && expr.expr is ast.BasicLiteral {
321 inner := expr.expr as ast.BasicLiteral
322 return '-' + inner.value, 'AttributeKind__number'
323 }
324 }
325 else {}
326 }
327
328 return expr.name(), 'AttributeKind__plain'
329}
330
331fn sql_attribute_value_string(expr ast.Expr) string {
332 match expr {
333 ast.StringLiteral {
334 return sql_unquote_literal(expr.value)
335 }
336 ast.BasicLiteral {
337 return expr.value
338 }
339 ast.Ident {
340 return expr.name
341 }
342 ast.PrefixExpr {
343 if expr.op == .minus && expr.expr is ast.BasicLiteral {
344 inner := expr.expr as ast.BasicLiteral
345 return '-' + inner.value
346 }
347 }
348 else {}
349 }
350
351 return expr.name()
352}
353
354fn sql_unquote_literal(raw string) string {
355 if raw.len < 2 {
356 return raw
357 }
358 first := raw[0]
359 last := raw[raw.len - 1]
360 if first == last && first in [`'`, `"`] {
361 return raw[1..raw.len - 1]
362 }
363 return raw
364}
365
366const sql_plain_attribute_args = ['serial', 'i8', 'i16', 'int', 'i64', 'u8', 'u16', 'u32', 'u64',
367 'f32', 'f64', 'bool', 'string']
368
369fn (mut t Transformer) sql_orm_type_expr(name string, pos token.Pos) ast.Expr {
370 _ = pos
371 expr_pos := t.next_synth_pos()
372 lhs_pos := t.next_synth_pos()
373 rhs_pos := t.next_synth_pos()
374 return ast.Expr(ast.SelectorExpr{
375 lhs: ast.Expr(ast.Ident{
376 name: 'orm'
377 pos: lhs_pos
378 })
379 rhs: ast.Ident{
380 name: name
381 pos: rhs_pos
382 }
383 pos: expr_pos
384 })
385}
386
387fn (mut t Transformer) sql_orm_const_expr(name string, pos token.Pos) ast.Expr {
388 _ = pos
389 expr_pos := t.next_synth_pos()
390 lhs_pos := t.next_synth_pos()
391 rhs_pos := t.next_synth_pos()
392 return ast.Expr(ast.SelectorExpr{
393 lhs: ast.Expr(ast.Ident{
394 name: 'orm'
395 pos: lhs_pos
396 })
397 rhs: ast.Ident{
398 name: name
399 pos: rhs_pos
400 }
401 pos: expr_pos
402 })
403}
404
405fn (mut t Transformer) sql_string_expr(value string, pos token.Pos) ast.Expr {
406 _ = pos
407 lit_pos := t.next_synth_pos()
408 return ast.Expr(ast.StringLiteral{
409 kind: .v
410 value: value
411 pos: lit_pos
412 })
413}
414
415fn (mut t Transformer) sql_int_expr(value int, pos token.Pos) ast.Expr {
416 _ = pos
417 lit_pos := t.next_synth_pos()
418 return ast.Expr(ast.BasicLiteral{
419 kind: .number
420 value: value.str()
421 pos: lit_pos
422 })
423}
424
425fn (mut t Transformer) sql_bool_expr(value bool, pos token.Pos) ast.Expr {
426 _ = pos
427 lit_pos := t.next_synth_pos()
428 return ast.Expr(ast.BasicLiteral{
429 kind: if value { token.Token.key_true } else { token.Token.key_false }
430 value: if value { 'true' } else { 'false' }
431 pos: lit_pos
432 })
433}
434