File Coverage

blib/lib/Mylisp/LintMyAst.pm
Criterion Covered Total %
statement 20 619 3.2
branch 0 120 0.0
condition n/a
subroutine 7 86 8.1
pod 0 79 0.0
total 27 904 2.9


line stmt bran cond sub pod time code
1             package Mylisp::LintMyAst;
2            
3 1     1   3070 use 5.012;
  1         4  
4 1     1   7 use experimental 'switch';
  1         3  
  1         6  
5            
6 1     1   166 use Exporter;
  1         3  
  1         96  
7             our @ISA = qw(Exporter);
8             our @EXPORT = qw(Report IsDefine Context InBlock InFunc InContext OutContext OutBlock GetIndent LintMyAst GetAtomType GetArrayType GetCallType);
9            
10 1     1   16 use Mylisp;
  1         4  
  1         81  
11 1     1   18 use Mylisp::Builtin;
  1         4  
  1         241  
12 1     1   9 use Mylisp::Estr;
  1         2  
  1         183  
13 1     1   8 use Mylisp::Match;
  1         3  
  1         11734  
14            
15             sub get_type_grammar {
16             return <<'EOF'
17            
18             door -> |\s+ Spec|+ $
19             Spec -> Rule '->' pat
20             pat -> |\h+ Branch More Maybe Token Str|+
21             Branch -> '|'
22             More -> Token'+'
23             Maybe -> Token'?'
24             Rule -> name
25             Token -> name
26             Str -> ':'\a+
27             name -> \a+
28            
29             EOF
30 0     0 0   ;;
31             }
32            
33             sub get_my_type_grammar {
34             return <<'EOF'
35            
36             Nil -> :Nil
37             Bool -> :Bool
38             Str -> :Str|:String|:Lstr|:Char
39             Int -> :Int
40             Strs -> :Strs
41             Ints -> :Ints
42             Table -> :Table
43             Tree -> :Tree
44             Fn -> :Fn
45             Atom -> Str|Int
46             Array -> Strs|Ints
47             Hash -> Table|Tree
48            
49             EOF
50 0     0 0   ;;
51             }
52            
53             sub get_type_table {
54 0     0 0   my $grammar = get_type_grammar;
55 0           my $ast = GrammarToAst($grammar);
56 0           return AstToTable($ast);
57             }
58            
59             sub get_my_type_table {
60 0     0 0   my $table = get_type_table;
61 0           my $grammar = get_my_type_grammar;
62 0           my ($match,$ok) = MatchTable($table,$grammar);
63 0 0         if (not($ok)) {
64 0           error("$match my-type-grammar syntax error");
65             }
66 0           my $ast = opt_type_match($match);
67 0           lint_type_ast($ast);
68 0           return AstToTable($ast);
69             }
70            
71             sub new_lint {
72 0     0 0   my $table = get_type_table();
73 0           my $mytable = get_my_type_table();
74 0           my $stack = ['main'];
75 0           return {'text' => '','locate' => '','stack' => $stack,'tree' => {},'ret' => '','depth' => 0,'pos' => 0,'count' => 0,'typetable' => $table,'mytypetable' => $mytable};
76             }
77            
78             sub pat_to_type_rule {
79 0     0 0   my ($t,$pat) = @_;
80 0           my $table = $t->{'typetable'};
81 0           my ($match,$ok) = MatchDoor($table,$pat,'pat');
82 0 0         if (not($ok)) {
83 0           Report($t,"pattern: |$pat| could not to rule!");
84             }
85 0           return opt_type_pat($match);
86             }
87            
88             sub opt_type_pat {
89 0     0 0   my $atoms = shift;
90 0           my $end = estr('End','e');
91 0 0         if (is_atom($atoms)) {
92 0           my $atom = opt_type_atom($atoms);
93 0           return estr('Rules',estr($atom,$end));
94             }
95 0           my $rule = opt_type_atoms($atoms);
96 0 0         if (is_branch($rule)) {
97 0           return estr('Rules',estr($rule,$end));
98             }
99 0           return epush($rule,$end);
100             }
101            
102             sub opt_type_match {
103 0     0 0   my $match = shift;
104 0 0         if (is_atom($match)) {
105 0           return opt_type_atom($match);
106             }
107 0           return estr_strs([ map { opt_type_atom($_) } @{atoms($match)} ]);
  0            
  0            
108             }
109            
110             sub opt_type_atom {
111 0     0 0   my $atom = shift;
112 0           my ($name,$value) = flat($atom);
113 0           given ($name) {
114 0           when ('Spec') {
115 0           return opt_type_spec($value);
116             }
117 0           when ('More') {
118 0           return opt_type_more($value);
119             }
120 0           when ('Maybe') {
121 0           return opt_type_maybe($value);
122             }
123 0           when ('Str') {
124 0           return opt_type_str($value);
125             }
126 0           default {
127 0           return estr($name,$value);
128             }
129             }
130             }
131            
132             sub opt_type_spec {
133 0     0 0   my $atoms = shift;
134 0           my ($token,$rules) = match($atoms);
135 0           my $name = value($token);
136 0           my $opt_rules = opt_type_atoms($rules);
137 0           return estr($name,$opt_rules);
138             }
139            
140             sub opt_type_atoms {
141 0     0 0   my $atoms = shift;
142 0           my $opt_atoms = [ map { opt_type_atom($_) } @{atoms($atoms)} ];
  0            
  0            
143 0           return gather_type_branch($opt_atoms);
144             }
145            
146             sub gather_type_branch {
147 0     0 0   my $atoms = shift;
148 0           my $branches = [];
149 0           my $branch = [];
150 0           my $flag = 0;
151 0           my $count = 0;
152 0           for my $atom (@{$atoms}) {
  0            
153 0 0         if (is_branch($atom)) {
154 0 0         if ($count > 1) {
    0          
155 0           apush($branches,estr('Rules',estr_strs($branch)));
156             }
157             elsif ($count == 0) {
158 0           croak("branch -> error locate");
159             }
160             else {
161 0           apush($branches,$branch->[0]);
162             }
163 0           $flag = 1;
164 0           $branch = [];
165 0           $count = 0;
166             }
167             else {
168 0           apush($branch,$atom);
169 0           $count++;;
170             }
171             }
172 0 0         if ($flag == 0) {
173 0 0         if ($count == 1) {
174 0           return $branch->[0];
175             }
176             else {
177 0           return estr('Rules',estr_strs($branch));
178             }
179             }
180 0 0         if ($count > 1) {
181 0           apush($branches,estr('Rules',estr_strs($branch)));
182             }
183             else {
184 0           apush($branches,$branch->[0]);
185             }
186 0           return estr('Branch',estr_strs($branches));
187             }
188            
189             sub is_branch {
190 0     0 0   my $atom = shift;
191 0           return is_atom_name($atom,'Branch');
192             }
193            
194             sub opt_type_more {
195 0     0 0   my $atoms = shift;
196 0           my $atom = first(atoms($atoms));
197 0           return estr('More',opt_type_atom($atom));
198             }
199            
200             sub opt_type_maybe {
201 0     0 0   my $atoms = shift;
202 0           my $atom = first(atoms($atoms));
203 0           return estr('Maybe',opt_type_atom($atom));
204             }
205            
206             sub opt_type_str {
207 0     0 0   my $str = shift;
208 0           return estr('Str',rest_str($str));
209             }
210            
211             sub lint_type_ast {
212 0     0 0   my $ast = shift;
213 0           my $table = {};
214 0           for my $atom (@{atoms($ast)}) {
  0            
215 0           my ($name,$value) = flat($atom);
216 0 0         if (exists $table->{$name}) {
217 0           say "repeat define type: |$name|";
218             }
219             else {
220 0           $table->{$name} = 'ok';
221 0           lint_type_atom($value,$table);
222             }
223             }
224             }
225            
226             sub lint_type_atom {
227 0     0 0   my ($rule,$t) = @_;
228 0           my ($name,$atoms) = flat($rule);
229 0 0         if (not($name ~~ ['Str','End'])) {
230 0           given ($name) {
231 0           when ('Rules') {
232 0           lint_type_atoms($atoms,$t);
233             }
234 0           when ('Branch') {
235 0           lint_type_atoms($atoms,$t);
236             }
237 0           when ('More') {
238 0           lint_type_atom($atoms,$t);
239             }
240 0           when ('Maybe') {
241 0           lint_type_atom($atoms,$t);
242             }
243 0           default {
244 0           lint_type_token($atoms,$t);
245             }
246             }
247             }
248             }
249            
250             sub lint_type_token {
251 0     0 0   my ($name,$table) = @_;
252 0 0         if (not(exists $table->{$name})) {
253 0           say "not exists type define: |$name|";
254             }
255             }
256            
257             sub lint_type_atoms {
258 0     0 0   my ($atoms,$table) = @_;
259 0           for my $atom (@{atoms($atoms)}) {
  0            
260 0           lint_type_atom($atom,$table);
261             }
262             }
263            
264             sub apply_char {
265 0     0 0   my $t = shift;
266 0           my $text = $t->{'text'};
267 0           my $pos = $t->{'pos'};
268 0           return substr($text, $pos, 1);
269             }
270            
271             sub load_text {
272 0     0 0   my ($t,$text) = @_;
273 0           $t->{'text'} = add($text,End);
274 0           $t->{'pos'} = 0;
275             }
276            
277             sub match_type {
278 0     0 0   my ($t,$rule,$text) = @_;
279 0           load_text($t,$text);
280 0           return match_type_rule($t,$rule);
281             }
282            
283             sub match_type_rule {
284 0     0 0   my ($t,$rule) = @_;
285 0           my ($name,$value) = flat($rule);
286 0           given ($name) {
287 0           when ('Rules') {
288 0           return match_type_rules($t,$value);
289             }
290 0           when ('Branch') {
291 0           return match_type_branch($t,$value);
292             }
293 0           when ('More') {
294 0           return match_type_more($t,$value);
295             }
296 0           when ('Maybe') {
297 0           return match_type_maybe($t,$value);
298             }
299 0           when ('Str') {
300 0           return match_type_str($t,$value);
301             }
302 0           when ('Token') {
303 0           return match_type_token($t,$value);
304             }
305 0           when ('End') {
306 0           return match_type_end($t,$value);
307             }
308 0           default {
309 0           croak("unknown rule: $name to match!");
310             }
311             }
312 0           return 0;
313             }
314            
315             sub match_type_rules {
316 0     0 0   my ($t,$rules) = @_;
317 0           for my $rule (@{atoms($rules)}) {
  0            
318 0 0         if (not(match_type_rule($t,$rule))) {
319 0           return 0;
320             }
321             }
322 0           return 1;
323             }
324            
325             sub match_type_branch {
326 0     0 0   my ($t,$branch) = @_;
327 0           my $pos = $t->{'pos'};
328 0           for my $rule (@{atoms($branch)}) {
  0            
329 0 0         if (match_type_rule($t,$rule)) {
330 0           return 1;
331             }
332 0           $t->{'pos'} = $pos;
333             }
334 0           return 0;
335             }
336            
337             sub match_type_token {
338 0     0 0   my ($t,$name) = @_;
339 0           while (is_space(apply_char($t))) {
340 0           $t->{'pos'}++;;
341             }
342 0           my $mytable = $t->{'mytypetable'};
343 0 0         if (not(exists $mytable->{$name})) {
344 0           Report($t,"not regist type: |$name|");
345             }
346 0           my $rule = $mytable->{$name};
347 0           return match_type_rule($t,$rule);
348             }
349            
350             sub match_type_more {
351 0     0 0   my ($t,$rule) = @_;
352 0           my $time = 0;
353 0           while (1) {
354 0           my $pos = $t->{'pos'};
355 0 0         if (not(match_type_rule($t,$rule))) {
356 0 0         if ($time == 0) {
357 0           return 0;
358             }
359 0           $t->{'pos'} = $pos;
360 0           return 1;
361             }
362 0           $time++;;
363             }
364 0           return 1;
365             }
366            
367             sub match_type_maybe {
368 0     0 0   my ($t,$rule) = @_;
369 0           my $cache = $t->{'pos'};
370 0 0         if (not(match_type_rule($t,$rule))) {
371 0           $t->{'pos'} = $cache;
372             }
373 0           return 1;
374             }
375            
376             sub match_type_str {
377 0     0 0   my ($t,$str) = @_;
378 0           for my $char (@{to_chars($str)}) {
  0            
379 0 0         if ($char ne apply_char($t)) {
380 0           return 0;
381             }
382 0           $t->{'pos'}++;;
383             }
384 0           return 1;
385             }
386            
387             sub match_type_end {
388 0     0 0   my ($t,$end) = @_;
389 0 0         if (apply_char($t) eq End) {
390 0           return 1;
391             }
392 0           return 0;
393             }
394            
395             sub Report {
396 0     0 0   my ($t,$message) = @_;
397 0           my $locate = $t->{'locate'};
398 0           my $line = value($locate);
399 0           error("error! line: $line $message");
400             }
401            
402             sub IsDefine {
403 0     0 0   my ($t,$name) = @_;
404 0           my $stack = $t->{'stack'};
405 0           for my $ns (@{$stack}) {
  0            
406 0           my $tree = $t->{'tree'};
407 0 0         if (exists $tree->{$ns}{$name}) {
408 0           return 1;
409             }
410             }
411 0           return 0;
412             }
413            
414             sub update_off {
415 0     0 0   my ($t,$atom) = @_;
416 0           $t->{'locate'} = off($atom);
417             }
418            
419             sub Context {
420 0     0 0   my $t = shift;
421 0           my $stack = $t->{'stack'};
422 0           return $stack->[0];
423             }
424            
425             sub InBlock {
426 0     0 0   my $t = shift;
427 0           my $ns = int_to_str($t->{'count'});
428 0           $t->{'count'}++;;
429 0           $t->{'depth'}++;;
430 0           InContext($t,$ns);
431             }
432            
433             sub InFunc {
434 0     0 0   my ($t,$ns) = @_;
435 0           $t->{'depth'}++;;
436 0           InContext($t,$ns);
437             }
438            
439             sub InContext {
440 0     0 0   my ($t,$ns) = @_;
441 0 0         if ($ns ne Context($t)) {
442 0           my $tree = $t->{'tree'};
443 0 0         if (not(exists $tree->{$ns})) {
444 0           $tree->{$ns} = {};
445             }
446 0           aunshift($ns,$t->{'stack'});
447             }
448             }
449            
450             sub OutContext {
451 0     0 0   my $t = shift;
452 0           ashift($t->{'stack'});
453             }
454            
455             sub OutBlock {
456 0     0 0   my $t = shift;
457 0           OutContext($t);
458 0           $t->{'depth'} --;
459             }
460            
461             sub GetIndent {
462 0     0 0   my $t = shift;
463 0           my $depth = $t->{'depth'};
464 0           return repeat(' ',$depth);
465             }
466            
467             sub set_name_value {
468 0     0 0   my ($t,$name,$value) = @_;
469 0           my $ns = Context($t);
470 0           my $tree = $t->{'tree'};
471 0 0         if (exists $tree->{$ns}{$name}) {
472 0           Report($t,"redefine exists symbol |$name|.");
473             }
474 0           $tree->{$ns}{$name} = $value;
475             }
476            
477             sub get_name_value {
478 0     0 0   my ($t,$name) = @_;
479 0           my $stack = $t->{'stack'};
480 0           my $tree = $t->{'tree'};
481 0           for my $ns (@{$stack}) {
  0            
482 0 0         if (exists $tree->{$ns}{$name}) {
483 0           return $tree->{$ns}{$name};
484             }
485             }
486 0           Report($t,"|$name| undefine!");
487 0           return '';
488             }
489            
490             sub LintMyAst {
491 0     0 0   my $ast = shift;
492 0           my $t = new_lint();
493 0           init_my_lint($t,$ast);
494 0           lint_my_atoms($t,$ast);
495 0           $t->{'count'} = 0;
496 0           return $t;
497             }
498            
499             sub init_my_lint {
500 0     0 0   my ($t,$ast) = @_;
501 0           for my $expr (@{atoms($ast)}) {
  0            
502 0           my ($name,$args) = flat($expr);
503 0           update_off($t,$expr);
504 0           given ($name) {
505 0           when ('package') {
506 0           InContext($t,$args);
507             }
508 0           when ('func') {
509 0           regist_func($t,$args);
510             }
511             }
512             }
513             }
514            
515             sub use_package {
516 0     0 0   my ($t,$package) = @_;
517 0           my $dirs = asplit('::',$package);
518 0           my $path = ajoin('/',$dirs);
519 0           my $ast_file = add($path,'.o');
520 0           my $ast = read_file($ast_file);
521 0           load_ast($t,$ast);
522             }
523            
524             sub load_ast {
525 0     0 0   my ($t,$ast) = @_;
526 0           for my $expr (@{atoms($ast)}) {
  0            
527 0           my ($name,$args) = flat($expr);
528 0           update_off($t,$expr);
529 0           given ($name) {
530 0           when ('const') {
531 0           regist_const($t,$args);
532             }
533 0           when ('type') {
534 0           regist_type($t,$args);
535             }
536 0           when ('struct') {
537 0           regist_struct($t,$args);
538             }
539 0           when ('func') {
540 0           regist_func($t,$args);
541             }
542             }
543             }
544             }
545            
546             sub regist_const {
547 0     0 0   my ($t,$args) = @_;
548 0           my ($sym,$value) = flat($args);
549 0           my $name = value($sym);
550 0           my $value_type = GetAtomType($t,$value);
551 0           set_name_value($t,$name,$value_type);
552             }
553            
554             sub regist_type {
555 0     0 0   my ($t,$args) = @_;
556 0           my ($sym,$type) = flat($args);
557 0           my $name = value($sym);
558 0           my $value = value($type);
559 0           set_name_value($t,$name,$value);
560             }
561            
562             sub regist_struct {
563 0     0 0   my ($t,$atom) = @_;
564 0           my ($type,$fields) = flat($atom);
565 0           my $type_value = estr('Table',$type);
566 0           set_name_value($t,$type,$type_value);
567 0           InContext($t,$type);
568 0           for my $field (@{atoms($fields)}) {
  0            
569 0           my ($name,$value) = flat($field);
570 0           set_name_value($t,$name,$value);
571             }
572 0           OutContext($t);
573             }
574            
575             sub regist_func {
576 0     0 0   my ($t,$atoms) = @_;
577 0           my ($name_args,$return) = flat($atoms);
578 0           my $return_type = get_my_atoms_value(value($return));
579 0           my ($name,$args) = flat($name_args);
580 0 0         if (is_blank($args)) {
581 0           set_name_value($t,$name,$return_type);
582             }
583             else {
584 0           my $args_type = get_my_atoms_value($args);
585 0           my $value = estr($args_type,$return_type);
586 0           set_name_value($t,$name,$value);
587             }
588             }
589            
590             sub get_my_atoms_value {
591 0     0 0   my $atoms = shift;
592 0           my $names = [ map { value($_) } @{atoms($atoms)} ];
  0            
  0            
593 0           return ajoin(' ',$names);
594             }
595            
596             sub get_return_type_str {
597 0     0 0   my $expr = shift;
598 0           my $args = value($expr);
599 0           my $names = [ map { value($_) } @{atoms($args)} ];
  0            
  0            
600 0           my $types = [ map { arg_type_to_return($_) } @{$names} ];
  0            
  0            
601 0           return ajoin(' ',$types);
602             }
603            
604             sub arg_type_to_return {
605 0     0 0   my $type = shift;
606 0           given ($type) {
607 0           when ('Str+') {
608 0           return 'Strs';
609             }
610 0           when ('Int+') {
611 0           return 'Ints';
612             }
613 0           when ('Str?') {
614 0           return 'Str';
615             }
616 0           when ('Int?') {
617 0           return 'Int';
618             }
619 0           default {
620 0           return $type;
621             }
622             }
623             }
624            
625             sub lint_my_atoms {
626 0     0 0   my ($t,$atoms) = @_;
627 0           for my $atom (@{atoms($atoms)}) {
  0            
628 0           lint_my_atom($t,$atom);
629             }
630             }
631            
632             sub lint_my_atom {
633 0     0 0   my ($t,$atom) = @_;
634 0           my ($name,$args) = flat($atom);
635 0 0         if (not($name ~~ ['package','Str','Lstr','Int','Bool','Char','->'])) {
636 0           update_off($t,$atom);
637 0           given ($name) {
638 0           when ('use') {
639 0           use_package($t,$args);
640             }
641 0           when ('const') {
642 0           regist_const($t,$args);
643             }
644 0           when ('type') {
645 0           regist_type($t,$args);
646             }
647 0           when ('struct') {
648 0           regist_struct($t,$args);
649             }
650 0           when ('Array') {
651 0           lint_my_atoms($t,$args);
652             }
653 0           when ('Aindex') {
654 0           lint_my_atoms($t,$args);
655             }
656 0           when ('Arange') {
657 0           lint_my_atoms($t,$args);
658             }
659 0           when ('func') {
660 0           lint_my_func($t,$args);
661             }
662 0           when (':ocall') {
663 0           lint_my_ocall($t,$args);
664             }
665 0           when ('return') {
666 0           lint_my_return($t,$args);
667             }
668 0           when ('my') {
669 0           lint_my_my($t,$args);
670             }
671 0           when ('our') {
672 0           lint_my_our($t,$args);
673             }
674 0           when ('set') {
675 0           lint_my_set($t,$args);
676             }
677 0           when ('Sym') {
678 0           lint_my_sym($t,$args);
679             }
680 0           when ('for') {
681 0           lint_my_for($t,$args);
682             }
683 0           when ('while') {
684 0           lint_my_exprs($t,$args);
685             }
686 0           when ('given') {
687 0           lint_my_exprs($t,$args);
688             }
689 0           when ('when') {
690 0           lint_my_exprs($t,$args);
691             }
692 0           when ('if') {
693 0           lint_my_exprs($t,$args);
694             }
695 0           when ('elif') {
696 0           lint_my_exprs($t,$args);
697             }
698 0           when ('then') {
699 0           lint_my_block($t,$args);
700             }
701 0           when ('else') {
702 0           lint_my_block($t,$args);
703             }
704 0           when ('Hash') {
705 0           lint_my_hash($t,$args);
706             }
707 0           when ('String') {
708 0           lint_my_string($t,$args);
709             }
710 0           default {
711 0           lint_my_call($t,$name,$args);
712             }
713             }
714             }
715             }
716            
717             sub lint_my_string {
718 0     0 0   my ($t,$strs) = @_;
719 0           for my $name (@{atoms($strs)}) {
  0            
720 0 0         if (start_with($name,'$')) {
721 0 0         next if IsDefine($t,$name);
722 0           Report($t,"undefine Variable: |$name|");
723             }
724             }
725             }
726            
727             sub lint_my_hash {
728 0     0 0   my ($t,$pairs) = @_;
729 0           for my $pair (@{atoms($pairs)}) {
  0            
730 0           lint_my_atom($t,value($pair));
731             }
732             }
733            
734             sub lint_my_exprs {
735 0     0 0   my ($t,$atoms) = @_;
736 0           my ($cond_atom,$exprs) = match($atoms);
737 0           lint_my_atom($t,$cond_atom);
738 0           lint_my_block($t,$exprs);
739             }
740            
741             sub lint_my_block {
742 0     0 0   my ($t,$exprs) = @_;
743 0           InBlock($t);
744 0           lint_my_atoms($t,$exprs);
745 0           OutBlock($t);
746             }
747            
748             sub lint_my_func {
749 0     0 0   my ($t,$args) = @_;
750 0           my ($name_args,$rest) = match($args);
751 0           my ($return,$atoms) = match($rest);
752 0           my $return_type_str = get_return_type_str($return);
753 0           $t->{'ret'} = $return_type_str;
754 0           my ($call,$func_args) = flat($name_args);
755 0           InFunc($t,$call);
756 0           for my $arg (@{atoms($func_args)}) {
  0            
757 0           my ($name,$type) = flat($arg);
758 0           $type = arg_type_to_return($type);
759 0           set_name_value($t,$name,$type);
760             }
761 0           lint_my_atoms($t,$atoms);
762 0           OutBlock($t);
763             }
764            
765             sub lint_my_return {
766 0     0 0   my ($t,$args) = @_;
767 0           my $return_type = $t->{'ret'};
768 0           my $args_type_str = get_args_type_str($t,$args);
769 0 0         if ($return_type ne $args_type_str) {
770 0           my $args_pat = pat_to_type_rule($t,$args_type_str);
771 0           lint_my_atoms($t,$args);
772 0 0         if (not(match_type($t,$args_pat,$return_type))) {
773 0           say "|$args_type_str| != |$return_type|";
774 0           Report($t,"return type != declare type|");
775             }
776             }
777             }
778            
779             sub get_args_type_str {
780 0     0 0   my ($t,$atoms) = @_;
781 0           my $types = [];
782 0           for my $atom (@{atoms($atoms)}) {
  0            
783 0           apush($types,GetAtomType($t,$atom));
784             }
785 0           return ajoin(' ',$types);
786             }
787            
788             sub lint_my_my {
789 0     0 0   my ($t,$args) = @_;
790 0           my ($sym,$value) = flat($args);
791 0           lint_my_atom($t,$value);
792 0           my $type = GetAtomType($t,$value);
793 0           my $name = value($sym);
794 0 0         if (is_str($type)) {
795 0           set_name_value($t,$name,$type);
796             }
797             else {
798 0           Report($t,"one sym accept more assign");
799             }
800             }
801            
802             sub lint_my_our {
803 0     0 0   my ($t,$args) = @_;
804 0           my ($array,$value) = flat($args);
805 0           lint_my_atom($t,$value);
806 0           my $type = GetAtomType($t,$value);
807 0           my $types = asplit(' ',$type);
808 0           my $syms = value($array);
809 0           my ($a,$b) = flat($syms);
810 0 0         if (len($types) != 2) {
811 0           Report($t,"my return value not two");
812             }
813 0           my $a_name = value($a);
814 0           my $b_name = value($b);
815 0           my $a_type = $types->[0];
816 0           my $b_type = $types->[1];
817 0           set_name_value($t,$a_name,$a_type);
818 0           set_name_value($t,$b_name,$b_type);
819             }
820            
821             sub lint_my_ocall {
822 0     0 0   my ($t,$ocall) = @_;
823 0           my ($sym,$call) = flat($ocall);
824 0           my $type = get_name_value($t,$sym);
825 0           my $tree = $t->{'tree'};
826 0 0         if (not(exists $tree->{$type}{$call})) {
827 0           Report($t,"ocall |$call| not define!");
828             }
829             }
830            
831             sub lint_my_call {
832 0     0 0   my ($t,$name,$args) = @_;
833 0           my $value = get_name_value($t,$name);
834 0 0         if (is_blank($args)) {
835 0 0         if (not(is_str($value))) {
836 0           Report($t,"call |$name| less argument");
837             }
838             }
839             else {
840 0 0         if (is_str($value)) {
841 0           Report($t,"call |$name| more argument");
842             }
843 0           my $call_str = name($value);
844 0           lint_my_atoms($t,$args);
845 0           my $args_str = get_args_type_str($t,$args);
846 0 0         if ($call_str ne $args_str) {
847 0           my $call_rule = pat_to_type_rule($t,$call_str);
848 0 0         if (not(match_type($t,$call_rule,$args_str))) {
849 0           say "|$call_str| != |$args_str|";
850 0           Report($t,"call |$name| args type not same!");
851             }
852             }
853             }
854             }
855            
856             sub lint_my_for {
857 0     0 0   my ($t,$args) = @_;
858 0           my ($iter_expr,$exprs) = match($args);
859 0           my ($name,$iter_atom) = flat($iter_expr);
860 0           my $type = get_iter_type($t,$iter_atom);
861 0           set_name_value($t,$name,$type);
862 0           return lint_my_block($t,$exprs);;
863             }
864            
865             sub lint_my_set {
866 0     0 0   my ($t,$args) = @_;
867 0           my ($sym,$value) = flat($args);
868 0           my $sym_type = GetAtomType($t,$sym);
869 0           my $value_type = GetAtomType($t,$value);
870 0 0         if ($sym_type ne $value_type) {
871 0           say "|$sym_type| != |$value_type|";
872 0           Report($t,"assign type not same with before!");
873             }
874             }
875            
876             sub lint_my_sym {
877 0     0 0   my ($t,$name) = @_;
878 0 0         if (not(IsDefine($t,$name))) {
879 0           Report($t,"not define symbol: |$name|");
880             }
881             }
882            
883             sub GetAtomType {
884 0     0 0   my ($t,$atom) = @_;
885 0           my ($name,$value) = flat($atom);
886 0           update_off($t,$atom);
887 0 0         if ($name ~~ ['Int','Str','Bool','Hash']) {
888 0           return $name;
889             }
890 0 0         if ($name ~~ ['Char','Lstr','String']) {
891 0           return 'Str';
892             }
893 0 0         if ($name eq 'Sym') {
894 0           return get_sym_type($t,$value);
895             }
896 0 0         if ($name eq ':ocall') {
897 0           return get_ocall_type($t,$value);
898             }
899 0 0         if ($name eq 'Array') {
900 0           return GetArrayType($t,$value);
901             }
902 0 0         if ($name eq 'Arange') {
903 0           return get_arange_type($t,$value);
904             }
905 0 0         if ($name eq 'Aindex') {
906 0           return get_aindex_type($t,$value);
907             }
908 0           return GetCallType($t,$name);
909             }
910            
911             sub get_sym_type {
912 0     0 0   my ($t,$name) = @_;
913 0           my $value = get_name_value($t,$name);
914 0 0         if (is_str($value)) {
915 0           return $value;
916             }
917 0           return 'Fn';
918             }
919            
920             sub get_ocall_type {
921 0     0 0   my ($t,$ocall) = @_;
922 0           my ($sym,$call) = flat($ocall);
923 0           my $type = get_name_value($t,$sym);
924 0           my $tree = $t->{'tree'};
925 0 0         if (not(exists $tree->{$type}{$call})) {
926 0           Report($t,"undefined call: |$call|");
927             }
928 0           return $tree->{$type}{$call};
929             }
930            
931             sub GetArrayType {
932 0     0 0   my ($t,$args) = @_;
933 0 0         if (is_blank($args)) {
934 0           return 'Strs';
935             }
936 0           my $sub_type = GetAtomType($t,first(atoms($args)));
937 0 0         if ($sub_type eq 'Int') {
938 0           return 'Ints';
939             }
940 0           return 'Strs';
941             }
942            
943             sub get_iter_type {
944 0     0 0   my ($t,$atom) = @_;
945 0           my $type = GetAtomType($t,$atom);
946 0 0         if ($type eq 'Ints') {
947 0           return 'Int';
948             }
949 0 0         if ($type ~~ ['Strs','Table','Str']) {
950 0           return 'Str';
951             }
952 0           return 'Nil';
953             }
954            
955             sub get_arange_type {
956 0     0 0   my ($t,$args) = @_;
957 0           my $sym = first(atoms($args));
958 0           return GetAtomType($t,$sym);
959             }
960            
961             sub get_aindex_type {
962 0     0 0   my ($t,$args) = @_;
963 0           my ($sym,$indexs) = match($args);
964 0           my $value = GetAtomType($t,$sym);
965 0           for my $index (@{atoms($indexs)}) {
  0            
966 0           my $type = GetAtomType($t,$index);
967 0           my $name = value($index);
968 0           $value = get_index_type($t,$value,$type,$name);
969             }
970 0           return $value;
971             }
972            
973             sub get_index_type {
974 0     0 0   my ($t,$value,$type,$name) = @_;
975 0           my $type_str = add($value,$type);
976 0           given ($type_str) {
977 0           when ('StrInt') {
978 0           return 'Str';
979             }
980 0           when ('StrsInt') {
981 0           return 'Str';
982             }
983 0           when ('IntsInt') {
984 0           return 'Int';
985             }
986 0           when ('TableStr') {
987 0           return 'Str';
988             }
989 0           when ('TreeStr') {
990 0           return 'Table';
991             }
992 0           default {
993 0           my $tree = $t->{'tree'};
994 0 0         if (exists $tree->{$value}) {
995 0           my $table = $tree->{$value};
996 0 0         if (exists $table->{$name}) {
997 0           return $tree->{$value}{$name};
998             }
999             }
1000             }
1001             }
1002 0           return 'Nil';
1003             }
1004            
1005             sub GetCallType {
1006 0     0 0   my ($t,$name) = @_;
1007 0 0         if ($name ~~ ['func','if','else','elif','given','when','then','my','use','package','const','for','while','return','struct','type']) {
1008 0           return 'Nil';
1009             }
1010 0           my $value = get_name_value($t,$name);
1011 0 0         if (is_str($value)) {
1012 0           return $value;
1013             }
1014 0           return value($value);
1015             }
1016             1;