v2 / vlib / v / parser / v_parser_test.v
505 lines · 464 sloc · 12.5 KB · edbe4301538f6d9783bbedff0e567917c6dd75ec
Raw
1// vtest build: tinyc && !sanitized_job?
2module parser
3
4// import v.eval
5import v.ast
6import v.gen.c
7import v.checker
8import v.pref
9import term
10import os
11import benchmark
12
13const vroot = os.dir(os.dir(os.dir(os.dir(@FILE))))
14
15fn test_eval() {
16 /*
17 inputs := [
18 //
19 '2+2',
20 'struct User { age int }',
21 '2+3',
22 '4',
23 'x := 10',
24 'x',
25 'x + 1',
26 'y := 2',
27 'x * y', // 20
28 //
29 ]
30 expected := [
31 //
32 '5',
33 '4',
34 '>>',
35 '10',
36 '11',
37 '>>',
38 '20',
39 //
40 ]
41 table := ast.new_table()
42 vpref := &pref.Preferences{}
43 mut scope := &ast.Scope{
44 start_pos: 0
45 parent: unsafe { nil }
46 }
47 mut stmts := []ast.Stmt{}
48 for input in inputs {
49 stmts << parse_stmt(input, table, scope)
50 }
51 file := &ast.File{
52 stmts: stmts
53 scope: scope
54 }
55 mut checker := checker.new_checker(table, vpref)
56 checker.check(file)
57 mut ev := eval.Eval{}
58 s := ev.eval(file, table)
59 println('eval done')
60 println(s)
61 assert s == expected.join('\n')
62 exit(0)
63 */
64 return
65}
66
67fn test_parse_text() {
68 println(@LOCATION)
69 source_text := '
70fn foo() int {
71 f := 23
72 return 10+4+f
73}
74fn ff(x int) {}
75
76fn main() {
77 ff(12 + 3)
78 x := 10
79 bar(5+7)
80 ff(8+x)
81}
82'
83 mut table := ast.new_table()
84 vpref := &pref.Preferences{}
85 mut prog := parse_text(source_text, '', mut table, .skip_comments, vpref)
86 mut checker_ := checker.new_checker(table, vpref)
87 checker_.check(mut prog)
88}
89
90fn test_one() {
91 if true {
92 return
93 }
94 println(@LOCATION)
95 input := ['a := 10', 'b := -a', 'c := 20']
96 expected := 'int a = 10;int b = -a;int c = 20;'
97 mut table := ast.new_table()
98 vpref := &pref.Preferences{}
99 mut scope := &ast.Scope{
100 start_pos: 0
101 }
102 mut e := []ast.Stmt{}
103 for line in input {
104 e << parse_stmt(line, mut table, mut scope)
105 }
106 mut program := &ast.File{
107 stmts: e
108 scope: scope
109 global_scope: scope
110 }
111 mut checker_ := checker.new_checker(table, vpref)
112 checker_.check(mut program)
113 result := c.gen([program], mut table, vpref)
114 res := result.res_builder.bytestr().replace('\n', '').trim_space().after('#endif')
115 println(res)
116 ok := expected == res
117 println(res)
118 dump(ok)
119 assert ok
120 if !ok {
121 }
122 // exit(0)
123}
124
125fn test_parse_expr() {
126 println(@LOCATION)
127 if true {
128 return
129 }
130 input := ['1 == 1', '234234', '2 * 8 + 3', 'a := 3', 'a++', 'b := 4 + 2', 'neg := -a', 'a + a',
131 'bo := 2 + 3 == 5', '2 + 1', 'q := 1', 'q + 777', '2 + 3', '2+2*4', 'x := 10', 'mut aa := 12',
132 'ab := 10 + 3 * 9', 's := "hi"', 'x = 11', 'a += 10', '1.2 + 3.4', '4 + 4', '1 + 2 * 5',
133 '-a+1', '2+2']
134 expecting := ['1 == 1;', '234234;', '2 * 8 + 3;', 'int a = 3;', 'a++;', 'int b = 4 + 2;',
135 'int neg = -a;', 'a + a;', 'bool bo = 2 + 3 == 5;', '2 + 1;', 'int q = 1;', 'q + 777;',
136 '2 + 3;', '2 + 2 * 4;', 'int x = 10;', 'int aa = 12;', 'int ab = 10 + 3 * 9;',
137 'string s = tos3("hi");', 'x = 11;', 'a += 10;', '1.2 + 3.4;', '4 + 4;', '1 + 2 * 5;',
138 '-a + 1;', '2 + 2;']
139 mut e := []ast.Stmt{}
140 mut table := ast.new_table()
141 vpref := &pref.Preferences{}
142 mut chk := checker.new_checker(table, vpref)
143 mut scope := &ast.Scope{
144 start_pos: 0
145 }
146 for s in input {
147 println('\n\nst="${s}"')
148 e << parse_stmt(s, mut table, mut scope)
149 }
150 mut program := &ast.File{
151 stmts: e
152 scope: scope
153 global_scope: scope
154 }
155 chk.check(mut program)
156 result := c.gen([program], mut table, vpref)
157 res := result.res_builder.bytestr().after('#endif')
158 println('========')
159 println(res)
160 println('========')
161 lines := res.trim_space().split_into_lines()
162 mut i := 0
163 for line in lines {
164 if line == '' {
165 continue
166 }
167 if line != expecting[i] {
168 println('V:"${line}" expecting:"${expecting[i]}"')
169 }
170 assert line == expecting[i]
171 println(term.green('${i} OK'))
172 println(line)
173 println('')
174 i++
175 if i >= expecting.len {
176 break
177 }
178 }
179}
180
181fn test_num_literals() {
182 println(@LOCATION)
183 inputs := [
184 'a := -1',
185 'b := -12.e17',
186 'c := -12.',
187 'd := -a',
188 ]
189 mut table := ast.new_table()
190 mut scope := &ast.Scope{
191 start_pos: 0
192 }
193 mut rhs_types := []string{}
194 for input in inputs {
195 stmt := parse_stmt(input, mut table, mut scope)
196 r := (stmt as ast.AssignStmt).right
197 match r[0] {
198 ast.IntegerLiteral { rhs_types << 'int literal' }
199 ast.FloatLiteral { rhs_types << 'float literal' }
200 ast.PrefixExpr { rhs_types << 'prefix expression' }
201 else { rhs_types << 'something else' }
202 }
203 }
204 mut rhs_type := rhs_types[0]
205 assert rhs_type == 'int literal'
206 rhs_type = rhs_types[1]
207 assert rhs_type == 'float literal'
208 rhs_type = rhs_types[2]
209 assert rhs_type == 'float literal'
210 rhs_type = rhs_types[3]
211 assert rhs_type == 'prefix expression'
212}
213
214/*
215table := &ast.Table{}
216for s in text_expr {
217 // print using str method
218 x := parse_expr(s, table)
219 println('source: ${s}')
220 println('parsed: ${x}')
221 println('===================')
222}
223*/
224
225fn test_fn_is_html_open_tag() {
226 println(@LOCATION)
227 mut s := '<style>'
228 mut b := is_html_open_tag('style', s)
229 assert b == true
230
231 s = '<style media="print" custom-attr >'
232 b = is_html_open_tag('style', s)
233 assert b == true
234
235 s = '<style/>'
236 b = is_html_open_tag('style', s)
237 assert !b
238
239 s = 'styl'
240 b = is_html_open_tag('style', s)
241 assert !b
242
243 s = 'style'
244 b = is_html_open_tag('style', s)
245 assert !b
246
247 s = '<style'
248 b = is_html_open_tag('style', s)
249 assert !b
250
251 s = '<<style>'
252 b = is_html_open_tag('style', s)
253 assert !b
254
255 s = '<style>>'
256 b = is_html_open_tag('style', s)
257 assert !b
258
259 s = '<stylex>'
260 b = is_html_open_tag('style', s)
261 assert !b
262
263 s = '<html>'
264 b = is_html_open_tag('style', s)
265 assert !b
266
267 s = '<script>'
268 b = is_html_open_tag('style', s)
269 assert !b
270}
271
272fn scan_v(mut files []string, path string) ! {
273 for i in os.ls(path)! {
274 p := os.join_path(path, i)
275 if !os.exists(p) {
276 continue
277 }
278 if os.is_file(p) {
279 if i.ends_with('.v') && !i.contains_any_substr(['_test.', 'test_', 'tests_']) {
280 files << p
281 }
282 } else {
283 if !i.starts_with('test') && !i.ends_with('_tests') && !i.ends_with('builtin') {
284 scan_v(mut files, p)!
285 }
286 }
287 }
288}
289
290fn parse(output_mode pref.OutputMode) ! {
291 mut b := benchmark.start()
292 mut files := []string{}
293 // mode_files := os.walk_ext(os.join_path(vroot, 'vlib/v/parser/testdata/${output_mode}'), '.vv')
294 // files << mode_files
295 scan_v(mut files, os.join_path(vroot, 'vlib'))!
296 scan_v(mut files, os.join_path(vroot, 'cmd'))!
297 mut pref_ := pref.new_preferences()
298 pref_.output_mode = output_mode
299 for idx, f in files {
300 // eprintln('> parsing in mode: ${output_mode}, ${idx+1:5}/${files.len} ${f} ...')
301 mut table := ast.new_table()
302 p := parse_file(f, mut table, .parse_comments, pref_)
303 assert !isnil(p), 'failed to parse `${f}` in mode: ${output_mode}'
304 assert p.errors.len == 0, 'file ${f} should have been parsed with 0 errors'
305 }
306 b.measure('parsing ${files.len} files in ${output_mode} mode')
307}
308
309fn test_parse_with_silent() {
310 println(@LOCATION)
311 parse(.silent)!
312}
313
314fn test_parse_with_stdout() {
315 println(@LOCATION)
316 $if windows {
317 // On Windows with TCC, parsing in stdout mode can crash due to
318 // exit() being called inside the parser on parse errors, which
319 // corrupts memory under TCC's runtime.
320 eprintln('> skipping stdout mode parsing test on Windows')
321 return
322 }
323 parse(.stdout)!
324}
325
326fn test_parse_vls_info() {
327 println(@LOCATION)
328 source_text := '
329// not_my_const comment line1
330// my_const comment line2
331const my_const = 123 // my_const end_comment
332
333// not_MyS comment line1
334// MyS comment line2
335struct MyS {
336 // a comment line1
337 // a comment line2
338 a int // a end_comment
339 // b comment line1
340 // b comment line2
341 b int // b end_comment
342}
343
344// not_MyInterface comment line1
345// MyInterface comment line2
346interface MyInterface {
347 // method comment line1
348 // method comment line2
349 method() string // method end_comment
350 // data comment line1
351 // data comment line2
352 data int // data end_comment
353}
354
355// not_my_global comment line1
356// my_global comment line2
357__global my_global = 456 // my_global end_comment
358
359// not_MyAlias comment line1
360// MyAlias comment line2
361type MyAlias = u8 // MyAlias end_comment
362
363// not_MySum comment line1
364// MySum comment line2
365type MySum = u8 | u16 // MySum end_comment
366
367// not_MyFnType comment line1
368// MyFnType comment line2
369type MyFnType = fn (msg &char, arg usize) // MyFnType end_comment
370
371
372// not_MyEnum comment line1
373// MyEnum comment line2
374enum MyEnum {
375 // x comment line1
376 // x comment line2
377 x // x end_comment
378 // y comment line1
379 // y comment line2
380 y // y end_comment
381}
382
383// not_add comment line1
384// add comment line2
385fn (mut k MyS) add(val int) {
386 k.a += val
387}
388
389
390// not_foo comment line1
391// foo comment line2
392fn foo() int {
393 f := 23
394 return 10+4+f
395}
396
397// not_ff comment line1
398// ff comment line2
399fn ff(x int) {} // ff end_comment
400
401// not_main comment line1
402// main comment line2
403fn main() {
404 ff(12 + 3)
405 x := 10
406 bar(5+7)
407 ff(8+x)
408}
409'
410 mut table := ast.new_table()
411 vpref := &pref.Preferences{
412 enable_globals: true
413 is_vls: true
414 }
415 mut prog := parse_text(source_text, '', mut table, .parse_comments, vpref)
416 mut checker_ := checker.new_checker(table, vpref)
417 checker_.check(mut prog)
418
419 assert 'const_main.my_const' in table.vls_info
420 assert 'struct_main.MyS' in table.vls_info
421 assert 'struct_main.MyS.a' in table.vls_info
422 assert 'struct_main.MyS.b' in table.vls_info
423 assert 'interface_main.MyInterface' in table.vls_info
424 assert 'fn_main[MyInterface]method' in table.vls_info // MyInterface method
425 assert 'interface_main.MyInterface.data' in table.vls_info
426 assert 'fn_main[MyS]add' in table.vls_info // MyS method
427 assert 'fn_main[]foo' in table.vls_info
428 assert 'global_main.my_global' in table.vls_info // module specific global
429 assert 'global_my_global' in table.vls_info
430 assert 'aliastype_main.MyAlias' in table.vls_info
431 assert 'sumtype_main.MySum' in table.vls_info
432 assert 'fntype_main.MyFnType' in table.vls_info
433 assert 'enum_main.MyEnum' in table.vls_info
434 assert 'enum_main.MyEnum.x' in table.vls_info
435 assert 'enum_main.MyEnum.y' in table.vls_info
436 dump(table.vls_info['const_main.my_const'])
437 dump(table.vls_info['struct_main.MyS'])
438 dump(table.vls_info['struct_main.MyS.a'])
439 dump(table.vls_info['struct_main.MyS.b'])
440 dump(table.vls_info['interface_main.MyInterface'])
441 dump(table.vls_info['fn_main[MyInterface]method'])
442 dump(table.vls_info['interface_main.MyInterface.data'])
443 dump(table.vls_info['fn_main[MyS]add'])
444 dump(table.vls_info['fn_main[]foo'])
445 dump(table.vls_info['global_main.my_global'])
446 dump(table.vls_info['global_my_global'])
447 dump(table.vls_info['aliastype_main.MyAlias'])
448 dump(table.vls_info['sumtype_main.MySum'])
449 dump(table.vls_info['fntype_main.MyFnType'])
450 dump(table.vls_info['enum_main.MyEnum'])
451 dump(table.vls_info['enum_main.MyEnum.x'])
452 dump(table.vls_info['enum_main.MyEnum.y'])
453 assert table.vls_info['const_main.my_const'].doc.trim_space() == 'my_const comment line2
454my_const end_comment'
455 assert table.vls_info['struct_main.MyS'].doc.trim_space() == 'MyS comment line2'
456 assert table.vls_info['struct_main.MyS.a'].doc.trim_space() == 'a comment line1
457a comment line2
458a end_comment'
459 assert table.vls_info['struct_main.MyS.b'].doc.trim_space() == 'b comment line1
460b comment line2
461b end_comment'
462 assert table.vls_info['interface_main.MyInterface'].doc.trim_space() == 'MyInterface comment line2'
463 assert table.vls_info['fn_main[MyInterface]method'].doc.trim_space() == 'method comment line1
464method comment line2
465method end_comment'
466 assert table.vls_info['interface_main.MyInterface.data'].doc.trim_space() == 'data comment line1
467data comment line2
468data end_comment'
469 assert table.vls_info['fn_main[MyS]add'].doc.trim_space() == 'add comment line2'
470 assert table.vls_info['fn_main[]foo'].doc.trim_space() == 'foo comment line2'
471 assert table.vls_info['global_main.my_global'].doc.trim_space() == 'my_global comment line2'
472 assert table.vls_info['global_my_global'].doc.trim_space() == 'my_global comment line2'
473 assert table.vls_info['aliastype_main.MyAlias'].doc.trim_space() == 'MyAlias comment line2
474MyAlias end_comment'
475 assert table.vls_info['sumtype_main.MySum'].doc.trim_space() == 'MySum comment line2
476MySum end_comment'
477 assert table.vls_info['fntype_main.MyFnType'].doc.trim_space() == 'MyFnType comment line2
478MyFnType end_comment'
479 assert table.vls_info['enum_main.MyEnum'].doc.trim_space() == 'MyEnum comment line2'
480 assert table.vls_info['enum_main.MyEnum.x'].doc.trim_space() == 'x comment line1
481x comment line2
482x end_comment'
483 assert table.vls_info['enum_main.MyEnum.y'].doc.trim_space() == 'y comment line1
484y comment line2
485y end_comment'
486}
487
488fn test_no_closure_auto_import_for_field_names() {
489 vpref := &pref.Preferences{}
490 for field_name in ast.builtin_array_generic_methods {
491 source := 'struct Sample {
492mut:
493 ${field_name} u32
494}
495
496fn use_field(mut sample Sample) {
497 _ = sample.${field_name}
498 sample.${field_name}++
499}
500'
501 mut table := ast.new_table()
502 prog := parse_text(source, '', mut table, .skip_comments, vpref)
503 assert 'builtin.closure' !in prog.auto_imports
504 }
505}
506