v2 / vlib / v / gen / js / array.v
302 lines · 281 sloc · 9.05 KB · f811cd4ffd1400bb01368f4b7bda53605e0135c9
Raw
1module js
2
3import v.ast
4import strings
5
6const special_array_methods = [
7 'sort',
8 'insert',
9 'prepend',
10 'index',
11 'last_index',
12 'contains',
13]
14
15fn (mut g JsGen) gen_array_index_method(left_type ast.Type, is_last_index bool) string {
16 unwrap_left_type := g.unwrap_generic(left_type)
17 mut left_sym := g.table.sym(unwrap_left_type)
18 mut left_type_str := g.styp(unwrap_left_type).trim('*')
19 fn_name := if is_last_index { '${left_type_str}_last_index' } else { '${left_type_str}_index' }
20
21 if (!is_last_index && !left_sym.has_method('index'))
22 || (is_last_index && !left_sym.has_method('last_index')) {
23 info := left_sym.info as ast.Array
24 elem_sym := g.table.sym(info.elem_type)
25 if elem_sym.kind == .function {
26 left_type_str = 'Array_voidptr'
27 }
28
29 mut fn_builder := strings.new_builder(512)
30 fn_builder.writeln('function ${fn_name}(a, v) {')
31 fn_builder.writeln('\tlet pelem = a.arr;')
32 if is_last_index {
33 fn_builder.writeln('\tif (pelem.arr.length == 0) return new int(-1);')
34 fn_builder.writeln('\tfor (let i = pelem.arr.length - 1; i >=0; --i) {')
35 } else {
36 fn_builder.writeln('\tfor (let i = 0; i < pelem.arr.length; ++i) {')
37 }
38 if elem_sym.kind == .string {
39 fn_builder.writeln('\t\tif (pelem.get(new int(i)).str == v.str) {')
40 } else if elem_sym.kind == .array && !info.elem_type.is_ptr() {
41 ptr_typ := g.gen_array_equality_fn(info.elem_type)
42 fn_builder.writeln('\t\tif (${ptr_typ}_arr_eq(pelem.get(new int(i)), v).val) {')
43 } else if elem_sym.kind == .function && !info.elem_type.is_ptr() {
44 fn_builder.writeln('\t\tif (pelem.get(new int(i)) == v) {')
45 } else if elem_sym.kind == .map && !info.elem_type.is_ptr() {
46 ptr_typ := g.gen_map_equality_fn(info.elem_type)
47 fn_builder.writeln('\t\tif (${ptr_typ}_map_eq(pelem.get(new int(i)), v).val) {')
48 } else if elem_sym.kind == .struct && !info.elem_type.is_ptr() {
49 ptr_typ := g.gen_struct_equality_fn(info.elem_type)
50 fn_builder.writeln('\t\tif (${ptr_typ}_struct_eq(pelem.get(new int(i)), v)) {')
51 } else {
52 fn_builder.writeln('\t\tif (vEq(pelem.get(new int(i)), v)) {')
53 }
54 fn_builder.writeln('\t\t\treturn new int(i);')
55 fn_builder.writeln('\t\t}')
56 fn_builder.writeln('\t}')
57 fn_builder.writeln('\treturn new int(-1);')
58 fn_builder.writeln('}')
59 g.definitions.writeln(fn_builder.str())
60 left_sym.register_method(ast.Fn{
61 name: 'index'
62 params: [ast.Param{
63 typ: unwrap_left_type
64 }, ast.Param{
65 typ: info.elem_type
66 }]
67 })
68 }
69
70 return fn_name
71}
72
73fn (mut g JsGen) gen_array_method_call(it ast.CallExpr) {
74 node := it
75
76 match node.name {
77 'index' {
78 g.gen_array_index(node, false)
79 return
80 }
81 'last_index' {
82 g.gen_array_index(node, true)
83 return
84 }
85 'contains' {
86 g.gen_array_contains(node)
87 return
88 }
89 'insert' {
90 g.write('array_')
91 arg2_sym := g.table.sym(node.args[1].typ)
92 is_arg2_array := arg2_sym.kind == .array && node.args[1].typ == node.left_type
93 if is_arg2_array {
94 g.write('insert_many(')
95 } else {
96 g.write('insert(')
97 }
98 g.expr(it.left)
99 mut ltyp := it.left_type
100 for ltyp.is_ptr() {
101 g.write('.val')
102 ltyp = ltyp.deref()
103 }
104 g.write(',')
105 g.expr(node.args[0].expr)
106 g.write(',')
107 if is_arg2_array {
108 g.expr(node.args[1].expr)
109 g.write('.arr,')
110 g.expr(node.args[1].expr)
111 g.write('.len')
112 } else {
113 g.expr(node.args[1].expr)
114 }
115 g.write(')')
116 return
117 }
118 'prepend' {
119 g.write('array_')
120 arg_sym := g.table.sym(node.args[0].typ)
121 is_arg_array := arg_sym.kind == .array && node.args[0].typ == node.left_type
122 if is_arg_array {
123 g.write('prepend_many(')
124 } else {
125 g.write('prepend(')
126 }
127 g.expr(it.left)
128 mut ltyp := it.left_type
129 for ltyp.is_ptr() {
130 g.write('.val')
131 ltyp = ltyp.deref()
132 }
133 g.write(',')
134 if is_arg_array {
135 g.expr(node.args[0].expr)
136 g.write('.arr, ')
137 g.expr(node.args[0].expr)
138 g.write('.len')
139 } else {
140 g.expr(node.args[0].expr)
141 }
142 g.write(')')
143 return
144 }
145 'sort' {
146 g.gen_array_sort(node)
147 }
148 else {}
149 }
150}
151
152fn (mut g JsGen) gen_array_index(node ast.CallExpr, is_last_index bool) {
153 fn_name := g.gen_array_index_method(node.left_type, is_last_index)
154 g.write('${fn_name}(')
155 g.expr(node.left)
156 g.gen_deref_ptr(node.left_type)
157 g.write(',')
158 g.expr(node.args[0].expr)
159 g.write(')')
160}
161
162fn (mut g JsGen) gen_array_contains(node ast.CallExpr) {
163 fn_name := g.gen_array_contains_method(node.left_type)
164 g.write('${fn_name}(')
165 g.expr(node.left)
166 g.gen_deref_ptr(node.left_type)
167 g.write(',')
168 g.expr(node.args[0].expr)
169 g.write(')')
170}
171
172fn (mut g JsGen) gen_array_contains_method(left_type ast.Type) string {
173 mut unwrap_left_type := g.unwrap_generic(left_type)
174 if unwrap_left_type.share() == .shared_t {
175 unwrap_left_type = unwrap_left_type.clear_flag(.shared_f)
176 }
177 mut left_sym := g.table.sym(unwrap_left_type)
178 left_final_sym := g.table.final_sym(unwrap_left_type)
179 mut left_type_str := g.styp(unwrap_left_type).replace('*', '')
180 fn_name := '${left_type_str}_contains'
181 if !left_sym.has_method('contains') {
182 left_info := left_final_sym.info as ast.Array
183 elem_sym := g.table.sym(left_info.elem_type)
184 if elem_sym.kind == .function {
185 left_type_str = 'Array_voidptr'
186 }
187
188 mut fn_builder := strings.new_builder(512)
189 fn_builder.writeln('function ${fn_name}(a,v) {')
190 fn_builder.writeln('\tfor (let i = 0; i < a.len; ++i) {')
191 if elem_sym.kind == .string {
192 fn_builder.writeln('\t\tif (a.arr.get(new int(i)).str == v.str) {')
193 } else if elem_sym.kind == .array && left_info.elem_type.nr_muls() == 0 {
194 ptr_typ := g.gen_array_equality_fn(left_info.elem_type)
195 fn_builder.writeln('\t\tif (${ptr_typ}_arr_eq(a.arr.get(new int(i)),v).val) {')
196 } else if elem_sym.kind == .function {
197 fn_builder.writeln('\t\tif (a.arr.get(new int(i)) == v) {')
198 } else if elem_sym.kind == .map && left_info.elem_type.nr_muls() == 0 {
199 ptr_typ := g.gen_map_equality_fn(left_info.elem_type)
200 fn_builder.writeln('\t\tif (${ptr_typ}_map_eq(a.arr.get(new int(i)),v).val) {')
201 } else if elem_sym.kind == .struct && left_info.elem_type.nr_muls() == 0 {
202 ptr_typ := g.gen_struct_equality_fn(left_info.elem_type)
203 fn_builder.writeln('\t\tif (${ptr_typ}_struct_eq(a.arr.get(new int(i)),v).val) {')
204 } else {
205 fn_builder.writeln('\t\tif (vEq(a.arr.get(new int(i)),v)) {')
206 }
207 fn_builder.writeln('\t\t\treturn new bool(true);')
208 fn_builder.writeln('\t\t}')
209 fn_builder.writeln('\t}')
210 fn_builder.writeln('\treturn new bool(false);')
211 fn_builder.writeln('}')
212 g.definitions.writeln(fn_builder.str())
213 left_sym.register_method(ast.Fn{
214 name: 'contains'
215 params: [ast.Param{
216 typ: unwrap_left_type
217 }, ast.Param{
218 typ: left_info.elem_type
219 }]
220 })
221 }
222 return fn_name
223}
224
225fn (mut g JsGen) gen_array_sort(node ast.CallExpr) {
226 rec_sym := g.table.sym(node.receiver_type)
227 if rec_sym.kind != .array {
228 println(node.name)
229 verror('.sort() is an array method')
230 }
231
232 info := rec_sym.info as ast.Array
233
234 elem_stype := g.styp(info.elem_type)
235 mut compare_fn := 'compare_${elem_stype.replace('*', '_ptr')}'
236 mut comparison_type := g.unwrap(ast.void_type)
237 mut left_expr, mut right_expr := '', ''
238
239 if node.args.len == 0 {
240 comparison_type = g.unwrap(info.elem_type.set_nr_muls(0))
241 if compare_fn in g.array_sort_fn {
242 g.gen_array_sort_call(node, compare_fn)
243 return
244 }
245
246 left_expr = 'a'
247 right_expr = 'b'
248 } else {
249 infix_expr := node.args[0].expr as ast.InfixExpr
250 comparison_type = g.unwrap(infix_expr.left_type.set_nr_muls(0))
251 left_name := infix_expr.left.str()
252 if left_name.len > 1 {
253 compare_fn += '_by' + left_name[1..].replace_each(['.', '_', '[', '_', ']', '_'])
254 }
255 // is_reverse is `true` for `.sort(a > b)` and `.sort(b < a)`
256 is_reverse := (left_name.starts_with('a') && infix_expr.op == .gt)
257 || (left_name.starts_with('b') && infix_expr.op == .lt)
258 if is_reverse {
259 compare_fn += '_reverse'
260 }
261 if compare_fn in g.array_sort_fn {
262 g.gen_array_sort_call(node, compare_fn)
263 return
264 }
265 if left_name.starts_with('a') != is_reverse {
266 left_expr = g.expr_string(infix_expr.left)
267 right_expr = g.expr_string(infix_expr.right)
268 } else {
269 left_expr = g.expr_string(infix_expr.right)
270 right_expr = g.expr_string(infix_expr.left)
271 }
272 }
273
274 // Register a new custom `compare_xxx` function for qsort()
275 // TODO: move to checker
276 g.table.register_fn(name: compare_fn, return_type: ast.int_type)
277 g.array_sort_fn[compare_fn] = true
278
279 g.definitions.writeln('function ${compare_fn}(a,b) {')
280 g.definitions.writeln('\ta = new \$ref(a);')
281 g.definitions.writeln('\tb = new \$ref(b);')
282 c_condition := if comparison_type.sym.has_method('<') {
283 '${g.styp(comparison_type.typ)}__lt(${left_expr}, ${right_expr})'
284 } else if comparison_type.unaliased_sym.has_method('<') {
285 '${g.styp(comparison_type.unaliased)}__lt(${left_expr}, ${right_expr})'
286 } else {
287 '${left_expr}.valueOf() < ${right_expr}.valueOf()'
288 }
289 g.definitions.writeln('\tif (${c_condition}) return -1;')
290 g.definitions.writeln('\telse return 1;')
291 g.definitions.writeln('}\n')
292
293 // write call to the generated function
294 g.gen_array_sort_call(node, compare_fn)
295}
296
297fn (mut g JsGen) gen_array_sort_call(node ast.CallExpr, compare_fn string) {
298 g.write('v_sort(')
299 g.expr(node.left)
300 g.gen_deref_ptr(node.left_type)
301 g.write(',${compare_fn})')
302}
303