Commit | Line | Data |
3fea05b9 |
1 | # Copyright (c) 2006 Hans Jeuken. All rights reserved. |
2 | # This program is free software; you can redistribute it and/or |
3 | # modify it under the same terms as Perl itself. |
4 | |
5 | package Syntax::Highlight::Engine::Kate::ToolKit; |
6 | |
7 | our $VERSION = '0.06'; |
8 | |
9 | use strict; |
10 | use warnings; |
11 | use XML::Dumper; |
12 | require Syntax::Highlight::Engine::Kate::XMLData; |
13 | |
14 | use File::Basename; |
15 | |
16 | my $regfile = "SHEKREGISTRY.xml"; |
17 | my $regchars = "\\^.\$|()[]{}*+?~!%^&/"; |
18 | |
19 | my %tests = ( |
20 | AnyChar => \&testRuleAnyChar, |
21 | DetectChar => \&testRuleDetectChar, |
22 | Detect2Chars => \&testRuleDetect2Chars, |
23 | DetectIdentifier => \&testRuleDetectIdentifier, |
24 | DetectSpaces => \&testRuleDetectSpaces, |
25 | Float => \&testRuleFloat, |
26 | HlCChar => \&testRuleHlCChar, |
27 | HlCHex => \&testRuleHlCHex, |
28 | HlCOct => \&testRuleHlCOct, |
29 | HlCStringChar => \&testRuleHlCStringChar, |
30 | IncludeRules => \&testRuleIncludeRules, |
31 | Int => \&testRuleInt, |
32 | keyword => \&testRuleKeyword, |
33 | LineContinue => \&testRuleLineContinue, |
34 | RangeDetect => \&testRuleRangeDetect, |
35 | RegExpr => \&testRuleRegExpr, |
36 | StringDetect => \&testRuleStringDetect, |
37 | ); |
38 | |
39 | my %parses = ( |
40 | AnyChar => \&pmRuleAnyChar, |
41 | DetectChar => \&pmRuleDetectChar, |
42 | Detect2Chars => \&pmRuleDetect2Chars, |
43 | DetectIdentifier => \&pmRuleDetectIdentifier, |
44 | DetectSpaces => \&pmRuleDetectSpaces, |
45 | Float => \&pmRuleFloat, |
46 | HlCChar => \&pmRuleHlCChar, |
47 | HlCHex => \&pmRuleHlCHex, |
48 | HlCOct => \&pmRuleHlCOct, |
49 | HlCStringChar => \&pmRuleHlCStringChar, |
50 | IncludeRules => \&pmRuleIncludeRules, |
51 | Int => \&pmRuleInt, |
52 | keyword => \&pmRuleKeyword, |
53 | LineContinue => \&pmRuleLineContinue, |
54 | RangeDetect => \&pmRuleRangeDetect, |
55 | RegExpr => \&pmRuleRegExpr, |
56 | StringDetect => \&pmRuleStringDetect, |
57 | ); |
58 | |
59 | |
60 | my @stdoptions = qw(lookAhead column firstNonSpace context attribute); |
61 | my $stringtest = sub { return (length(shift) > 0) }; |
62 | my $booltest = sub { my $l = lc(shift) ; return (($l eq 'true') or ($l eq 'false') or ($l eq '0') or ($l eq '1')) }; |
63 | my $chartest = sub { return (length(shift) eq 1) }; |
64 | |
65 | my %testopts = ( |
66 | attribute => $stringtest, |
67 | char => $chartest, |
68 | char1 => $chartest, |
69 | column => sub { return (shift =~ /^\d+$/)}, |
70 | context => $stringtest, |
71 | dynamic => $booltest, |
72 | firstNonSpace =>$booltest, |
73 | insensitive => $booltest, |
74 | lookAhead => $booltest, |
75 | minimal => $booltest, |
76 | String => $stringtest, |
77 | ); |
78 | |
79 | sub new { |
80 | my $proto = shift; |
81 | my $class = ref($proto) || $proto; |
82 | |
83 | my $self = { |
84 | indent => 0, |
85 | indentchar => ' ', |
86 | curlang => '', |
87 | curcontext => '', |
88 | guiadd => sub { }, |
89 | logcmd => sub { warn shift }, |
90 | outcmd => sub { print shift }, |
91 | policy => 'abort', |
92 | polcmd => sub { return 0 }, |
93 | reportdata => [], |
94 | registered => {}, |
95 | runtestspace => '', |
96 | verbose => 1, |
97 | version => '0.01', |
98 | }; |
99 | bless ($self, $class); |
100 | return $self; |
101 | } |
102 | |
103 | |
104 | sub booleanize { |
105 | my ($self, $d) = @_; |
106 | if (lc($d) eq 'true') { $d = 1 }; |
107 | if (lc($d) eq 'false') { $d = 0 }; |
108 | if (($d ne 0) and ($d ne 1)) { return undef }; |
109 | return $d; |
110 | } |
111 | |
112 | sub checkIntegrity { |
113 | my $self = shift; |
114 | my @l = @_; |
115 | unless (@l) { @l = $self->registered }; |
116 | while (@l) { |
117 | my $lang = shift @l; |
118 | $self->curlang($lang); |
119 | $self->indent(0); |
120 | my $xml = $self->xmldata($lang); |
121 | if (defined($xml)) { |
122 | my $ctx = $xml->contexts; |
123 | foreach my $k (sort keys %$ctx) { |
124 | $self->curcontext($k); |
125 | $self->lprint("checking context $k"); |
126 | $self->indentUp; |
127 | my $itl = $ctx->{$k}->{'items'}; |
128 | $self->testContextItems(@$itl); |
129 | $self->indentDown; |
130 | } |
131 | } else { |
132 | $self->log("could not retrieve data for $lang"); |
133 | } |
134 | } |
135 | } |
136 | |
137 | sub curcontext { |
138 | my $self = shift; |
139 | if (@_) { $self->{'curcontext'} = shift; }; |
140 | return $self->{'curcontext'}; |
141 | } |
142 | |
143 | sub curlang { |
144 | my $self = shift; |
145 | if (@_) { $self->{'curlang'} = shift; }; |
146 | return $self->{'curlang'}; |
147 | } |
148 | |
149 | sub guiadd { |
150 | my $self = shift; |
151 | if (@_) { $self->{'guiadd'} = shift; }; |
152 | return $self->{'guiadd'}; |
153 | } |
154 | |
155 | sub indent { |
156 | my $self = shift; |
157 | if (@_) { $self->{'indent'} = shift; }; |
158 | return $self->{'indent'}; |
159 | } |
160 | |
161 | sub indentchar { |
162 | my $self = shift; |
163 | if (@_) { $self->{'indentchar'} = shift; }; |
164 | return $self->{'indentchar'}; |
165 | } |
166 | |
167 | sub indentDown { |
168 | my $self = shift; |
169 | if ($self->indent > 0) { |
170 | $self->indent($self->indent - 1); |
171 | } else { |
172 | $self->log("indentation already 0\n"); |
173 | } |
174 | } |
175 | |
176 | sub indentUp { |
177 | my $self = shift; |
178 | $self->indent($self->indent + 1); |
179 | } |
180 | |
181 | sub log { |
182 | my ($self, $msg) = @_; |
183 | my $c = $self->logcmd; |
184 | &$c($msg); |
185 | } |
186 | |
187 | sub logcmd { |
188 | my $self = shift; |
189 | if (@_) { $self->{'logcmd'} = shift; }; |
190 | return $self->{'logcmd'}; |
191 | } |
192 | |
193 | sub lprint { |
194 | my ($self, $txt) = @_; |
195 | if (defined($txt)) { #check if only a newline should be given |
196 | if ($txt ne '') { #do not indent empty lines |
197 | my $c = 0; |
198 | while ($c < $self->{'indent'}) { |
199 | $txt = $self->indentchar . $txt; |
200 | $c ++; |
201 | } |
202 | } |
203 | } else { |
204 | $txt = ''; |
205 | } |
206 | my $c = $self->outcmd; |
207 | &$c("$txt\n"); |
208 | } |
209 | |
210 | sub moduleName { |
211 | my ($self, $name) = @_; |
212 | my %numb = ( |
213 | '1' => 'One', |
214 | '2' => 'Two', |
215 | '3' => 'Three', |
216 | '4' => 'Four', |
217 | '5' => 'Five', |
218 | '6' => 'Six', |
219 | '7' => 'Seven', |
220 | '8' => 'Eight', |
221 | '9' => 'Nine', |
222 | '0' => 'Zero', |
223 | ); |
224 | if ($name =~ s/^(\d)//) { |
225 | $name = $numb{$1} . $name; |
226 | } |
227 | $name =~ s/\.//; |
228 | $name =~ s/\+/plus/g; |
229 | $name =~ s/\-/minus/g; |
230 | $name =~ s/#/dash/g; |
231 | $name =~ s/[^0-9a-zA-Z]/_/g; |
232 | $name =~ s/__/_/g; |
233 | $name =~ s/_$//; |
234 | $name = ucfirst($name); |
235 | return $name; |
236 | } |
237 | |
238 | sub outcmd { |
239 | my $self = shift; |
240 | if (@_) { $self->{'outcmd'} = shift; }; |
241 | return $self->{'outcmd'}; |
242 | } |
243 | |
244 | |
245 | sub pmGenerate { |
246 | my $self = shift; |
247 | my @l = @_; |
248 | unless (@l) { @l = $self->registered }; |
249 | while (@l) { |
250 | my $lang = shift @l; |
251 | my $vbck = $self->verbose; |
252 | $self->verbose(0); |
253 | $self->indent(0); |
254 | $self->curlang($lang); |
255 | my $xml = $self->xmldata($lang); |
256 | my $file = basename($xml->filename); |
257 | my $lh = $xml->language; |
258 | my $name = $self->moduleName($lang); |
259 | my $i = $xml->itemdata; |
260 | my %itemdata = %$i; |
261 | my $l = $xml->lists; |
262 | my %lists = %$l; |
263 | my $c = $xml->contexts; |
264 | my %contexts = %$c; |
265 | |
266 | $self->lprint("# Copyright (c) 2005 - 2006 Hans Jeuken. All rights reserved."); |
267 | $self->lprint("# This program is free software; you can redistribute it and/or"); |
268 | $self->lprint("# modify it under the same terms as Perl itself."); |
269 | $self->lprint; |
270 | $self->lprint("# This file was generated from the '$file' file of the syntax highlight"); |
271 | $self->lprint("# engine of the kate text editor (http://www.kate-editor.org"); |
272 | $self->lprint; |
273 | if (exists $lh->{'version'}) { |
274 | $self->lprint("#kate xml version " . $lh->{'version'}); |
275 | } |
276 | if (exists $lh->{'kateversion'}) { |
277 | $self->lprint("#kate version " . $lh->{"kateversion"}); |
278 | } |
279 | if (exists $lh->{'author'}) { |
280 | $self->lprint("#kate author " . $lh->{"author"}); |
281 | } |
282 | my $time = localtime; |
283 | $self->lprint("#generated: $time, localtime"); |
284 | $self->lprint; |
285 | $self->lprint("package Syntax::Highlight::Engine::Kate::$name;"); |
286 | $self->lprint; |
287 | $self->lprint("use vars qw(\$VERSION);"); |
288 | $self->lprint("\$VERSION = '" . $self->version . "';"); |
289 | $self->lprint; |
290 | $self->lprint("use strict;"); |
291 | $self->lprint("use warnings;"); |
292 | $self->lprint("use base('Syntax::Highlight::Engine::Kate::Template');"); |
293 | $self->lprint; |
294 | $self->lprint("sub new {"); |
295 | $self->indentUp; |
296 | $self->lprint("my \$proto = shift;"); |
297 | $self->lprint("my \$class = ref(\$proto) || \$proto;"); |
298 | $self->lprint("my \$self = \$class->SUPER::new(\@_);"); |
299 | if (%itemdata) { |
300 | $self->lprint("\$self->attributes({"); |
301 | $self->indentUp; |
302 | foreach my $at (sort keys %itemdata) { |
303 | my $v = $itemdata{$at}; |
304 | $self->lprint("'$at' => '$v',"); |
305 | } |
306 | $self->indentDown; |
307 | $self->lprint("});"); |
308 | } |
309 | if (%lists) { |
310 | foreach my $k (sort keys %lists) { |
311 | $self->lprint("\$self->listAdd('$k',"); |
312 | $self->indentUp; |
313 | my $il = $lists{$k}; |
314 | foreach my $i (sort @$il) { |
315 | $i =~ s/\\/\\\\/g; |
316 | $i =~ s/\'/\\'/g; |
317 | $self->lprint($self->stringalize($i) . ","); |
318 | } |
319 | $self->indentDown; |
320 | $self->lprint(");"); |
321 | } |
322 | } |
323 | $self->lprint("\$self->contextdata({"); |
324 | $self->indentUp; |
325 | foreach my $ctx (sort keys %contexts) { |
326 | my $p = $contexts{$ctx}; |
327 | $self->lprint("'$ctx' => {"); |
328 | $self->indentUp; |
329 | $self->lprint("callback => \\&" . $self->pmMethodName($ctx) . ","); |
330 | if (exists $p->{'attribute'}) { |
331 | my $coi = $p->{'attribute'}; |
332 | $self->lprint("attribute => '$coi',"); |
333 | } |
334 | if (exists $p->{'lineEndContext'}) { |
335 | my $e = $p->{'lineEndContext'}; |
336 | unless ($e eq '#stay') { |
337 | $self->lprint("lineending => '$e',"); |
338 | } |
339 | } |
340 | if (exists $p->{'lineBeginContext'}) { |
341 | my $e = $p->{'lineBeginContext'}; |
342 | unless ($e eq '#stay') { |
343 | $self->lprint("linebeginning => '$e',"); |
344 | } |
345 | } |
346 | if (exists $p->{'fallthrough'}) { |
347 | my $e = $p->{'fallthrough'}; |
348 | if ($e eq 'true') { |
349 | if (exists $p->{'fallthroughContext'}) { |
350 | my $e = $p->{'fallthroughContext'}; |
351 | $self->lprint("fallthrough => '$e',"); |
352 | } |
353 | } |
354 | } |
355 | if (exists $p->{'dynamic'}) { |
356 | my $e = $p->{'dynamic'}; |
357 | if ($e eq 'true') { |
358 | $self->lprint("dynamic => 1,"); |
359 | } |
360 | } |
361 | $self->indentDown; |
362 | $self->lprint("},"); |
363 | } |
364 | $self->indentDown; |
365 | $self->lprint("});"); |
366 | |
367 | my $deliminators = ".():!+,-<=>%&*/;?[]^{|}~\\"; |
368 | my $wdelim = $xml->weakDeliminator; |
369 | while ($wdelim ne '') { |
370 | $wdelim =~ s/^(.)//; |
371 | my $wd = $1; |
372 | if (index($regchars, $wd) >= 0) { $wd = "\\$wd" }; |
373 | $deliminators =~ s/$wd//; |
374 | } |
375 | my $adelim = $xml->additionalDeliminator; |
376 | $deliminators = $deliminators . $adelim; |
377 | my @delimchars = split //, $deliminators; |
378 | my $tmp = ''; |
379 | for (@delimchars) { |
380 | my $dc = $_; |
381 | if (index($regchars, $dc ) >= 0) { $dc = "\\$dc" }; |
382 | $tmp = "$tmp|$dc"; |
383 | } |
384 | $tmp = '\\s|' . $tmp; |
385 | $self->lprint("\$self->deliminators(" . $self->stringalize($tmp) . ");"); |
386 | $self->lprint("\$self->basecontext(" . $self->stringalize($xml->basecontext) . ");"); |
387 | $self->lprint("\$self->keywordscase(" . $xml->keywordscase . ");"); |
388 | $self->lprint("\$self->initialize;"); |
389 | $self->lprint("bless (\$self, \$class);"); |
390 | $self->lprint("return \$self;"); |
391 | $self->indentDown; |
392 | $self->lprint("}"); |
393 | $self->lprint; |
394 | $self->lprint("sub language {"); |
395 | $self->indentUp; |
396 | $self->lprint("return " . $self->stringalize($lang) . ";"); |
397 | $self->indentDown; |
398 | $self->lprint("}"); |
399 | $self->lprint; |
400 | |
401 | foreach my $ctxt (sort keys %contexts) { |
402 | $self->curcontext($ctxt); |
403 | $self->lprint("sub " . $self->pmMethodName($ctxt) . " {"); |
404 | $self->indentUp; |
405 | $self->lprint("my (\$self, \$text) = \@_;"); |
406 | my $c = $contexts{$ctxt}; |
407 | my $it = $c->{'items'}; |
408 | $self->pmParseRules(@$it); |
409 | # $self->indentDown; |
410 | # $self->lprint("}\n\n"); |
411 | $self->lprint("return 0;"); |
412 | $self->indentDown; |
413 | $self->lprint("};"); |
414 | $self->lprint; |
415 | } |
416 | |
417 | $self->lprint; |
418 | $self->lprint("1;"); |
419 | $self->lprint; |
420 | $self->lprint("__END__"); |
421 | $self->lprint; |
422 | $self->lprint("=head1 NAME"); |
423 | $self->lprint; |
424 | $self->lprint("Syntax::Highlight::Engine::Kate::$name - a Plugin for $lang syntax highlighting"); |
425 | $self->lprint; |
426 | $self->lprint("=head1 SYNOPSIS"); |
427 | $self->lprint; |
428 | $self->lprint(" require Syntax::Highlight::Engine::Kate::$name;"); |
429 | $self->lprint(" my \$sh = new Syntax::Highlight::Engine::Kate::$name(["); |
430 | #todotodotodotodo |
431 | $self->lprint(" ]);"); |
432 | $self->lprint; |
433 | $self->lprint("=head1 DESCRIPTION"); |
434 | $self->lprint; |
435 | $self->lprint("Syntax::Highlight::Engine::Kate::$name is a plugin module that provides syntax highlighting"); |
436 | $self->lprint("for $lang to the Syntax::Haghlight::Engine::Kate highlighting engine."); |
437 | $self->lprint; |
438 | $self->lprint("This code is generated from the syntax definition files used"); |
439 | $self->lprint("by the Kate project."); |
440 | $self->lprint("It works quite fine, but can use refinement and optimization."); |
441 | $self->lprint; |
442 | $self->lprint("It inherits Syntax::Higlight::Engine::Kate::Template. See also there."); |
443 | $self->lprint; |
444 | $self->lprint("=cut"); |
445 | $self->lprint; |
446 | $self->lprint("=head1 AUTHOR"); |
447 | $self->lprint; |
448 | $self->lprint("Hans Jeuken (haje <at> toneel <dot> demon <dot> nl)"); |
449 | $self->lprint; |
450 | $self->lprint("=cut"); |
451 | $self->lprint; |
452 | $self->lprint("=head1 BUGS"); |
453 | $self->lprint; |
454 | $self->lprint("Unknown. If you find any, please contact the author"); |
455 | $self->lprint; |
456 | $self->lprint("=cut"); |
457 | $self->lprint; |
458 | |
459 | $self->verbose($vbck); |
460 | } |
461 | } |
462 | |
463 | sub pmMethodName { |
464 | my ($self, $in) = @_; |
465 | $in =~ s/\(/Bo/g; |
466 | $in =~ s/\)/Bc/g; |
467 | $in =~ s/\{/Co/g; |
468 | $in =~ s/\}/Cc/g; |
469 | $in =~ s/\[/So/g; |
470 | $in =~ s/\]/Sc/g; |
471 | $in =~ s/([^A-za-z0-9_])//g; |
472 | return "parse$in"; |
473 | } |
474 | |
475 | sub pmParseRules { |
476 | my $self = shift; |
477 | for (@_) { |
478 | my $rule = $_; |
479 | foreach my $k (sort keys %$rule) { |
480 | $self->lprint("# $k => '" . $rule->{$k} . "'"); |
481 | } |
482 | my $test = $tests{$rule->{'type'}}; |
483 | if (&$test($self, $rule)) { |
484 | my $call = $parses{$rule->{'type'}}; |
485 | &$call($self, $rule); |
486 | } else { |
487 | $self->lprint("#This rule is buggy, not sending it to output"); |
488 | } |
489 | } |
490 | } |
491 | |
492 | sub pmParseRuleFinish { |
493 | my ($self, $rule) = @_; |
494 | $self->indentUp; |
495 | if (exists $rule->{'items'}) { |
496 | my $i = $rule->{'items'}; |
497 | $self->pmParseRules(@$i); #recursive; |
498 | } else { |
499 | $self->lprint("return 1"); |
500 | } |
501 | $self->indentDown; |
502 | $self->lprint("}"); |
503 | |
504 | } |
505 | |
506 | sub pmParseRuleConvertArgs { |
507 | my $self = shift; |
508 | my $rule = shift; |
509 | my $r = ""; |
510 | my %default = ( |
511 | lookAhead => 0, |
512 | insensitive => 0, |
513 | minimal => 0, |
514 | dynamic => 0, |
515 | context => '#stay', |
516 | firstNonSpace => 0, |
517 | ); |
518 | while (@_) { |
519 | my $n = shift; |
520 | my $d; |
521 | if (exists($rule->{$n})) { |
522 | $d = $rule->{$n}; |
523 | } elsif (exists($default{$n})) { |
524 | $d = $default{$n}; |
525 | } else { |
526 | $d = undef; |
527 | } |
528 | if (defined($d)) { |
529 | my @boole = qw(insensitive dynamic firstNonSpace lookAhead minimal); |
530 | my @str = qw(String char char1 context attribute); |
531 | if ($n eq 'String') { |
532 | $d = $self->stringalize($d); |
533 | } elsif (grep {$n eq $_} @boole) { |
534 | $d = $self->booleanize($d); |
535 | } elsif (grep {$n eq $_} @str) { |
536 | $d = $self->stringalize($d); |
537 | } |
538 | } else { |
539 | $d = "undef" |
540 | } |
541 | if ($r ne '') { |
542 | $r = $r . ', ' |
543 | } |
544 | $r = $r . $d; |
545 | } |
546 | return $r |
547 | } |
548 | |
549 | sub pmRuleAnyChar { |
550 | my ($self, $rule) = @_; |
551 | my $optxt = $self->pmParseRuleConvertArgs($rule, qw/String insensitive/, @stdoptions); |
552 | $self->lprint("if (\$self->testAnyChar(\$text, $optxt)) {"); |
553 | $self->pmParseRuleFinish($rule); |
554 | } |
555 | |
556 | sub pmRuleDetectChar { |
557 | my ($self, $rule) = @_; |
558 | my $optxt = $self->pmParseRuleConvertArgs($rule, qw/char insensitive dynamic/, @stdoptions); |
559 | $self->lprint("if (\$self->testDetectChar(\$text, $optxt)) {"); |
560 | $self->pmParseRuleFinish($rule); |
561 | } |
562 | |
563 | sub pmRuleDetect2Chars { |
564 | my ($self, $rule) = @_; |
565 | my $optxt = $self->pmParseRuleConvertArgs($rule, qw/char char1 insensitive dynamic/, @stdoptions); |
566 | $self->lprint("if (\$self->testDetect2Chars(\$text, $optxt)) {"); |
567 | $self->pmParseRuleFinish($rule); |
568 | } |
569 | |
570 | sub pmRuleDetectIdentifier { |
571 | my ($self, $rule) = @_; |
572 | my $optxt = $self->pmParseRuleConvertArgs($rule, @stdoptions); |
573 | $self->lprint("if (\$self->testDetectIdentifier(\$text, $optxt)) {"); |
574 | $self->pmParseRuleFinish($rule); |
575 | } |
576 | |
577 | sub pmRuleDetectSpaces { |
578 | my ($self, $rule) = @_; |
579 | my $optxt = $self->pmParseRuleConvertArgs($rule, @stdoptions); |
580 | $self->lprint("if (\$self->testDetectSpaces(\$text, $optxt)) {"); |
581 | $self->pmParseRuleFinish($rule); |
582 | } |
583 | |
584 | sub pmRuleFloat { |
585 | my ($self, $rule) = @_; |
586 | my $optxt = $self->pmParseRuleConvertArgs($rule, @stdoptions); |
587 | $self->lprint("if (\$self->testFloat(\$text, $optxt)) {"); |
588 | $self->pmParseRuleFinish($rule); |
589 | } |
590 | |
591 | sub pmRuleHlCChar { |
592 | my ($self, $rule) = @_; |
593 | my $optxt = $self->pmParseRuleConvertArgs($rule, @stdoptions); |
594 | $self->lprint("if (\$self->testHlCChar(\$text, $optxt)) {"); |
595 | $self->pmParseRuleFinish($rule); |
596 | } |
597 | |
598 | sub pmRuleHlCHex { |
599 | my ($self, $rule) = @_; |
600 | my $optxt = $self->pmParseRuleConvertArgs($rule, @stdoptions); |
601 | $self->lprint("if (\$self->testHlCHex(\$text, $optxt)) {"); |
602 | $self->pmParseRuleFinish($rule); |
603 | } |
604 | |
605 | sub pmRuleHlCOct { |
606 | my ($self, $rule) = @_; |
607 | my $optxt = $self->pmParseRuleConvertArgs($rule, @stdoptions); |
608 | $self->lprint("if (\$self->testHlCOct(\$text, $optxt)) {"); |
609 | $self->pmParseRuleFinish($rule); |
610 | } |
611 | |
612 | sub pmRuleHlCStringChar { |
613 | my ($self, $rule) = @_; |
614 | my $optxt = $self->pmParseRuleConvertArgs($rule, @stdoptions); |
615 | $self->lprint("if (\$self->testHlCStringChar(\$text, $optxt)) {"); |
616 | $self->pmParseRuleFinish($rule); |
617 | } |
618 | |
619 | sub pmRuleIncludeRules { |
620 | my ($self, $rule) = @_; |
621 | my $context = $self->stringalize($rule->{'context'}); |
622 | my $ed; |
623 | if ($context =~ s/^(')##/$1/) { |
624 | $ed = "includePlugin"; |
625 | } else { |
626 | $ed = "includeRules"; |
627 | } |
628 | $self->lprint("if (\$self->$ed($context, \$text)) {"); |
629 | $self->pmParseRuleFinish($rule); |
630 | } |
631 | |
632 | sub pmRuleInt { |
633 | my ($self, $rule) = @_; |
634 | my $optxt = $self->pmParseRuleConvertArgs($rule, @stdoptions); |
635 | $self->lprint("if (\$self->testInt(\$text, $optxt)) {"); |
636 | $self->pmParseRuleFinish($rule); |
637 | } |
638 | |
639 | sub pmRuleKeyword { |
640 | my ($self, $rule) = @_; |
641 | my $optxt = $self->pmParseRuleConvertArgs($rule, 'String', @stdoptions); |
642 | $self->lprint("if (\$self->testKeyword(\$text, $optxt)) {"); |
643 | $self->pmParseRuleFinish($rule); |
644 | } |
645 | |
646 | sub pmRuleLineContinue { |
647 | my ($self, $rule) = @_; |
648 | my $optxt = $self->pmParseRuleConvertArgs($rule, @stdoptions); |
649 | $self->lprint("if (\$self->testLineContinue(\$text, $optxt)) {"); |
650 | $self->pmParseRuleFinish($rule); |
651 | } |
652 | |
653 | sub pmRuleRangeDetect { |
654 | my ($self, $rule) = @_; |
655 | my $optxt = $self->pmParseRuleConvertArgs($rule, qw/char char1 insensitive/, @stdoptions); |
656 | $self->lprint("if (\$self->testRangeDetect(\$text, $optxt)) {"); |
657 | $self->pmParseRuleFinish($rule); |
658 | } |
659 | |
660 | sub pmRuleRegExpr { |
661 | my ($self, $rule) = @_; |
662 | my $optxt = $self->pmParseRuleConvertArgs($rule, qw/insensitive dynamic/, @stdoptions); |
663 | my $string = $rule->{'String'}; |
664 | my $minimal = $rule->{'minimal'}; |
665 | unless (defined($minimal)) { $minimal = 0 } |
666 | $minimal = $self->booleanize($minimal); |
667 | my $reg = ''; |
668 | if ($minimal) { |
669 | my $lastchar = ''; |
670 | while ($string ne '') { |
671 | if ($string =~ s/^(\*|\+)//) { |
672 | $reg = "$reg$1"; |
673 | if ($lastchar ne "\\") { |
674 | $reg = "$reg?"; |
675 | } |
676 | $lastchar = $1; |
677 | } else { |
678 | $string =~ s/^(.)//; |
679 | $reg = "$reg$1"; |
680 | $lastchar = $1; |
681 | } |
682 | } |
683 | } else { |
684 | $reg = $string; |
685 | } |
686 | $reg = $self->stringalize($reg); |
687 | $self->lprint("if (\$self->testRegExpr(\$text, $reg, $optxt)) {"); |
688 | $self->pmParseRuleFinish($rule); |
689 | } |
690 | |
691 | sub pmRuleStringDetect { |
692 | my ($self, $rule) = @_; |
693 | my $optxt = $self->pmParseRuleConvertArgs($rule, qw/String insensitive dynamic/, @stdoptions); |
694 | $self->lprint("if (\$self->testStringDetect(\$text, $optxt)) {"); |
695 | $self->pmParseRuleFinish($rule); |
696 | } |
697 | |
698 | sub policy { |
699 | my $self = shift; |
700 | if (@_) { $self->{'policy'} = shift; }; |
701 | return $self->{'policy'}; |
702 | } |
703 | |
704 | sub register { |
705 | my ($self, $new) = @_; |
706 | my $reg = $self->{'registered'}; |
707 | my $k = new Syntax::Highlight::Engine::Kate::XMLData($new); |
708 | if (defined($k)) { |
709 | my $name = $k->language->{'name'}; |
710 | if (exists $reg->{$name}) { |
711 | $self->log("language $name already registered, aborting"); |
712 | } else { |
713 | $reg->{$name} = $k; |
714 | my $cmd = $self->guiadd; |
715 | &$cmd($name); |
716 | return $name; |
717 | } |
718 | } else { |
719 | $self->log("cannot load $new"); |
720 | } |
721 | return undef |
722 | } |
723 | |
724 | sub registered { |
725 | my $self = shift; |
726 | my $reg = $self->{'registered'}; |
727 | return sort {uc($a) cmp uc($b)} keys %$reg; |
728 | } |
729 | |
730 | |
731 | sub registryLoad { |
732 | my $self = shift; |
733 | unless (-e $regfile) { return }; |
734 | my $dump = new XML::Dumper; |
735 | my $h = $dump->xml2pl($regfile); |
736 | foreach my $k (sort keys %$h) { |
737 | my $n = $self->register($k); |
738 | my $x = $self->xmldata($n); |
739 | my $m = $h->{$k}; |
740 | foreach my $l (keys %$m) { |
741 | $x->metadata($l, $m->{$l}); |
742 | } |
743 | } |
744 | } |
745 | |
746 | sub registrySave { |
747 | my $self = shift; |
748 | my @keys = $self->registered; |
749 | my %h = (); |
750 | foreach my $k (@keys) { |
751 | my $x = $self->xmldata($k); |
752 | $h{$x->filename} = $x->metadataBackup; |
753 | } |
754 | my $dump = new XML::Dumper; |
755 | $dump->pl2xml(\%h, $regfile); |
756 | } |
757 | |
758 | sub reportAdd { |
759 | my ($self, $status, $rule, $msg) = @_; |
760 | if ($self->verbose) { |
761 | my $st = 'OK '; |
762 | unless ($status) { $st = 'ERROR ' }; |
763 | $self->lprint($st . $msg); |
764 | } |
765 | my $lang = $self->curlang; |
766 | my $context = $self->curcontext; |
767 | my $log = $self->reportdata; |
768 | push @$log, [$status, $lang, $context, $rule, $msg]; |
769 | } |
770 | |
771 | sub reportClear { |
772 | my $self = shift; |
773 | $self->reportdata([]); |
774 | } |
775 | |
776 | sub reportdata { |
777 | my $self = shift; |
778 | if (@_) { $self->{'reportdata'} = shift; }; |
779 | return $self->{'reportdata'}; |
780 | } |
781 | |
782 | sub reportGenerate { |
783 | my ($self, $status) = @_; |
784 | unless (defined($status)) { $status = -1 }; |
785 | my @pos = ('Fail', 'Pass'); |
786 | my @items = (); |
787 | my $l = $self->reportdata; |
788 | if ($status ne -1) { |
789 | foreach my $t (@$l) { |
790 | if ($t->[0] eq $status) { |
791 | push @items, $t; |
792 | } |
793 | } |
794 | } else { |
795 | @items = @$l |
796 | } |
797 | foreach my $i (@items) { |
798 | my $txt = $pos[$i->[0]] . " "; |
799 | $txt = $txt . $self->textLength($i->[1], 16); |
800 | $txt = $txt . $self->textLength($i->[2], 30); |
801 | $txt = $txt . $self->textLength($i->[3], 14); |
802 | $self->lprint($txt . " : " . $i->[4]); |
803 | } |
804 | } |
805 | |
806 | |
807 | #sub runtestLoad { |
808 | # (my $self, $code) = @_; |
809 | # my $sp = new Safe('MySafe'); |
810 | # $sp->reval($code); |
811 | # if ($@) { $self->log(%@) } |
812 | # my $name = $self->moduleName($self->xmldata($curentry)->language->{'name'}); |
813 | # my $mod = "MySafe::Syntax::Highlight::Engine::Kate::$name"; |
814 | # $sp->share('&' . $mod . '::highlight'); |
815 | # $sp->share('&' . $mod . '::reset'); |
816 | # my $p; |
817 | # eval ('$p = new ' . $mod); |
818 | # if ($@) { $self->log(%@) } |
819 | # if (defined($p)) { |
820 | # $self->runtest($p) |
821 | # } else { |
822 | # $self->runtest(''); |
823 | # } |
824 | #} |
825 | # |
826 | sub runtest { |
827 | my $self = shift; |
828 | if (@_) { $self->{'runtest'} = shift; }; |
829 | return $self->{'runtest'}; |
830 | } |
831 | |
832 | sub stringalize { |
833 | my ($self, $in) = @_; |
834 | $in =~ s/\\/\\\\/g; |
835 | $in =~ s/\'/\\'/g; |
836 | # $in =~ s/\$/\\\$/g; |
837 | $in = "'$in'"; |
838 | return $in; |
839 | } |
840 | |
841 | sub testContextItems { |
842 | my $self = shift; |
843 | my @test = sort keys %testopts; |
844 | while (@_) { |
845 | my $item = shift; |
846 | my $type = $item->{'type'}; |
847 | $self->lprint("testing rule $type"); |
848 | $self->indentUp; |
849 | if (exists $tests{$type}) { |
850 | my $c = $tests{$type}; |
851 | &$c($self, $item); |
852 | } else { |
853 | $self->reportType(0, $type, "rule type $type does NOT exist"); |
854 | } |
855 | if (exists $item->{'items'}) { |
856 | my $i = $item->{'items'}; |
857 | $self->lprint("testing sub rules"); |
858 | $self->indentUp; |
859 | $self->testContextItems(@$i); #recursive |
860 | $self->indentDown; |
861 | } |
862 | $self->indentDown; |
863 | } |
864 | } |
865 | |
866 | sub testRuleOptions { |
867 | my ($self, $item) = @_; |
868 | my @test = sort keys %testopts; |
869 | my $type = $item->{'type'}; |
870 | my $result = 1; |
871 | #test the options to the rule |
872 | foreach my $t (@test) { |
873 | my $o = $item->{$t}; |
874 | if (defined($o)) { |
875 | my $c = $testopts{$t}; |
876 | if (&$c($o)) { |
877 | $self->reportAdd(1, $type, "option '$t' with value '$o' is valid"); |
878 | } else { |
879 | $self->reportAdd(0, $type, "option '$t', value '$o' is NOT valid"); |
880 | $result = 0; |
881 | } |
882 | } |
883 | } |
884 | #test if attribute points to something defined |
885 | if (exists($item->{'attribute'})) { |
886 | my $att = $item->{'attribute'}; |
887 | if (exists $self->xmldata($self->curlang)->itemdata->{$att}) { |
888 | $self->reportAdd(1, $type, "attribute '$att' is defined in itemdata"); |
889 | } else { |
890 | $self->reportAdd(0, $type, "attribute '$att' is NOT defined in itemdata"); |
891 | $result = 0; |
892 | } |
893 | } |
894 | #test if context points to something defined |
895 | if (exists($item->{'context'})) { |
896 | my $ctx = $item->{'context'}; |
897 | if ($ctx eq '#stay') { |
898 | $self->reportAdd(1, $type, "context '$ctx' recognized"); |
899 | } elsif ($ctx =~ /^##(.+)/) { |
900 | my $x = $self->xmldata($1); |
901 | if (defined($1)) { |
902 | $self->reportAdd(1, $type, "context '$ctx' refers to language '$1'"); |
903 | } else { |
904 | $self->reportAdd(0, $type, "context '$ctx' refers to undefined language '$1'"); |
905 | $result = 0; |
906 | } |
907 | } elsif ($ctx =~ /^[#pop]+$/) { |
908 | $self->reportAdd(1, $type, "context '$ctx' recognized"); |
909 | } elsif (exists $self->xmldata($self->curlang)->contexts->{$ctx}) { |
910 | $self->reportAdd(1, $type, "context '$ctx' is defined in contexts"); |
911 | } else { |
912 | $self->reportAdd(0, $type, "context '$ctx' is NOT defined in contexts"); |
913 | $result = 0; |
914 | } |
915 | } |
916 | return $result; |
917 | } |
918 | |
919 | sub testRuleAnyChar { |
920 | my ($self, $item) = @_; |
921 | my $result = 1; |
922 | unless ($self->testRuleOptions($item)) { $result = 0 }; |
923 | return $result; |
924 | } |
925 | |
926 | sub testRuleDetectChar { |
927 | my ($self, $item) = @_; |
928 | my $result = 1; |
929 | unless ($self->testRuleOptions($item)) { $result = 0 }; |
930 | return $result; |
931 | } |
932 | |
933 | sub testRuleDetect2Chars { |
934 | my ($self, $item) = @_; |
935 | my $result = 1; |
936 | unless ($self->testRuleOptions($item)) { $result = 0 }; |
937 | return $result; |
938 | } |
939 | |
940 | sub testRuleDetectIdentifier { |
941 | my ($self, $item) = @_; |
942 | my $result = 1; |
943 | unless ($self->testRuleOptions($item)) { $result = 0 }; |
944 | return $result; |
945 | } |
946 | |
947 | sub testRuleDetectSpaces { |
948 | my ($self, $item) = @_; |
949 | my $result = 1; |
950 | unless ($self->testRuleOptions($item)) { $result = 0 }; |
951 | return $result; |
952 | } |
953 | |
954 | sub testRuleFloat { |
955 | my ($self, $item) = @_; |
956 | my $result = 1; |
957 | unless ($self->testRuleOptions($item)) { $result = 0 }; |
958 | return $result; |
959 | } |
960 | |
961 | sub testRuleHlCChar { |
962 | my ($self, $item) = @_; |
963 | my $result = 1; |
964 | unless ($self->testRuleOptions($item)) { $result = 0 }; |
965 | return $result; |
966 | } |
967 | |
968 | sub testRuleHlCHex { |
969 | my ($self, $item) = @_; |
970 | my $result = 1; |
971 | unless ($self->testRuleOptions($item)) { $result = 0 }; |
972 | return $result; |
973 | } |
974 | |
975 | sub testRuleHlCOct { |
976 | my ($self, $item) = @_; |
977 | my $result = 1; |
978 | unless ($self->testRuleOptions($item)) { $result = 0 }; |
979 | return $result; |
980 | } |
981 | |
982 | sub testRuleHlCStringChar { |
983 | my ($self, $item) = @_; |
984 | my $result = 1; |
985 | unless ($self->testRuleOptions($item)) { $result = 0 }; |
986 | return $result; |
987 | } |
988 | |
989 | sub testRuleIncludeRules { |
990 | my ($self, $item) = @_; |
991 | my $result = 1; |
992 | unless ($self->testRuleOptions($item)) { $result = 0 }; |
993 | return $result; |
994 | } |
995 | |
996 | sub testRuleInt { |
997 | my ($self, $item) = @_; |
998 | my $result = 1; |
999 | unless ($self->testRuleOptions($item)) { $result = 0 }; |
1000 | return $result; |
1001 | } |
1002 | |
1003 | sub testRuleKeyword { |
1004 | my ($self, $item) = @_; |
1005 | my $s = $item->{'String'}; |
1006 | my $l = $self->xmldata($self->curlang)->lists->{$s}; |
1007 | my $val = 0; |
1008 | if (defined($l) ) { |
1009 | $self->reportAdd(1, $item->{'type'}, "$s refers to an existing list"); |
1010 | $val = 1; |
1011 | } else { |
1012 | $self->reportAdd(0, $item->{'type'}, "$s does not refer to an existing list"); |
1013 | } |
1014 | return $val; |
1015 | } |
1016 | |
1017 | sub testRuleLineContinue { |
1018 | my ($self, $item) = @_; |
1019 | my $result = 1; |
1020 | unless ($self->testRuleOptions($item)) { $result = 0 }; |
1021 | return $result; |
1022 | } |
1023 | |
1024 | sub testRuleRangeDetect { |
1025 | my ($self, $item) = @_; |
1026 | my $result = 1; |
1027 | unless ($self->testRuleOptions($item)) { $result = 0 }; |
1028 | return $result; |
1029 | } |
1030 | |
1031 | sub testRuleRegExpr { |
1032 | my ($self, $item) = @_; |
1033 | my $s = $item->{'String'}; |
1034 | my $res = "regex test '$s'"; |
1035 | my $stub = "stubtext"; |
1036 | eval "\$stub =~ /\$s/"; |
1037 | my $val = 0; |
1038 | if ($@) { |
1039 | my $bck = $@; |
1040 | chomp $bck; |
1041 | $self->reportAdd(0, $item->{'type'}, "$res : $bck"); |
1042 | } else { |
1043 | $self->reportAdd(1, $item->{'type'}, $res); |
1044 | $val = 1; |
1045 | } |
1046 | # $self->lprint($res); |
1047 | return $val; |
1048 | } |
1049 | |
1050 | |
1051 | sub testRuleStringDetect { |
1052 | my ($self, $item) = @_; |
1053 | my $result = 1; |
1054 | unless ($self->testRuleOptions($item)) { $result = 0 }; |
1055 | return $result; |
1056 | } |
1057 | |
1058 | sub textLength { |
1059 | my ($cw, $txt, $length) = @_; |
1060 | while (length($txt) < $length) { $txt = $txt . " " } |
1061 | return $txt; |
1062 | } |
1063 | |
1064 | |
1065 | sub verbose { |
1066 | my $self = shift; |
1067 | if (@_) { $self->{'verbose'} = shift; }; |
1068 | return $self->{'verbose'}; |
1069 | } |
1070 | |
1071 | sub version { |
1072 | my $self = shift; |
1073 | if (@_) { $self->{'version'} = shift; }; |
1074 | return $self->{'version'}; |
1075 | } |
1076 | |
1077 | sub xmldata { |
1078 | my ($self, $lang) = @_; |
1079 | if (defined($lang)) { |
1080 | my $r = $self->{'registered'}; |
1081 | unless (exists $r->{$lang}) { return undef } |
1082 | return $r->{$lang} |
1083 | } else { |
1084 | $self->log("language not specified, cannot return xmldata object"); |
1085 | return undef; |
1086 | } |
1087 | } |
1088 | |
1089 | 1; |