File Coverage

blib/lib/Mylisp/Match.pm
Criterion Covered Total %
statement 14 412 3.4
branch 0 80 0.0
condition 0 3 0.0
subroutine 5 46 10.8
pod 0 41 0.0
total 19 582 3.2


line stmt bran cond sub pod time code
1             package Mylisp::Match;
2            
3 1     1   18 use 5.012;
  1         4  
4 1     1   7 use experimental 'switch';
  1         2  
  1         6  
5            
6 1     1   123 use Exporter;
  1         3  
  1         73  
7             our @ISA = qw(Exporter);
8             our @EXPORT = qw(MatchTable MatchDoor);
9            
10 1     1   32 use Mylisp::Builtin;
  1         5  
  1         251  
11 1     1   9 use Mylisp::Estr;
  1         2  
  1         7297  
12            
13            
14             sub new_cursor {
15 0     0 0   my ($str,$table) = @_;
16 0           my $text = add($str,End);
17 0           return {'text' => $text,'table' => $table,'pos' => 0,'line' => 1,'maxpos' => 0,'maxline' => 1};
18             }
19            
20             sub MatchTable {
21 0     0 0   my ($table,$text) = @_;
22 0           return MatchDoor($table,$text,'door');
23             }
24            
25             sub MatchDoor {
26 0     0 0   my ($table,$text,$door) = @_;
27 0           my $rule = $table->{$door};
28 0           my $c = new_cursor($text,$table);
29 0           my $match = match_spp_rule($c,$rule);
30 0 0         if (is_false($match)) {
31 0           my $report = fail_report($c);
32 0           return $report,0;
33             }
34 0           return $match,1;
35             }
36            
37             sub get_char {
38 0     0 0   my $c = shift;
39 0           my $text = $c->{'text'};
40 0           my $pos = $c->{'pos'};
41 0           return substr($text, $pos, 1);
42             }
43            
44             sub pre_char {
45 0     0 0   my $c = shift;
46 0           my $text = $c->{'text'};
47 0           my $pos = $c->{'pos'} - 1;
48 0           return substr($text, $pos, 1);
49             }
50            
51             sub to_next {
52 0     0 0   my $c = shift;
53 0 0         if (get_char($c) eq "\n") {
54 0           $c->{'line'}++;
55             }
56 0           $c->{'pos'}++;
57 0 0         if ($c->{'pos'} > $c->{'maxpos'}) {
58 0           $c->{'maxpos'} = $c->{'pos'};
59 0           $c->{'maxline'} = $c->{'line'};
60             }
61             }
62            
63             sub cache {
64 0     0 0   my $c = shift;
65 0           my $pos = $c->{'pos'};
66 0           my $line = $c->{'line'};
67 0           return [$pos,$line];
68             }
69            
70             sub reset_cache {
71 0     0 0   my ($c,$cache) = @_;
72 0           $c->{'pos'} = $cache->[0];
73 0           $c->{'line'} = $cache->[1];
74             }
75            
76             sub fail_report {
77 0     0 0   my $c = shift;
78 0           my $text = $c->{'text'};
79 0           my $pos = $c->{'maxpos'};
80 0           my $line = $c->{'maxline'};
81 0           my $line_str = to_end($text,$pos);
82 0           return "line: $line Stop match:\n$line-str\n^";
83             }
84            
85             sub match_spp_rule {
86 0     0 0   my ($c,$rule) = @_;
87 0           my ($name,$value) = flat($rule);
88 0           given ($name) {
89 0           when ('Rules') {
90 0           return match_spp_rules($c,$value);
91             }
92 0           when ('Group') {
93 0           return match_spp_rules($c,$value);
94             }
95 0           when ('Branch') {
96 0           return match_spp_branch($c,$value);
97             }
98 0           when ('Blank') {
99 0           return match_spp_blank($c,$value);
100             }
101 0           when ('Rept') {
102 0           return match_spp_rept($c,$value);
103             }
104 0           when ('Cclass') {
105 0           return match_spp_cclass($c,$value);
106             }
107 0           when ('Chclass') {
108 0           return match_spp_chclass($c,$value);
109             }
110 0           when ('Nclass') {
111 0           return match_spp_nclass($c,$value);
112             }
113 0           when ('Str') {
114 0           return match_spp_str($c,$value);
115             }
116 0           when ('Char') {
117 0           return match_spp_char($c,$value);
118             }
119 0           when ('Assert') {
120 0           return match_spp_assert($c,$value);
121             }
122 0           when ('Till') {
123 0           return match_spp_till($c,$value);
124             }
125 0           when ('Rtoken') {
126 0           return match_spp_rtoken($c,$value);
127             }
128 0           when ('Ctoken') {
129 0           return match_spp_ctoken($c,$value);
130             }
131 0           when ('Ntoken') {
132 0           return match_spp_ntoken($c,$value);
133             }
134 0           when ('Any') {
135 0           return match_spp_any($c,$value);
136             }
137 0           when ('Call') {
138 0           return match_spp_call($c,$value);
139             }
140 0           when ('Sym') {
141 0           return match_spp_sym($c,$value);
142             }
143 0           default {
144 0           return False;
145             }
146             }
147             }
148            
149             sub match_spp_any {
150 0     0 0   my ($c,$any) = @_;
151 0           my $char = get_char($c);
152 0 0         if ($char eq End) {
153 0           return False;
154             }
155 0           to_next($c);
156 0           return $char;
157             }
158            
159             sub match_spp_assert {
160 0     0 0   my ($c,$assert) = @_;
161 0           given ($assert) {
162 0           when ('$') {
163 0 0         if (get_char($c) eq End) {
164 0           return True;
165             }
166 0           return False;
167             }
168 0           when ('^') {
169 0 0         if ($c->{'pos'} == 0) {
170 0           return True;
171             }
172 0 0         if (pre_char($c) eq "\n") {
173 0           return True;
174             }
175 0           return False;
176             }
177 0           when ('$$') {
178 0 0         if (get_char($c) eq "\n") {
179 0           return True;
180             }
181 0 0         if (get_char($c) eq End) {
182 0           return True;
183             }
184 0           return False;
185             }
186 0           default {
187 0           say "unknown assert: |$assert|";
188             }
189             }
190 0           return False;
191             }
192            
193             sub match_spp_blank {
194 0     0 0   my ($c,$blank) = @_;
195 0           while (is_space(get_char($c))) {
196 0           $c->{'pos'}++;
197             }
198 0           return True;
199             }
200            
201             sub match_spp_rules {
202 0     0 0   my ($c,$rules) = @_;
203 0           my $gather = True;
204 0           for my $rule (@{atoms($rules)}) {
  0            
205 0           my $match = match_spp_rule($c,$rule);
206 0 0         if (is_false($match)) {
207 0           return False;
208             }
209 0           $gather = gather_spp_match($gather,$match);
210             }
211 0           return $gather;
212             }
213            
214             sub match_spp_branch {
215 0     0 0   my ($c,$branch) = @_;
216 0           my $cache = cache($c);
217 0           for my $rule (@{atoms($branch)}) {
  0            
218 0           my $match = match_spp_rule($c,$rule);
219 0 0         if (not(is_false($match))) {
220 0           return $match;
221             }
222 0           reset_cache($c,$cache);
223             }
224 0           return False;
225             }
226            
227             sub match_spp_ntoken {
228 0     0 0   my ($c,$name) = @_;
229 0           my $ns = $c->{'table'};
230 0           my $rule = $ns->{$name};
231 0           my $cache = cache($c);
232 0           my $match = match_spp_rule($c,$rule);
233 0 0         if (is_bool($match)) {
234 0           return $match;
235             }
236 0 0         if (is_str($match)) {
237 0           my $ref_name = add('@',$name);
238 0           my $ns = $c->{'table'};
239 0           $ns->{$ref_name} = $match;
240             }
241 0           return name_spp_match($name,$match,$cache);
242             }
243            
244             sub match_spp_ctoken {
245 0     0 0   my ($c,$name) = @_;
246 0           my $ns = $c->{'table'};
247 0           my $rule = $ns->{$name};
248 0           my $match = match_spp_rule($c,$rule);
249 0 0         if (is_str($match)) {
250 0           my $ref_name = add('@',$name);
251 0           my $ns = $c->{'table'};
252 0           $ns->{$ref_name} = $match;
253             }
254 0           return $match;
255             }
256            
257             sub match_spp_rtoken {
258 0     0 0   my ($c,$name) = @_;
259 0           my $ns = $c->{'table'};
260 0           my $rule = $ns->{$name};
261 0           my $match = match_spp_rule($c,$rule);
262 0 0         if (is_false($match)) {
263 0           return False;
264             }
265 0           return True;
266             }
267            
268             sub match_spp_till {
269 0     0 0   my ($c,$rule) = @_;
270 0           my $buf = [];
271 0           my $len = len($c->{'text'});
272 0           while ($c->{'pos'} < $len) {
273 0           my $char = get_char($c);
274 0           my $cache = cache($c);
275 0           my $match = match_spp_rule($c,$rule);
276 0 0         if (not(is_false($match))) {
277 0           my $gather_str = to_str($buf);
278 0           return gather_spp_match($gather_str,$match);
279             }
280 0           apush($buf,$char);
281 0           reset_cache($c,$cache);
282 0           to_next($c);
283             }
284 0           return False;
285             }
286            
287             sub match_spp_rept {
288 0     0 0   my ($c,$rule) = @_;
289 0           my $gather = True;
290 0           my $time = 0;
291 0           my ($rept,$atom) = flat($rule);
292 0           my ($min,$max) = get_rept_time($rept);
293 0           while ($time != $max) {
294 0           my $cache = cache($c);
295 0           my $match = match_spp_rule($c,$atom);
296 0 0         if (is_false($match)) {
297 0 0         if ($time < $min) {
298 0           return False;
299             }
300 0           reset_cache($c,$cache);
301 0           return $gather;
302             }
303 0           $time++;
304 0           $gather = gather_spp_match($gather,$match);
305             }
306 0           return $gather;
307             }
308            
309             sub get_rept_time {
310 0     0 0   my $rept = shift;
311 0           given ($rept) {
312 0           when ('?') {
313 0           return 0,1;
314             }
315 0           when ('*') {
316 0           return 0,-1;
317             }
318 0           when ('+') {
319 0           return 1,-1;
320             }
321 0           default {
322 0           return 0,1;
323             }
324             }
325             }
326            
327             sub match_spp_str {
328 0     0 0   my ($c,$str) = @_;
329 0           for my $char (@{to_chars($str)}) {
  0            
330 0 0         if ($char ne get_char($c)) {
331 0           return False;
332             }
333 0           to_next($c);
334             }
335 0           return $str;
336             }
337            
338             sub match_spp_char {
339 0     0 0   my ($c,$char) = @_;
340 0 0         if ($char ne get_char($c)) {
341 0           return False;
342             }
343 0           to_next($c);
344 0           return $char;
345             }
346            
347             sub match_spp_chclass {
348 0     0 0   my ($c,$atoms) = @_;
349 0           my $char = get_char($c);
350 0           for my $atom (@{atoms($atoms)}) {
  0            
351 0 0         if (match_spp_catom($atom,$char)) {
352 0           to_next($c);
353 0           return $char;
354             }
355             }
356 0           return False;
357             }
358            
359             sub match_spp_nclass {
360 0     0 0   my ($c,$atoms) = @_;
361 0           my $char = get_char($c);
362 0 0         if ($char eq End) {
363 0           return False;
364             }
365 0           for my $atom (@{atoms($atoms)}) {
  0            
366 0 0         if (match_spp_catom($atom,$char)) {
367 0           return False;
368             }
369             }
370 0           to_next($c);
371 0           return $char;
372             }
373            
374             sub match_spp_catom {
375 0     0 0   my ($atom,$char) = @_;
376 0           my ($name,$value) = flat($atom);
377 0           given ($name) {
378 0           when ('Range') {
379 0           return match_spp_range($value,$char);
380             }
381 0           when ('Cclass') {
382 0           return is_match_spp_cclass($value,$char);
383             }
384 0           when ('Cchar') {
385 0           return $value eq $char;
386             }
387 0           when ('Char') {
388 0           return $value eq $char;
389             }
390 0           default {
391 0           say "unknown spp catom: |$name|";
392             }
393             }
394 0           return 0;
395             }
396            
397             sub match_spp_cclass {
398 0     0 0   my ($c,$cclass) = @_;
399 0           my $char = get_char($c);
400 0 0         if ($char eq End) {
401 0           return False;
402             }
403 0 0         if (is_match_spp_cclass($cclass,$char)) {
404 0           to_next($c);
405 0           return $char;
406             }
407 0           return False;
408             }
409            
410             sub is_match_spp_cclass {
411 0     0 0   my ($cchar,$char) = @_;
412 0           given ($cchar) {
413 0           when ('a') {
414 0           return is_alpha($char);
415             }
416 0           when ('A') {
417 0           return not(is_alpha($char));
418             }
419 0           when ('d') {
420 0           return is_digit($char);
421             }
422 0           when ('D') {
423 0           return not(is_digit($char));
424             }
425 0           when ('h') {
426 0           return is_hspace($char);
427             }
428 0           when ('H') {
429 0           return not(is_hspace($char));
430             }
431 0           when ('l') {
432 0           return is_lower($char);
433             }
434 0           when ('L') {
435 0           return not(is_lower($char));
436             }
437 0           when ('s') {
438 0           return is_space($char);
439             }
440 0           when ('S') {
441 0           return not(is_space($char));
442             }
443 0           when ('u') {
444 0           return is_upper($char);
445             }
446 0           when ('U') {
447 0           return not(is_upper($char));
448             }
449 0           when ('v') {
450 0           return is_vspace($char);
451             }
452 0           when ('V') {
453 0           return not(is_vspace($char));
454             }
455 0           when ('w') {
456 0           return is_words($char);
457             }
458 0           when ('W') {
459 0           return not(is_words($char));
460             }
461 0           when ('x') {
462 0           return is_xdigit($char);
463             }
464 0           when ('X') {
465 0           return not(is_xdigit($char));
466             }
467 0           default {
468 0           return 0;
469             }
470             }
471             }
472            
473             sub match_spp_range {
474 0     0 0   my ($range,$char) = @_;
475 0           my ($from,$to) = flat($range);
476 0   0       return $from le $char && $char le $to;
477             }
478            
479             sub match_spp_sym {
480 0     0 0   my ($c,$name) = @_;
481 0           my $value = get_spp_sym_value($c,$name);
482 0           return match_spp_value($c,$value);
483             }
484            
485             sub get_spp_sym_value {
486 0     0 0   my ($c,$name) = @_;
487 0           my $ns = $c->{'table'};
488 0 0         if (exists $ns->{$name}) {
489 0           return $ns->{$name};
490             }
491 0           error("variable not define: |$name|");
492 0           return False;
493             }
494            
495             sub match_spp_value {
496 0     0 0   my ($c,$atom) = @_;
497 0           my ($name,$value) = flat($atom);
498 0           given ($name) {
499 0           when ('Array') {
500 0 0         if (is_blank($value)) {
501 0           return False;
502             }
503 0           return match_spp_branch($c,$value);
504             }
505 0           when ('Str') {
506 0           return match_spp_str($c,$value);
507             }
508 0           default {
509 0           error("unknown spp value: |$name|");
510             }
511             }
512 0           return False;
513             }
514            
515             sub match_spp_call {
516 0     0 0   my ($c,$call) = @_;
517 0           my $value = get_spp_call_value($c,$call);
518 0           return match_spp_value($c,$value);
519             }
520            
521             sub get_spp_call_value {
522 0     0 0   my ($c,$call) = @_;
523 0           my ($name,$args) = match($call);
524 0           given ($name) {
525 0           when ('my') {
526 0           return eval_spp_my($c,$args);
527             }
528 0           when ('push') {
529 0           return eval_spp_push($c,$args);
530             }
531 0           default {
532 0           say "not implement: ($name..)";
533             }
534             }
535 0           return False;
536             }
537            
538             sub eval_spp_my {
539 0     0 0   my ($c,$atoms) = @_;
540 0           my ($sym,$value) = flat($atoms);
541 0 0         if (is_sym($sym)) {
542 0           my $name = value($sym);
543 0           my $ns = $c->{'table'};
544 0           $ns->{$name} = $value;
545 0           return True;
546             }
547 0           croak("only assign symbol!");
548 0           return False;
549             }
550            
551             sub eval_spp_push {
552 0     0 0   my ($c,$atoms) = @_;
553 0           my $sym = name($atoms);
554 0 0         if (is_sym($sym)) {
555 0           my $name = value($sym);
556 0           my $atoms_value = get_spp_atoms_value($c,$atoms);
557 0           my ($array,$elem) = flat($atoms_value);
558 0           $array = value($array);
559 0           $array = epush($array,$elem);
560 0           my $ns = $c->{'table'};
561 0           $ns->{$name} = estr('Array',$array);
562 0           return True;
563             }
564 0           say 'push only accept array symbol!';
565 0           return False;
566             }
567            
568             sub get_spp_atom_value {
569 0     0 0   my ($c,$atom) = @_;
570 0           my ($name,$value) = flat($atom);
571 0           given ($name) {
572 0           when ('Array') {
573 0           return get_spp_array_value($c,$value);
574             }
575 0           when ('Sym') {
576 0           return get_spp_sym_value($c,$value);
577             }
578 0           when ('Str') {
579 0           return $atom;
580             }
581 0           default {
582 0           say "get unknown atom: |$name| value";
583             }
584             }
585 0           return False;
586             }
587            
588             sub get_spp_array_value {
589 0     0 0   my ($c,$array) = @_;
590 0 0         if (is_blank($array)) {
591 0           return estr('Array',$array);
592             }
593 0           my $atoms = get_spp_atoms_value($c,$array);
594 0           return estr('Array',$atoms);
595             }
596            
597             sub get_spp_atoms_value {
598 0     0 0   my ($c,$atoms) = @_;
599 0           my $atoms_value = [];
600 0           for my $atom (@{atoms($atoms)}) {
  0            
601 0           apush($atoms_value,get_spp_atom_value($c,$atom));
602             }
603 0           return estr_strs($atoms_value);
604             }
605            
606             sub name_spp_match {
607 0     0 0   my ($name,$match,$pos) = @_;
608 0 0         if (is_true($match)) {
609 0           return $match;
610             }
611 0           my $pos_str = estr_ints($pos);
612 0 0         if (is_atom($match)) {
613 0           return estr($name,estr($match),$pos_str);
614             }
615 0           return estr($name,$match,$pos_str);
616             }
617            
618             sub gather_spp_match {
619 0     0 0   my ($gather,$match) = @_;
620 0 0         if (is_true($match)) {
621 0           return $gather;
622             }
623 0 0         if (is_true($gather)) {
624 0           return $match;
625             }
626 0 0         if (is_str($match)) {
627 0 0         if (is_str($gather)) {
628 0           return add($gather,$match);
629             }
630 0           return $gather;
631             }
632 0 0         if (is_str($gather)) {
633 0           return $match;
634             }
635 0 0         if (is_atom($gather)) {
636 0 0         if (is_atom($match)) {
637 0           return estr($gather,$match);
638             }
639 0           return eunshift($gather,$match);
640             }
641 0 0         if (is_atom($match)) {
642 0           return epush($gather,$match);
643             }
644 0           return eappend($gather,$match);
645             }
646             1;