File Coverage

blib/lib/Mylisp/LintAst.pm
Criterion Covered Total %
statement 29 477 6.0
branch 0 48 0.0
condition n/a
subroutine 10 43 23.2
pod 0 33 0.0
total 39 601 6.4


line stmt bran cond sub pod time code
1             package Mylisp::LintAst;
2              
3 1     1   14 use 5.012;
  1         3  
4 1     1   5 no warnings "experimental";
  1         1  
  1         32  
5              
6 1     1   4 use Exporter;
  1         2  
  1         64  
7             our @ISA = qw(Exporter);
8             our @EXPORT =
9             qw(lint_mylisp_ast init_mylisp_lint use_package load_package load_ast my_func get_mylisp_atoms_value my_const lint_mylisp_atoms lint_mylisp_atom lint_mylisp_cond_atoms lint_mylisp_exprs lint_mylisp_func get_return_type_str lint_mylisp_return get_atoms_type_str lint_mylisp_my lint_mylisp_our lint_mylisp_call get_args_type_str lint_mylisp_for lint_mylisp_set lint_mylisp_sym_list lint_mylisp_sym get_atom_type get_sym_type get_call_type get_array_type get_arange_type get_iter_type lint_mylisp_aindex get_aindex_type get_index_type);
10 1     1   5 use Spp::Builtin;
  1         2  
  1         165  
11 1     1   5 use Spp::Tools;
  1         2  
  1         94  
12 1     1   5 use Spp;
  1         2  
  1         44  
13 1     1   6 use Mylisp::Grammar;
  1         1  
  1         29  
14 1     1   5 use Mylisp::OptAst;
  1         2  
  1         112  
15 1     1   6 use Mylisp::Lint;
  1         2  
  1         52  
16 1     1   5 use Mylisp::Type;
  1         2  
  1         2787  
17              
18             sub lint_mylisp_ast {
19 0     0 0   my ($t, $ast) = @_;
20 0           init_mylisp_lint($t, $ast);
21 0           print "init ok ..\n";
22 0           lint_mylisp_atoms($t, $ast);
23             }
24              
25             sub init_mylisp_lint {
26 0     0 0   my ($t, $ast) = @_;
27 0           for my $expr (@{ atoms($ast) }) {
  0            
28 0           my ($name, $args) = flat($expr);
29 0           update_pos($t, $expr);
30 0           given ($name) {
31 0           when ('package') { in_package($t, $args) }
  0            
32 0           when ('use') { use_package($t, $args) }
  0            
33 0           when ('const') { my_const($t, $args) }
  0            
34 0           when ('func') { my_func($t, $args) }
  0            
35             }
36             }
37             }
38              
39             sub use_package {
40 0     0 0   my ($t, $args) = @_;
41 0           load_package($t, $args);
42 0           my $table = $t->{'st'}{$args};
43 0           for my $name (keys %{$table}) {
  0            
44 0 0         next if start_with($name, '_');
45 0           my $value = $table->{$name};
46 0           set_name_value($t, $name, $value);
47             }
48             }
49              
50             sub load_package {
51 0     0 0   my ($t, $package) = @_;
52 0           my $dirs = [split '::', $package];
53 0           my $path = join '/', @{$dirs};
  0            
54 0           my $ast_file = add($path, ".spp.estr");
55 0           my $ast = read_file($ast_file);
56 0           print "load ast $ast_file ok\n";
57 0           load_ast($t, $ast);
58             }
59              
60             sub load_ast {
61 0     0 0   my ($t, $ast) = @_;
62 0           for my $expr (@{ atoms($ast) }) {
  0            
63 0           my ($name, $args) = flat($expr);
64 0           update_pos($t, $expr);
65 0           given ($name) {
66 0           when ('package') { in_package($t, $args) }
  0            
67 0           when ('func') { my_func($t, $args) }
  0            
68 0           when ('const') { my_const($t, $args) }
  0            
69 0           when ('end') { out_ns($t) }
  0            
70             }
71             }
72             }
73              
74             sub my_func {
75 0     0 0   my ($t, $atoms) = @_;
76 0           my ($name_args, $return) = flat($atoms);
77 0           my $return_type = get_mylisp_atoms_value(value($return));
78 0           my ($name, $args) = flat($name_args);
79 0 0         if (is_blank($args)) {
80 0           set_name_value($t, $name, $return_type);
81             }
82             else {
83 0           my $args_type = get_mylisp_atoms_value($args);
84 0           my $value = cons($args_type, $return_type);
85 0           set_name_value($t, $name, $value);
86             }
87             }
88              
89             sub get_mylisp_atoms_value {
90 0     0 0   my $atoms = shift;
91 0           my $names = [map { value($_) } @{ atoms($atoms) }];
  0            
  0            
92 0           return join ' ', @{$names};
  0            
93             }
94              
95             sub my_const {
96 0     0 0   my ($t, $args) = @_;
97 0           my ($sym, $value) = flat($args);
98 0           my $name = value($sym);
99 0           my $value_type = get_atom_type($t, $value);
100 0           set_name_value($t, $name, $value_type);
101             }
102              
103             sub lint_mylisp_atoms {
104 0     0 0   my ($t, $atoms) = @_;
105 0           for my $atom (@{ atoms($atoms) }) {
  0            
106 0           lint_mylisp_atom($t, $atom);
107             }
108             }
109              
110             sub lint_mylisp_atom {
111 0     0 0   my ($t, $atom) = @_;
112 0           my ($name, $args) = flat($atom);
113 0           update_pos($t, $atom);
114 0           given ($name) {
115 0           when ('func') { lint_mylisp_func($t, $args) }
  0            
116 0           when ('return') { lint_mylisp_return($t, $args) }
  0            
117 0           when ('my') { lint_mylisp_my($t, $args) }
  0            
118 0           when ('our') { lint_mylisp_our($t, $args) }
  0            
119 0           when ('=') { lint_mylisp_set($t, $args) }
  0            
120 0           when ('Sym') { lint_mylisp_sym($t, $args) }
  0            
121 0           when ('Aindex') { lint_mylisp_aindex($t, $args) }
  0            
122 0           when ('for') { lint_mylisp_for($t, $args) }
  0            
123 0           when ('while') { lint_mylisp_cond_atoms($t, $args) }
  0            
124 0           when ('given') { lint_mylisp_cond_atoms($t, $args) }
  0            
125 0           when ('when') { lint_mylisp_cond_atoms($t, $args) }
  0            
126 0           when ('then') { lint_mylisp_exprs($t, $args) }
  0            
127 0           when ('if') { lint_mylisp_cond_atoms($t, $args) }
  0            
128 0           when ('elif') { lint_mylisp_cond_atoms($t, $args) }
  0            
129 0           when ('else') { lint_mylisp_exprs($t, $args) }
  0            
130 0           when ('String') { lint_mylisp_atoms($t, $args) }
  0            
131 0           when ('Array') { lint_mylisp_atoms($t, $args) }
  0            
132 0           when ('Hash') { lint_mylisp_atoms($t, $args) }
  0            
133 0           when ('Pair') { lint_mylisp_atoms($t, $args) }
  0            
134 0           when ('end') { out_ns($t) }
  0            
135 0           when ('package') { return 1 }
  0            
136 0           when ('const') { return 1 }
  0            
137 0           when ('Str') { return 1 }
  0            
138 0           when ('Lstr') { return 1 }
  0            
139 0           when ('Int') { return 1 }
  0            
140 0           when ('Char') { return 1 }
  0            
141 0           when ('Bool') { return 1 }
  0            
142 0           when ('Type') { return 1 }
  0            
143 0           when ('Ns') { return 1 }
  0            
144 0           when ('use') { return 1 }
  0            
145 0           when ('import') { return 1 }
  0            
146 0           when ('->') { return 1 }
  0            
147 0           default { lint_mylisp_call($t, $name, $args) }
  0            
148             }
149             }
150              
151             sub lint_mylisp_cond_atoms {
152 0     0 0   my ($t, $atoms) = @_;
153 0           my ($cond_atom, $exprs) = match($atoms);
154 0           lint_mylisp_atom($t, $cond_atom);
155 0           lint_mylisp_exprs($t, $exprs);
156             }
157              
158             sub lint_mylisp_exprs {
159 0     0 0   my ($t, $exprs) = @_;
160 0           my $uuid = uuid();
161 0           in_ns($t, $uuid);
162 0           lint_mylisp_atoms($t, $exprs);
163 0           out_block($t, $uuid);
164             }
165              
166             sub lint_mylisp_func {
167 0     0 0   my ($t, $args) = @_;
168 0           my ($name_args, $rest) = match($args);
169 0           my ($return, $atoms) = match($rest);
170 0           my $return_type_str = get_return_type_str($return);
171 0           $t->{'return'} = $return_type_str;
172 0           my ($call, $func_args) = flat($name_args);
173 0           in_ns($t, $call);
174 0           for my $arg (@{ atoms($func_args) }) {
  0            
175 0           my ($name, $type) = flat($arg);
176 0           set_name_value($t, $name, $type);
177             }
178 0           lint_mylisp_atoms($t, $atoms);
179 0           out_ns($t);
180             }
181              
182             sub get_return_type_str {
183 0     0 0   my $expr = shift;
184 0           my $args = value($expr);
185 0           my $names = [map { value($_) } @{ atoms($args) }];
  0            
  0            
186 0           return join '', @{$names};
  0            
187             }
188              
189             sub lint_mylisp_return {
190 0     0 0   my ($t, $args) = @_;
191 0           my $call = ns($t);
192 0           my $return_type = $t->{'return'};
193 0           my $args_type_str = get_atoms_type_str($t, $args);
194 0           my $args_pat = pat_to_type_rule($t, $args_type_str);
195 0           my $p = $t->{'parser'};
196 0           my $match = match_type($p, $args_pat, $return_type);
197 0           lint_mylisp_atoms($t, $args);
198              
199 0 0         if (is_false($match)) {
200 0           say "return type is not same with call declare";
201 0           report($t, "$args_type_str != $return_type");
202             }
203             }
204              
205             sub get_atoms_type_str {
206 0     0 0   my ($t, $atoms) = @_;
207 0           my $types = [];
208 0           for my $atom (@{ atoms($atoms) }) {
  0            
209 0           push @{$types}, get_atom_type($t, $atom);
  0            
210             }
211 0           return join ' ', @{$types};
  0            
212             }
213              
214             sub lint_mylisp_my {
215 0     0 0   my ($t, $args) = @_;
216 0           my ($sym, $value) = flat($args);
217 0           lint_mylisp_atom($t, $value);
218 0           my $type = get_atom_type($t, $value);
219 0           my $name = value($sym);
220 0 0         if (is_str($type)) { set_name_value($t, $name, $type) }
  0            
221 0           else { report($t, "one sym accept more assign") }
222             }
223              
224             sub lint_mylisp_our {
225 0     0 0   my ($t, $args) = @_;
226 0           my ($array, $value) = flat($args);
227 0           lint_mylisp_atom($t, $value);
228 0           my $type = get_atom_type($t, $value);
229 0           my $types = [split ' ', $type];
230 0           my $syms = value($array);
231 0           lint_mylisp_sym_list($t, $syms);
232 0           my ($a, $b) = flat($syms);
233              
234 0 0         if (len($types) != 2) {
235 0           report($t, "return value not two");
236             }
237 0           my $a_name = value($a);
238 0           my $b_name = value($b);
239 0           my $a_type = $types->[0];
240 0           my $b_type = $types->[1];
241 0           set_name_value($t, $a_name, $a_type);
242 0           set_name_value($t, $b_name, $b_type);
243             }
244              
245             sub lint_mylisp_call {
246 0     0 0   my ($t, $name, $args) = @_;
247 0           my $value = get_name_value($t, $name);
248 0 0         if (is_blank($args)) {
249 0 0         if (is_estr($value)) {
250 0           report($t, "call |$name| less argument");
251             }
252 0           return 1;
253             }
254 0 0         if (is_str($value)) {
255 0           report($t, "call |$name| more argument");
256             }
257 0           my $call_type_str = name($value);
258 0           lint_mylisp_atoms($t, $args);
259 0           my $args_type_str = get_args_type_str($t, $args);
260 0           my $p = $t->{'parser'};
261 0           my $call_rule = pat_to_type_rule($t, $call_type_str);
262 0           my $match = match_type($p, $call_rule, $args_type_str);
263 0 0         if (is_false($match)) {
264 0           say "$call_type_str != $args_type_str";
265 0           report($t, "call |$name| args type not same!");
266             }
267             }
268              
269             sub get_args_type_str {
270 0     0 0   my ($t, $args) = @_;
271 0           my $type_strs = [];
272 0           for my $arg (@{ atoms($args) }) {
  0            
273 0           my $arg_type = get_atom_type($t, $arg);
274 0           push @{$type_strs}, $arg_type;
  0            
275             }
276 0           return join '', @{$type_strs};
  0            
277             }
278              
279             sub lint_mylisp_for {
280 0     0 0   my ($t, $args) = @_;
281 0           my ($iter_expr, $exprs) = match($args);
282 0           my ($name, $iter_atom) = flat($iter_expr);
283 0           my $type = get_iter_type($t, $iter_atom);
284 0           set_name_value($t, $name, $type);
285 0           lint_mylisp_exprs($t, $exprs);
286             }
287              
288             sub lint_mylisp_set {
289 0     0 0   my ($t, $args) = @_;
290 0           my ($sym, $value) = flat($args);
291 0           my $sym_type = get_atom_type($t, $sym);
292 0           my $value_type = get_atom_type($t, $value);
293 0 0         if ($sym_type ne $value_type) {
294 0           say "$sym_type != $value_type";
295 0           report($t, "assign type not same with define!");
296             }
297             }
298              
299             sub lint_mylisp_sym_list {
300 0     0 0   my ($t, $list) = @_;
301 0           for my $sym (@{ atoms($list) }) {
  0            
302 0 0         next if is_sym($sym);
303 0           report($t, "Symbol List have no variable!");
304             }
305             }
306              
307             sub lint_mylisp_sym {
308 0     0 0   my ($t, $name) = @_;
309 0 0         if (not(is_define($t, $name))) {
310 0           report($t, "not defined symbol: |$name|");
311             }
312             }
313              
314             sub get_atom_type {
315 0     0 0   my ($t, $atom) = @_;
316 0           my ($name, $args) = flat($atom);
317 0           update_pos($t, $atom);
318 0           given ($name) {
319 0           when ('Type') { return $args }
  0            
320 0           when ('Int') { return $name }
  0            
321 0           when ('Str') { return $name }
  0            
322 0           when ('Bool') { return $name }
  0            
323 0           when ('Hash') { return $name }
  0            
324 0           when ('Char') { return 'Str' }
  0            
325 0           when ('Lstr') { return 'Str' }
  0            
326 0           when ('String') { return 'Str' }
  0            
327 0           when ('Ns') { return 'Ns' }
  0            
328 0           when ('Array') { return get_array_type($t, $args) }
  0            
329 0           when ('Aindex') { return get_aindex_type($t, $args) }
  0            
330 0           when ('Sym') { return get_sym_type($t, $args) }
  0            
331 0           when ('if') { report($t, "$name as argument") }
  0            
332 0           when ('elif') { report($t, "$name as argument") }
  0            
333 0           when ('else') { report($t, "$name as argument") }
  0            
334 0           when ('given') { report($t, "$name as argument") }
  0            
335 0           when ('when') { report($t, "$name as argument") }
  0            
336 0           when ('then') { report($t, "$name as argument") }
  0            
337 0           when ('func') { report($t, "$name as argument") }
  0            
338 0           when ('my') { report($t, "$name as argument") }
  0            
339 0           when ('our') { report($t, "$name as argument") }
  0            
340 0           when ('use') { report($t, "$name as argument") }
  0            
341 0           when ('import') { report($t, "$name as argument") }
  0            
342 0           when ('package') { report($t, "$name as argument") }
  0            
343 0           when ('const') { report($t, "$name as argument") }
  0            
344 0           when ('for') { report($t, "$name as argument") }
  0            
345 0           when ('while') { report($t, "$name as argument") }
  0            
346 0           when ('return') { report($t, "$name as argument") }
  0            
347 0           default { return get_call_type($t, $name, $args) }
  0            
348             }
349             }
350              
351             sub get_sym_type {
352 0     0 0   my ($t, $args) = @_;
353 0           my $value = get_name_value($t, $args);
354 0 0         if (is_str($value)) { return $value }
  0            
355 0           return 'Fn';
356             }
357              
358             sub get_call_type {
359 0     0 0   my ($t, $name, $args) = @_;
360 0           my $value = get_name_value($t, $name);
361 0 0         if (is_str($value)) { return $value }
  0            
362 0           return value($value);
363             }
364              
365             sub get_array_type {
366 0     0 0   my ($t, $args) = @_;
367 0 0         if (is_blank($args)) { return 'Array' }
  0            
368 0           my $sub_type = get_atom_type($t, name($args));
369 0 0         if ($sub_type eq 'Int') { return 'Iarray' }
  0            
370 0           return 'Array';
371             }
372              
373             sub get_arange_type {
374 0     0 0   my ($t, $args) = @_;
375 0           my $sym = name($args);
376 0           my $type = get_atom_type($t, $sym);
377 0 0         if ($type eq 'Array') { return $type }
  0            
378 0           report($t, "Not Array arange");
379             }
380              
381             sub get_iter_type {
382 0     0 0   my ($t, $atom) = @_;
383 0           my $type = get_atom_type($t, $atom);
384 0           given ($type) {
385 0           when ('Array') { return 'Str' }
  0            
386 0           when ('Hash') { return 'Str' }
  0            
387 0           when ('Str') { return 'Str' }
  0            
388 0           when ('Iarray') { return 'Int' }
  0            
389 0           when ('Int+') { return 'Int' }
  0            
390 0           when ('Str+') { return 'Str' }
  0            
391 0           default { report($t, "|$type| could not index") }
  0            
392             }
393             }
394              
395             sub lint_mylisp_aindex {
396 0     0 0   my ($t, $args) = @_;
397 0           lint_mylisp_atoms($t, $args);
398             }
399              
400             sub get_aindex_type {
401 0     0 0   my ($t, $args) = @_;
402 0           my ($sym, $indexs) = match($args);
403 0           my $value = get_atom_type($t, $sym);
404 0           for my $index (@{ atoms($indexs) }) {
  0            
405 0           my $type = get_atom_type($t, $index);
406 0           my $name = value($index);
407 0           $value = get_index_type($t, $value, $type, $name);
408             }
409 0           return $value;
410             }
411              
412             sub get_index_type {
413 0     0 0   my ($t, $value, $type, $name) = @_;
414 0           given ($value) {
415 0           when ('Array') {
416 0 0         if ($type eq 'Int') { return 'Str' }
  0            
417 0           report($t, "Array index is: $type")
418             }
419 0           when ('Iarray') {
420 0 0         if ($type eq 'Int') { return 'Int' }
  0            
421 0           report($t, "Iarray index is $type")
422             }
423 0           when ('Hash') {
424 0 0         if ($type eq 'Str') { return 'Str' }
  0            
425 0           report($t, "Hash index is: |$type|")
426             }
427 0           when ('Stable') {
428 0 0         if ($type eq 'Str') { return 'Hash' }
  0            
429 0           report($t, "StrHash index is: |$type|")
430             }
431 0           when ('Cursor') {
432 0 0         if ($type eq 'Str') {
433 0           given ($name) {
434 0           when ('code') { return 'Str' }
  0            
435 0           when ('ns') { return 'Hash' }
  0            
436 0           when ('off') { return 'Int' }
  0            
437 0           when ('depth') { return 'Int' }
  0            
438 0           when ('length') { return 'Int' }
  0            
439 0           when ('line') { return 'Int' }
  0            
440 0           when ('maxline') { return 'Int' }
  0            
441 0           when ('maxoff') { return 'Int' }
  0            
442 0           default { report($t, "Cursor !exists $name") }
  0            
443             }
444             }
445 0           report($t, "Cursor index is: |$type|")
446             }
447 0           when ('Lint') {
448 0 0         if ($type eq 'Str') {
449 0           given ($name) {
450 0           when ('code') { return 'Str' }
  0            
451 0           when ('offline') { return 'Str' }
  0            
452 0           when ('st') { return 'Stable' }
  0            
453 0           when ('stack') { return 'Array' }
  0            
454 0           when ('parser') { return 'Parser' }
  0            
455 0           when ('patter') { return 'Str' }
  0            
456 0           when ('return') { return 'Str' }
  0            
457 0           default { report($t, "Lint !exists $name") }
  0            
458             }
459             }
460 0           report($t, "Lint index is: |$type|")
461             }
462 0           when ('Parser') {
463 0 0         if ($type eq 'Str') {
464 0           given ($name) {
465 0           when ('str') { return 'Str' }
  0            
466 0           when ('off') { return 'Int' }
  0            
467 0           when ('ns') { return 'Hash' }
  0            
468 0           default { report($t, "Parser not exists $name") }
  0            
469             }
470             }
471 0           report($t, "Parser index is: |$type|")
472             }
473 0           default { report($t, "Could not index: $value") }
  0            
474             }
475             }
476             1;