/[suikacvs]/markup/html/whatpm/Whatpm/WebIDL.pm
Suika

Contents of /markup/html/whatpm/Whatpm/WebIDL.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.15 - (show annotations) (download)
Tue Sep 16 04:20:52 2008 UTC (17 years, 11 months ago) by wakaba
Branch: MAIN
Changes since 1.14: +275 -40 lines
++ whatpm/t/webidl/ChangeLog	16 Sep 2008 04:20:29 -0000
2008-09-16  Wakaba  <wakaba@suika.fam.cx>

	* webidl-interface.dat, webidl-exception.dat: New test cases for
	extended attributes are added.

	* webidl-defs.dat: Test results updated.

++ whatpm/Whatpm/ChangeLog	16 Sep 2008 04:19:57 -0000
2008-09-16  Wakaba  <wakaba@suika.fam.cx>

	* WebIDL.pm: Unescapes extended attribute names and extended
	attribute identifiers.  Preserve whether an extended attribute has
	an argument list of not.  Support for extended attributes:
	Constructor, ExceptionConsts, IndexGetter, IndexSetter,
	NameGetter, NameSetter, and Null.
	(has_argument_list): New attribute.
	(idl_text): Stringifies argument lists, if any, even if it is
	empty.

1 package Whatpm::WebIDL;
2 use strict;
3
4 package Whatpm::WebIDL::Parser;
5
6 my $integer = qr/-?0([Xx][0-9A-Fa-f]+|[0-7]*)|[1-9][0-9]*/;
7 ## ISSUE: Spec's pattern is wrong as a Perl5 regular expression.
8 my $float = qr/-?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+/;
9 my $identifier = qr/[A-Z_a-z][0-9A-Z_a-z]*/;
10 my $whitespace = qr<[\t\n\r ]+|[\t\n\r ]*((//.*|/\*.*?\*/)[\t\n\r ]*)+>;
11
12 my $default_levels = {
13 must => 'm',
14 should => 's',
15 warn => 'w',
16 fact => 'w',
17 undefined => 'w',
18 info => 'i',
19 uncertain => 'u',
20 };
21
22 sub new ($) {
23 my $self = bless {
24 must_level => 'm',
25 should_level => 's',
26 warn_level => 'w',
27 info_level => 'i',
28 level => $default_levels,
29 }, $_[0];
30 return $self;
31 } # new
32
33 sub parse_char_string ($$;$) {
34 my $self = shift;
35 my $s = ref $_[0] ? $_[0] : \($_[0]);
36
37 my $defs = Whatpm::WebIDL::Definitions->new;
38 $defs->set_user_data (manakai_source_line => 1);
39 $defs->set_user_data (manakai_source_column => 1);
40
41 pos ($$s) = 0;
42 my $line = 1;
43 my $column = 0;
44
45 my $get_next_token = sub {
46 if (length $$s <= pos $$s) {
47 return {type => 'eof'};
48 }
49
50 while ($$s =~ /\G($whitespace)/gc) {
51 my $v = $1;
52 while ($v =~ s/^[^\x0D\x0A]*(?>\x0D\x0A?|\x0A)//) {
53 $line++;
54 $column = 0;
55 }
56 $column += length $v;
57 }
58
59 if (length $$s <= pos $$s) {
60 return {type => 'eof'};
61 }
62
63 ## ISSUE: "must"s in "A. IDL grammer" are not "MUST"s.
64
65 if ($$s =~ /\G($identifier)/gc) {
66 my $v = $1;
67 $column += length $v;
68 if ({
69 module => 1, interface => 1, exception => 1, typedef => 1,
70 valuetype => 1, DOMString => 1, sequence => 1, unsigned => 1,
71 short => 1, const => 1, TRUE => 1, FALSE => 1, readonly => 1,
72 attribute => 1, getraises => 1, setraises => 1, raises => 1, in => 1,
73 any => 1, boolean => 1, octet => 1, float => 1, Object => 1,
74 short => 1, long => 1, void => 1,
75 }->{$v}) {
76 return {type => $v};
77 } else {
78 return {type => 'identifier', value => $v};
79 }
80 } elsif ($$s =~ /\G($float)/gc) { ## ISSUE: negative number
81 $column += length $1;
82 return {type => 'float', value => $1};
83 } elsif ($$s =~ /\G($integer)/gc) { ## ISSUE: negative nmber
84 $column += length $1;
85 return {type => 'integer', value => $1};
86 } elsif ($$s =~ /\G::/gcs) {
87 $column += 2;
88 return {type => '::'};
89 } elsif ($$s =~ /\G(.)/gcs) { ## NOTE: Should never be a newline char.
90 $column++;
91 return {type => $1};
92 } else {
93 die "get_next_token: non-possible case: " . substr ($$s, pos $$s, 20);
94 }
95 }; # $get_next_token
96
97 my $state = 'before definitions';
98 my $token = $get_next_token->();
99 my $nest_level = 0;
100 my $next_state;
101 my $xattrs;
102 my $prev_xattrs = [];
103 my $last_xattr;
104 my $read_only;
105 my $current_type;
106 my @current = ($defs);
107
108 my $_onerror = $_[1] || sub {
109 my %opt = @_;
110 my $r = 'Line ' . $opt{line} . ' column ' . $opt{column} . ': ';
111
112 if ($opt{token}) {
113 $r .= 'Token ' . (defined $opt{token}->{value} ? $opt{token}->{value} : $opt{token}->{type}) . ': ';
114 }
115
116 $r .= $opt{type} . ';' . $opt{level};
117
118 warn $r . "\n";
119 }; # $_onerror
120 my $onerror = sub {
121 $_onerror->(line => $line, column => $column, token => $token, @_);
122 }; # $onerror
123
124 my $get_scoped_name = sub {
125 ## NOTE: Convert a |ScopedName| into a "scoped name".
126
127 my $name = [];
128
129 ## NOTE: "DOMString" is not a scoped name, while "::DOMString"
130 ## and "x::DOMString" are.
131
132 if ($token->{type} eq 'identifier') {
133 my $identifier = $token->{value};
134 $identifier =~ s/^_//;
135 push @$name, $identifier;
136 $token = $get_next_token->();
137 while ($token->{type} eq '::') {
138 $token = $get_next_token->();
139 if ($token->{type} eq 'identifier') {
140 my $identifier = $token->{value};
141 $identifier =~ s/^_//;
142 push @$name, $identifier;
143 $token = $get_next_token->();
144 } elsif ($token->{type} eq 'DOMString') {
145 push @$name, 'DOMString', '', '';
146 $token = $get_next_token->();
147 last;
148 }
149 }
150 } elsif ($token->{type} eq '::') {
151 push @$name, '';
152 while ($token->{type} eq '::') {
153 $token = $get_next_token->();
154 if ($token->{type} eq 'identifier') {
155 my $identifier = $token->{value};
156 $identifier =~ s/^_//;
157 push @$name, $identifier;
158 $token = $get_next_token->();
159 } elsif ($token->{type} eq 'DOMString') {
160 push @$name, 'DOMString', '', '';
161 $token = $get_next_token->();
162 last;
163 } else {
164 last;
165 }
166 }
167
168 if (@$name == 1) {
169 return undef;
170 }
171 } else {
172 # reconsume
173 return undef;
174 }
175 return join '::', @$name;
176 }; # $get_scoped_name
177
178 my $get_type;
179 $get_type = sub {
180 my $r;
181 if ({
182 void => 1, any => 1, boolean => 1, octet => 1, float => 1,
183 DOMString => 1, Object => 1, short => 1,
184 }->{$token->{type}}) {
185 $r = '::'.$token->{type}.'::';
186 $token = $get_next_token->();
187 } elsif ($token->{type} eq 'unsigned') {
188 $token = $get_next_token->();
189 if ($token->{type} eq 'short') {
190 $r = '::unsigned '.$token->{type}.'::';
191 $token = $get_next_token->();
192 } elsif ($token->{type} eq 'long') {
193 $token = $get_next_token->();
194 if ($token->{type} eq 'long') {
195 $r = '::unsigned long long::';
196 $token = $get_next_token->();
197 } else {
198 $r = '::unsigned long::';
199 # reconsume
200 }
201 } else {
202 $onerror->(type => 'unsigned', level => $self->{must_level});
203 return undef;
204 }
205 } elsif ($token->{type} eq 'long') {
206 $token = $get_next_token->();
207 if ($token->{type} eq 'long') {
208 $r = '::long long::';
209 $token = $get_next_token->();
210 } else {
211 $r = '::long::';
212 # reconsume
213 }
214 } elsif ($token->{type} eq '::' or $token->{type} eq 'identifier') {
215 $r = $get_scoped_name->();
216 if (defined $r) {
217 # next token
218 } else { # "::" not followed by identifier or "DOMString"
219 $onerror->(type => 'scoped name', level => $self->{must_level});
220 return undef;
221 }
222 } elsif ($token->{type} eq 'sequence') {
223 $token = $get_next_token->();
224 if ($token->{type} eq '<') {
225 $token = $get_next_token->();
226 if ({
227 void => 1, any => 1, boolean => 1, octet => 1, float => 1,
228 DOMString => 1, Object => 1, short => 1, long => 1, unsigned => 1,
229 sequence => 1, '::' => 1, identifier => 1,
230 }->{$token->{type}}) {
231 my $type = $get_type->();
232 if (defined $type) {
233 if ($token->{type} eq '>') {
234 $r = '::::sequence::::' . $type;
235 $token = $get_next_token->();
236 } else {
237 $onerror->(type => 'sequence gt', level => $self->{must_level});
238 return undef;
239 }
240 } else {
241 # error reported
242 return undef;
243 }
244 } else {
245 $onerror->(type => 'sequence type', level => $self->{must_level});
246 return undef;
247 }
248 } else {
249 $onerror->(type => 'sequence lt', level => $self->{must_level});
250 return undef;
251 }
252 } else {
253 die "get_type: bad token: $token->{type}";
254 }
255
256 return $r;
257 }; # $get_type
258
259 while (1) {
260 if ($state eq 'before definitions') {
261 $xattrs = [];
262 if ($token->{type} eq '[') {
263 $token = $get_next_token->();
264 $state = 'before xattr';
265 $next_state = 'before def';
266 } elsif ({module => 1, interface => 1, exception => 1,
267 typedef => 1, valuetype => 1, const => 1}->{$token->{type}}) {
268 # reconsume
269 $state = 'before def';
270 } elsif ($token->{type} eq '}' and @current > 1) {
271 $token = $get_next_token->();
272 $state = 'before block semicolon';
273 } elsif ($token->{type} eq 'eof') {
274 last;
275 } else {
276 $onerror->(type => 'before definitions', level => 'm',
277 token => $token);
278 # reconsume
279 $state = 'ignore';
280 $nest_level = 0;
281 $next_state = 'before definitions';
282 }
283 } elsif ($state eq 'before xattr') {
284 if ($token->{type} eq 'identifier') {
285 my $id = $token->{value};
286 $id =~ s/^_//;
287 push @current, Whatpm::WebIDL::ExtendedAttribute->new ($id);
288 $current[-1]->set_user_data (manakai_source_line => $line);
289 $current[-1]->set_user_data (manakai_source_column => $column);
290 push @$xattrs, $current[-1];
291 $token = $get_next_token->();
292 $state = 'after xattr';
293 } else {
294 $onerror->(type => 'before xattr', level => 'm', token => $token);
295 # reconsume
296 $state = 'ignore';
297 $nest_level = 0;
298 $next_state = 'before definitions'; ## TODO:
299 }
300 } elsif ($state eq 'after xattr') {
301 if ($token->{type} eq '=') {
302 $token = $get_next_token->();
303 $state = 'before xattrarg';
304 } elsif ($token->{type} eq '(') {
305 $current[-1]->has_argument_list (1);
306 $token = $get_next_token->();
307 if ($token->{type} eq ')') {
308 $token = $get_next_token->();
309 push @$prev_xattrs, $xattrs;
310 $state = 'after xattrarg';
311 } else {
312 push @$prev_xattrs, $xattrs;
313 # reconsume
314 $state = 'before argument';
315 }
316 } else {
317 push @$prev_xattrs, $xattrs;
318 # reconsume
319 $state = 'after xattrarg';
320 }
321 } elsif ($state eq 'before xattrarg') {
322 if ($token->{type} eq 'identifier') {
323 my $identifier = $token->{value};
324 $identifier =~ s/^_//;
325 $current[-1]->value ($identifier);
326 $token = $get_next_token->();
327 if ($token->{type} eq '(') {
328 $current[-1]->has_argument_list (1);
329 $token = $get_next_token->();
330 if ($token->{type} eq ')') {
331 push @$prev_xattrs, $xattrs;
332 $token = $get_next_token->();
333 $state = 'after xattrarg';
334 } else {
335 push @$prev_xattrs, $xattrs;
336 # reconsume
337 $state = 'before argument';
338 }
339 } else {
340 push @$prev_xattrs, $xattrs;
341 # reconsume
342 $state = 'after xattrarg';
343 }
344 } else {
345 $onerror->(type => 'after xattrarg', level => 'm', token => $token);
346 # reconsume
347 $state = 'ignore';
348 $nest_level = 0;
349 $next_state = 'before definitions';
350 }
351 } elsif ($state eq 'after xattrarg') {
352 pop @current; # xattr
353 $xattrs = pop @$prev_xattrs;
354 if ($token->{type} eq ',') {
355 $token = $get_next_token->();
356 $state = 'before xattr';
357 } elsif ($token->{type} eq ']') {
358 $token = $get_next_token->();
359 if ($current[-1]->isa ('Whatpm::WebIDL::Definitions') or
360 $current[-1]->isa ('Whatpm::WebIDL::Module')) {
361 $state = 'before def';
362 } elsif ($current[-1]->isa ('Whatpm::WebIDL::Interface')) {
363 $state = 'before interface member';
364 } elsif ($current[-1]->isa ('Whatpm::WebIDL::Exception')) {
365 $state = 'before exception member';
366 } elsif ($current[-1]->isa ('Whatpm::WebIDL::Operation') or
367 $current[-1]->isa ('Whatpm::WebIDL::ExtendedAttribute')) {
368 $state = 'before argument in';
369 } else {
370 die "$0: Unknown xattr context: " . ref $current[-1];
371 }
372 } else {
373 $onerror->(type => 'after xattr', level => 'm', token => $token);
374 # reconsume
375 $state = 'ignore';
376 $nest_level = 0;
377 $next_state = 'before definitions'; ## TODO:
378 }
379 } elsif ($state eq 'before def') {
380 if ($token->{type} eq 'module') {
381 $token = $get_next_token->();
382 if ($token->{type} eq 'identifier') {
383 my $identifier = $token->{value};
384 $identifier =~ s/^_//;
385 push @current, Whatpm::WebIDL::Module->new ($identifier);
386 $current[-1]->set_user_data (manakai_source_line => $line);
387 $current[-1]->set_user_data (manakai_source_column => $column);
388 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
389 $token = $get_next_token->();
390 $state = 'before module block';
391 next;
392 } else {
393 $onerror->(type => 'module identifier',
394 level => $self->{must_level});
395 #
396 }
397 } elsif ($token->{type} eq 'interface') {
398 $token = $get_next_token->();
399 if ($token->{type} eq 'identifier') {
400 my $identifier = $token->{value};
401 $identifier =~ s/^_//;
402 push @current, Whatpm::WebIDL::Interface->new ($identifier);
403 $current[-1]->set_user_data (manakai_source_line => $line);
404 $current[-1]->set_user_data (manakai_source_column => $column);
405 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
406 $token = $get_next_token->();
407 $state = 'before interface inheritance';
408 next;
409 } else {
410 $onerror->(type => 'interface identifier',
411 level => $self->{must_level});
412 #
413 }
414 } elsif ($token->{type} eq 'exception') {
415 $token = $get_next_token->();
416 if ($token->{type} eq 'identifier') {
417 my $identifier = $token->{value};
418 $identifier =~ s/^_//;
419 push @current, Whatpm::WebIDL::Exception->new ($identifier);
420 $current[-1]->set_user_data (manakai_source_line => $line);
421 $current[-1]->set_user_data (manakai_source_column => $column);
422 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
423 $token = $get_next_token->();
424 $state = 'before exception block';
425 next;
426 } else {
427 $onerror->(type => 'exception identifier',
428 level => $self->{must_level});
429 #
430 }
431 } elsif ($token->{type} eq 'typedef') {
432 $token = $get_next_token->();
433 $state = 'before typedef type';
434 next;
435 } elsif ($token->{type} eq 'valuetype') {
436 $token = $get_next_token->();
437 if ($token->{type} eq 'identifier') {
438 my $identifier = $token->{value};
439 $identifier =~ s/^_//;
440 push @current, Whatpm::WebIDL::Valuetype->new ($identifier);
441 $current[-1]->set_user_data (manakai_source_line => $line);
442 $current[-1]->set_user_data (manakai_source_column => $column);
443 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
444 $token = $get_next_token->();
445 $state = 'before boxed type';
446 next;
447 } elsif ($token->{type} eq 'DOMString') {
448 push @current, Whatpm::WebIDL::Valuetype->new ('::DOMString::');
449 $current[-1]->set_user_data (manakai_source_line => $line);
450 $current[-1]->set_user_data (manakai_source_column => $column);
451 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
452 $token = $get_next_token->();
453 if ($token->{type} eq 'sequence') {
454 $token = $get_next_token->();
455 if ($token->{type} eq '<') {
456 $token = $get_next_token->();
457 if ($token->{type} eq 'unsigned') {
458 $token = $get_next_token->();
459 if ($token->{type} eq 'short') {
460 $token = $get_next_token->();
461 if ($token->{type} eq '>') {
462 $current[-1]->type ('::::sequence::::::unsigned short::');
463 $token = $get_next_token->();
464 $state = 'before semicolon';
465 $next_state = 'before definitions';
466 next;
467 } else {
468 #
469 }
470 } else {
471 #
472 }
473 } else {
474 #
475 }
476 } else {
477 #
478 }
479 } else {
480 #
481 }
482 $onerror->(type => 'valuetype DOMString',
483 level => $self->{must_level});
484 pop @current; # valuetype
485 #
486 } else {
487 $onerror->(type => 'valuetype identifier',
488 level => $self->{must_level});
489 #
490 }
491 } elsif ($token->{type} eq 'const') {
492 $token = $get_next_token->();
493 $state = 'before const type';
494 $next_state = 'before definitions';
495 next;
496 } else {
497 $onerror->(type => 'before definition', level => 'm',
498 token => $token);
499 # reconsume
500 #
501 }
502 $state = 'ignore';
503 $nest_level = 0;
504 $next_state = 'before definitions';
505 } elsif ($state eq 'before module block') {
506 if ($token->{type} eq '{') {
507 $token = $get_next_token->();
508 $state = 'before definitions';
509 } else {
510 $onerror->(type => 'before module block', level => 'm',
511 token => $token);
512 pop @current; # module
513 # reconsume
514 $state = 'ignore';
515 $nest_level = 0;
516 $next_state = 'before definitions';
517 }
518 } elsif ($state eq 'before interface inheritance') {
519 if ($token->{type} eq ':') {
520 $token = $get_next_token->();
521 $state = 'before parent interface name';
522 } elsif ($token->{type} eq ';') {
523 $current[-1]->is_forward_declaration (1);
524 # reconsume
525 $state = 'before semicolon';
526 $next_state = 'before interface member';
527 } else {
528 # reconsume
529 $state = 'before interface block';
530 }
531 } elsif ($state eq 'before parent interface name') {
532 my $name = $get_scoped_name->();
533 if (defined $name) {
534 $current[-1]->append_inheritance ($name);
535
536 if ($token->{type} eq ',') {
537 $token = $get_next_token->();
538 # stay in the state
539 } else {
540 # reconsume
541 $state = 'before interface block';
542 }
543 } else {
544 $onerror->(type => 'scoped name', level => $self->{must_level});
545 # reconsume
546 $state = 'ignore';
547 $nest_level = 0;
548 }
549 } elsif ($state eq 'before exception name') {
550 my $name = $get_scoped_name->();
551 if (defined $name) {
552 my $method = $next_state eq '*raises' ? 'append_raises' :
553 $next_state eq '*getraises' ? 'append_getraises' :
554 'append_setraises';
555 $current[-1]->$method ($name);
556
557 if ($token->{type} eq ',') {
558 $token = $get_next_token->();
559 # stay in the state
560 } elsif ($token->{type} eq ')') {
561 $token = $get_next_token->();
562 if ($next_state eq '*getraises' and $token->{type} eq 'setraises') {
563 $token = $get_next_token->();
564 $state = 'after raises';
565 $next_state = '*setraises';
566 } else {
567 # reprocess
568 $state = 'before semicolon';
569 $next_state = 'before interface member';
570 }
571 } else {
572 $onerror->(type => 'after exception name',
573 level => $self->{must_level});
574 # reconsume
575 $state = 'ignore';
576 $nest_level = 0;
577 }
578 } else {
579 $onerror->(type => 'scoped name', level => $self->{must_level});
580 # reconsume
581 $state = 'ignore';
582 $nest_level = 0;
583 }
584 } elsif ($state eq 'before interface block') {
585 if ($token->{type} eq '{') {
586 $token = $get_next_token->();
587 $state = 'before members';
588 $next_state = 'before interface member';
589 } else {
590 $onerror->(type => 'before interface block',
591 level => $self->{must_level});
592 pop @current; # interface
593 # reconsume
594 $state = 'ignore';
595 $nest_level = 0;
596 }
597 } elsif ($state eq 'before exception block') {
598 if ($token->{type} eq '{') {
599 $token = $get_next_token->();
600 $state = 'before members';
601 $next_state = 'before exception member';
602 } else {
603 $onerror->(type => 'before exception block',
604 level => $self->{must_level});
605 # reconsume
606 $state = 'ignore';
607 $nest_level = 0;
608 }
609 } elsif ($state eq 'before members') {
610 $xattrs = [];
611 if ($token->{type} eq '[') {
612 $token = $get_next_token->();
613 $state = 'before xattr';
614 #$next_state = $next_state; # 'before interface member' or ...
615 } elsif ($token->{type} eq '}') {
616 $token = $get_next_token->();
617 $state = 'before block semicolon';
618 } else {
619 # reconsume
620 $state = $next_state; # ... 'before exception member'
621 }
622 } elsif ($state eq 'before interface member') {
623 if ($token->{type} eq 'const') {
624 $token = $get_next_token->();
625 $state = 'before const type';
626 $next_state = 'before definitions';
627 } elsif ($token->{type} eq 'readonly') {
628 $read_only = 1;
629 $token = $get_next_token->();
630 if ($token->{type} eq 'attribute') {
631 $token = $get_next_token->();
632 $state = 'after attribute';
633 } else {
634 # reconsume
635 $state = 'ignore';
636 $nest_level = 0;
637 $next_state = 'before interface member';
638 }
639 } elsif ($token->{type} eq 'attribute') {
640 $read_only = 0;
641 $token = $get_next_token->();
642 $state = 'after attribute';
643 } elsif ({
644 void => 1, any => 1, boolean => 1, octet => 1, float => 1,
645 DOMString => 1, Object => 1, unsigned => 1, short => 1, long => 1,
646 '::' => 1, identifier => 1,
647 }->{$token->{type}}) {
648 # reconsume
649 $state = 'before operation type';
650 } elsif ($token->{type} eq '}') {
651 $token = $get_next_token->();
652 $state = 'before semicolon';
653 $next_state = 'before definitions';
654 } elsif ($token->{type} eq 'eof') {
655 last;
656 } else {
657 $onerror->(type => 'before interface member',
658 level => $self->{must_level});
659 # reconsume
660 $state = 'ignore';
661 $nest_level = 0;
662 }
663 } elsif ($state eq 'before exception member') {
664 if ({
665 void => 1, any => 1, boolean => 1, octet => 1, float => 1,
666 DOMString => 1, Object => 1, unsigned => 1, short => 1, long => 1,
667 '::' => 1, identifier => 1,
668 }->{$token->{type}}) {
669 # reconsume
670 $state = 'before exception member type';
671 } elsif ($token->{type} eq '}') {
672 $token = $get_next_token->();
673 $state = 'before semicolon';
674 $next_state = 'before definitions';
675 } elsif ($token->{type} eq 'eof') {
676 last;
677 } else {
678 $onerror->(type => 'before exception member',
679 level => $self->{must_level});
680 # reconsume
681 $state = 'ignore';
682 $nest_level = 0;
683 }
684 } elsif ($state eq 'before typedef type') {
685 if ({
686 void => 1, any => 1, boolean => 1, octet => 1, float => 1,
687 DOMString => 1, Object => 1, short => 1, long => 1, unsigned => 1,
688 sequence => 1, '::' => 1, identifier => 1,
689 }->{$token->{type}}) {
690 $current_type = $get_type->();
691 if (defined $current_type) {
692 # next token
693 $state = 'before typedef rest';
694 } else {
695 # reconsume
696 $state = 'ignore';
697 $nest_level = 0;
698 $next_state = 'before definitions';
699 }
700 } else {
701 $onerror->(type => 'before type', level => $self->{must_level});
702 # reconsume
703 $state = 'ignore';
704 $nest_level = 0;
705 $next_state = 'before definitions';
706 }
707 } elsif ($state eq 'before boxed type') {
708 if ({
709 boolean => 1, octet => 1, float => 1,
710 short => 1, long => 1, unsigned => 1,
711 sequence => 1, '::' => 1, identifier => 1,
712 }->{$token->{type}}) {
713 $current_type = $get_type->();
714 if (defined $current_type) {
715 $current[-1]->type ($current_type);
716 # next token
717 $state = 'before semicolon';
718 $next_state = 'before definitions';
719 } else {
720 pop @current; # valuetype
721 # reconsume
722 $state = 'ignore';
723 $nest_level = 0;
724 $next_state = 'before definitions';
725 }
726 } else {
727 $onerror->(type => 'before boxed type', level => $self->{must_level});
728 pop @current; # valuetype
729 # reconsume
730 $state = 'ignore';
731 $nest_level = 0;
732 $next_state = 'before definitions';
733 }
734 } elsif ($state eq 'before const type') {
735 if ({
736 any => 1, boolean => 1, octet => 1, float => 1,
737 DOMString => 1, Object => 1, short => 1, long => 1, unsigned => 1,
738 '::' => 1, identifier => 1,
739 }->{$token->{type}}) {
740 $current_type = $get_type->();
741 if (defined $current_type) {
742 # next token
743 $state = 'before const identifier';
744 } else {
745 # reconsume
746 $state = 'ignore';
747 $nest_level = 0;
748 #$next_state = $next_state;
749 }
750 } else {
751 $onerror->(type => 'before type', level => $self->{must_level});
752 # reconsume
753 $state = 'ignore';
754 $nest_level = 0;
755 #$next_state = $next_state;
756 }
757 } elsif ($state eq 'before typedef rest') {
758 if ($token->{type} eq 'identifier') {
759 ## TODO: unescape
760 push @current, Whatpm::WebIDL::Typedef->new ($token->{value});
761 $current[-1]->set_user_data (manakai_source_line => $line);
762 $current[-1]->set_user_data (manakai_source_column => $column);
763 $current[-1]->type ($current_type);
764 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
765 $token = $get_next_token->();
766 $state = 'before semicolon';
767 $next_state = 'before definitions';
768 } elsif ($token->{type} eq 'DOMString') {
769 push @current, Whatpm::WebIDL::Typedef->new ('::DOMString::');
770 $current[-1]->type ($current_type);
771 $current[-1]->set_user_data (manakai_source_line => $line);
772 $current[-1]->set_user_data (manakai_source_column => $column);
773 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
774 $token = $get_next_token->();
775 $state = 'before semicolon';
776 $next_state = 'before defnitions';
777 } else {
778 $onerror->(type => 'before typedef rest',
779 level => $self->{must_level});
780 # reconsume
781 $state = 'ignore';
782 $nest_level = 0;
783 $next_state = 'before definitions';
784 }
785 } elsif ($state eq 'before const identifier') {
786 if ($token->{type} eq 'identifier') {
787 my $identifier = $token->{value};
788 $identifier =~ s/^_//;
789 push @current, Whatpm::WebIDL::Const->new ($identifier);
790 $current[-1]->set_user_data (manakai_source_line => $line);
791 $current[-1]->set_user_data (manakai_source_column => $column);
792 $current[-1]->type ($current_type);
793 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
794 $token = $get_next_token->();
795 if ($token->{type} eq '=') {
796 $token = $get_next_token->();
797 $state = 'before const expr';
798 next;
799 } else {
800 $onerror->(type => 'const eq', level => $self->{must_level});
801 #
802 }
803 } else {
804 $onerror->(type => 'const identifier', level => $self->{must_level});
805 #
806 }
807 # reconsume
808 $state = 'ignore';
809 $nest_level = 0;
810 #$next_state = $next_state;
811 } elsif ($state eq 'before const expr') {
812 if ($token->{type} eq 'TRUE' or $token->{type} eq 'FALSE') {
813 $current[-1]->value ([$token->{type}]);
814 #
815 } elsif ($token->{type} eq 'integer') {
816 if ($token->{value} =~ /^0[Xx]/) {
817 $current[-1]->value ([$token->{type}, hex substr $token->{value},2]);
818 } elsif ($token->{value} =~ /^-0[Xx]/) {
819 $current[-1]->value ([$token->{type},-hex substr $token->{value},3]);
820 } elsif ($token->{value} =~ /^0/) {
821 $current[-1]->value ([$token->{type}, oct $token->{value}]);
822 } elsif ($token->{value} =~ /^-0/) {
823 $current[-1]->value ([$token->{type},-oct substr $token->{value},1]);
824 } else {
825 $current[-1]->value ([$token->{type}, 0+$token->{value}]);
826 }
827 #
828 } elsif ($token->{type} eq 'float') {
829 $current[-1]->value ([$token->{type}, 0+$token->{value}]);
830 #
831 } else {
832 # reconsume
833 $state = 'ignore';
834 $nest_level = 0;
835 #$next_state = $next_state;
836 next;
837 }
838
839 $token = $get_next_token->();
840 $state = 'before semicolon';
841 #$next_state = $next_state;
842 } elsif ($state eq 'after attribute') {
843 if ({
844 any => 1, boolean => 1, octet => 1, float => 1,
845 DOMString => 1, Object => 1, short => 1, long => 1, unsigned => 1,
846 '::' => 1, identifier => 1,
847 }->{$token->{type}}) {
848 $current_type = $get_type->();
849 if (defined $current_type) {
850 # next token
851 $state = 'before attribute identifier';
852 } else {
853 # reconsume
854 $state = 'ignore';
855 $nest_level = 0;
856 $next_state = 'before interface member';
857 }
858 } else {
859 $onerror->(type => 'before type', level => $self->{must_level});
860 # reconsume
861 $state = 'ignore';
862 $nest_level = 0;
863 $next_state = 'before interface member';
864 }
865 } elsif ($state eq 'before exception member type') {
866 if ({
867 any => 1, boolean => 1, octet => 1, float => 1,
868 DOMString => 1, Object => 1, short => 1, long => 1, unsigned => 1,
869 '::' => 1, identifier => 1,
870 }->{$token->{type}}) {
871 $current_type = $get_type->();
872 if (defined $current_type) {
873 # next token
874 $state = 'before exception member identifier';
875 } else {
876 # reconsume
877 $state = 'ignore';
878 $nest_level = 0;
879 $next_state = 'before exception member';
880 }
881 } else {
882 $onerror->(type => 'before type', level => $self->{must_level});
883 # reconsume
884 $state = 'ignore';
885 $nest_level = 0;
886 $next_state = 'before exception member';
887 }
888 } elsif ($state eq 'before operation type') {
889 if ({
890 any => 1, boolean => 1, octet => 1, float => 1,
891 DOMString => 1, Object => 1, short => 1, long => 1, unsigned => 1,
892 '::' => 1, identifier => 1,
893 void => 1,
894 }->{$token->{type}}) {
895 $current_type = $get_type->();
896 if (defined $current_type) {
897 # next token
898 $state = 'before operation identifier';
899 } else {
900 # reconsume
901 $state = 'ignore';
902 $nest_level = 0;
903 $next_state = 'before interface member';
904 }
905 } else {
906 $onerror->(type => 'before type', level => $self->{must_level});
907 # reconsume
908 $state = 'ignore';
909 $nest_level = 0;
910 $next_state = 'before interface member';
911 }
912 } elsif ($state eq 'before argument type') {
913 if ({
914 any => 1, boolean => 1, octet => 1, float => 1,
915 DOMString => 1, Object => 1, short => 1, long => 1, unsigned => 1,
916 '::' => 1, identifier => 1,
917 }->{$token->{type}}) {
918 $current_type = $get_type->();
919 if (defined $current_type) {
920 # next token
921 $state = 'before argument identifier';
922 } else {
923 # reconsume
924 $state = 'ignore';
925 $nest_level = 0;
926 }
927 } else {
928 $onerror->(type => 'before type', level => $self->{must_level});
929 # reconsume
930 $state = 'ignore';
931 $nest_level = 0;
932 }
933 } elsif ($state eq 'before attribute identifier') {
934 if ($token->{type} eq 'identifier') {
935 my $identifier = $token->{value};
936 $identifier =~ s/^_//;
937 push @current, Whatpm::WebIDL::Attribute->new ($identifier);
938 $current[-1]->set_user_data (manakai_source_line => $line);
939 $current[-1]->set_user_data (manakai_source_column => $column);
940 $current[-1]->readonly ($read_only);
941 $current[-1]->type ($current_type);
942 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
943 $token = $get_next_token->();
944 if ($token->{type} eq 'getraises') {
945 $token = $get_next_token->();
946 $state = 'after raises';
947 $next_state = '*getraises';
948 next;
949 } elsif ($token->{type} eq 'setraises') {
950 $token = $get_next_token->();
951 $state = 'after raises';
952 $next_state = '*setraises';
953 next;
954 } else {
955 # reconsume
956 $state = 'before semicolon';
957 $next_state = 'before interface member';
958 next;
959 }
960 } else {
961 $onerror->(type => 'attribute identifier',
962 level => $self->{must_level});
963 #
964 }
965 # reconsume
966 $state = 'ignore';
967 $nest_level = 0;
968 $next_state = 'before interface member';
969 } elsif ($state eq 'before exception member identifier') {
970 if ($token->{type} eq 'identifier') {
971 my $identifier = $token->{value};
972 $identifier =~ s/^_//;
973 push @current, Whatpm::WebIDL::ExceptionMember->new ($identifier);
974 $current[-1]->set_user_data (manakai_source_line => $line);
975 $current[-1]->set_user_data (manakai_source_column => $column);
976 $current[-1]->type ($current_type);
977 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
978 $token = $get_next_token->();
979 $state = 'before semicolon';
980 $next_state = 'before exception member';
981 } else {
982 $onerror->(type => 'exception member identifier',
983 level => $self->{must_level});
984 # reconsume
985 $state = 'ignore';
986 $nest_level = 0;
987 }
988 } elsif ($state eq 'before operation identifier') {
989 if ($token->{type} eq 'identifier') {
990 ## TODO: unescape
991 push @current, Whatpm::WebIDL::Operation->new ($token->{value});
992 $current[-1]->set_user_data (manakai_source_line => $line);
993 $current[-1]->set_user_data (manakai_source_column => $column);
994 $current[-1]->type ($current_type);
995 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
996 $token = $get_next_token->();
997 if ($token->{type} eq '(') {
998 $token = $get_next_token->();
999 if ($token->{type} eq ')') {
1000 $token = $get_next_token->();
1001 $state = 'before raises';
1002 $next_state = '*raises';
1003 next;
1004 } else {
1005 # reconsume
1006 $state = 'before argument';
1007 next;
1008 }
1009 } else {
1010 $onerror->(type => 'arguments lparen',
1011 level => $self->{must_level});
1012 #
1013 }
1014 } else {
1015 $onerror->(type => 'operation identifier',
1016 level => $self->{must_level});
1017 #
1018 }
1019 # reconsume
1020 $state = 'ignore';
1021 $nest_level = 0;
1022 $next_state = 'before interface member';
1023 } elsif ($state eq 'before argument identifier') {
1024 if ($token->{type} eq 'identifier') {
1025 my $id = $token->{value};
1026 $id =~ s/^_//;
1027 my $arg = Whatpm::WebIDL::Argument->new ($id);
1028 $arg->set_user_data (manakai_source_line => $line);
1029 $arg->set_user_data (manakai_source_column => $column);
1030 $arg->type ($current_type);
1031 $arg->set_extended_attribute_node ($_) for @$xattrs;
1032 $current[-1]->append_child ($arg);
1033 $token = $get_next_token->();
1034 if ($token->{type} eq ')') {
1035 $token = $get_next_token->();
1036 if ($current[-1]->isa ('Whatpm::WebIDL::Operation')) {
1037 $state = 'before raises';
1038 } else {
1039 $state = 'after xattrarg';
1040 }
1041 next;
1042 } elsif ($token->{type} eq ',') {
1043 $token = $get_next_token->();
1044 $state = 'before argument';
1045 next;
1046 } else {
1047 $onerror->(type => 'after argument',
1048 level => $self->{must_level});
1049 #
1050 }
1051 } else {
1052 $onerror->(type => 'argument identifier',
1053 level => $self->{must_level});
1054 #
1055 }
1056 # reconsume
1057 $state = 'ignore';
1058 $nest_level = 0;
1059 } elsif ($state eq 'before argument') {
1060 $xattrs = [];
1061 if ($token->{type} eq '[') {
1062 $token = $get_next_token->();
1063 $state = 'before xattr';
1064 $next_state = 'before argument in';
1065 } else {
1066 # reconsume
1067 $state = 'before argument in';
1068 }
1069 } elsif ($state eq 'before argument in') {
1070 if ($token->{type} eq 'in') {
1071 $token = $get_next_token->();
1072 $state = 'before argument type';
1073 } else {
1074 $onerror->(type => 'argument in',
1075 level => $self->{must_level});
1076 $state = 'ignore';
1077 $nest_level = 0;
1078 }
1079 } elsif ($state eq 'before raises') {
1080 if ($token->{type} eq 'raises') {
1081 $token = $get_next_token->();
1082 $state = 'after raises';
1083 $next_state = '*raises';
1084 } else {
1085 # reconsume
1086 $state = 'before semicolon';
1087 $next_state = 'before interface member';
1088 }
1089 } elsif ($state eq 'after raises') {
1090 if ($token->{type} eq '(') {
1091 $token = $get_next_token->();
1092 $state = 'before exception name';
1093 } else {
1094 $onerror->(type => 'raises lparen',
1095 level => $self->{must_level});
1096 $state = 'ignore';
1097 $nest_level = 0;
1098 }
1099 } elsif ($state eq 'before semicolon') {
1100 if ($token->{type} eq ';') {
1101 $current[-2]->append_child ($current[-1]);
1102 pop @current;
1103 $token = $get_next_token->();
1104 if ($current[-1]->isa ('Whatpm::WebIDL::Definitions') or
1105 $current[-1]->isa ('Whatpm::WebIDL::Module')) {
1106 $state = 'before definitions';
1107 } elsif ($current[-1]->isa ('Whatpm::WebIDL::Interface')) {
1108 $state = 'before members';
1109 $next_state = 'before interface member';
1110 } else {
1111 $state = 'before members';
1112 $next_state = 'before exception member';
1113 }
1114 } else {
1115 $onerror->(type => 'before semicolon', level => 'm',
1116 token => $token);
1117 # reconsume
1118 $state = 'ignore';
1119 $nest_level = 0;
1120 }
1121 } elsif ($state eq 'before block semicolon') {
1122 if ($token->{type} eq ';') {
1123 $current[-2]->append_child ($current[-1]);
1124 pop @current;
1125 $token = $get_next_token->();
1126 if ($current[-1]->isa ('Whatpm::WebIDL::Definitions') or
1127 $current[-1]->isa ('Whatpm::WebIDL::Module')) {
1128 $state = 'before definitions';
1129 } elsif ($current[-1]->isa ('Whatpm::WebIDL::Interface')) {
1130 $state = 'before members';
1131 $next_state = 'before interface member';
1132 } else {
1133 $state = 'before members';
1134 $next_state = 'before exception member';
1135 }
1136 } else {
1137 $onerror->(type => 'before semicolon', level => 'm',
1138 token => $token);
1139 pop @current; # avoid appended by 'ignore'
1140 # reconsume
1141 $state = 'ignore';
1142 $nest_level = 0;
1143 }
1144 } elsif ($state eq 'ignore') {
1145 if ($nest_level == 0 and $token->{type} eq ';') {
1146 while (@current > 1) {
1147 if ($current[-1]->isa ('Whatpm::WebIDL::Interface') or
1148 $current[-1]->isa ('Whatpm::WebIDL::Exception') or
1149 $current[-1]->isa ('Whatpm::WebIDL::Module')) {
1150 last;
1151 }
1152 pop @current;
1153 }
1154
1155 $token = $get_next_token->();
1156 if ($current[-1]->isa ('Whatpm::WebIDL::Definitions') or
1157 $current[-1]->isa ('Whatpm::WebIDL::Module')) {
1158 $state = 'before definitions';
1159 } elsif ($current[-1]->isa ('Whatpm::WebIDL::Interface')) {
1160 $state = 'before members';
1161 $next_state = 'before interface member';
1162 } else {
1163 $state = 'before members';
1164 $next_state = 'before exception member';
1165 }
1166 } elsif ($token->{type} eq '{') {
1167 $nest_level++;
1168 $token = $get_next_token->();
1169 # stay in the state
1170 } elsif ($nest_level and $token->{type} eq '}') {
1171 $nest_level--;
1172 $token = $get_next_token->();
1173 # stay in the state
1174 } elsif ($token->{type} eq 'eof') {
1175 while (@current > 1) {
1176 if ($current[-1]->isa ('Whatpm::WebIDL::Interface') or
1177 $current[-1]->isa ('Whatpm::WebIDL::Exception') or
1178 $current[-1]->isa ('Whatpm::WebIDL::Module')) {
1179 last;
1180 }
1181 pop @current;
1182 }
1183
1184 last;
1185 } else {
1186 # ignore the token
1187 $token = $get_next_token->();
1188 # stay in the state
1189 }
1190 } else {
1191 die "parse_char_string: unkown state: $state";
1192 }
1193 }
1194
1195 if (@current > 1) {
1196 $onerror->(type => 'premature end of file', level => $self->{must_level});
1197 while (@current > 1) {
1198 $current[-2]->append_child ($current[-1]);
1199 pop @current;
1200 }
1201 }
1202
1203 $get_type = undef; # unlink loop
1204
1205 return $defs;
1206 } # parse_char_string
1207
1208 package Whatpm::WebIDL::Node;
1209
1210 require Scalar::Util;
1211
1212 sub new ($) {
1213 return bless {child_nodes => []}, $_[0];
1214 } # new
1215
1216 sub append_child ($$) {
1217 my $self = shift;
1218 my $child = shift;
1219
1220 ## TODO: child type
1221 ## TODO: parent check
1222
1223 push @{$self->{child_nodes}}, $child;
1224
1225 $child->{parent_node} = $self;
1226 Scalar::Util::weaken ($child->{parent_node});
1227
1228 return $child;
1229 } # append_child
1230
1231 sub child_nodes ($) {
1232 return [@{$_[0]->{child_nodes}}]; # dead list
1233 } # child_nodes
1234
1235 sub idl_text ($) {
1236 return '[[ERROR: ' . (ref $_[0]) . '->idl_text]]';
1237 } # idl_text
1238
1239 sub node_name ($) {
1240 return $_[0]->{node_name}; # may be undef
1241 } # node_name
1242
1243 sub parent_node ($) {
1244 return $_[0]->{parent_node};
1245 } # parent_node
1246
1247 sub get_user_data ($$) {
1248 return $_[0]->{user_data}->{$_[1]};
1249 } # get_user_data
1250
1251 sub set_user_data ($$$) {
1252 if (defined $_[2]) {
1253 $_[0]->{user_data}->{$_[1]} = ''.$_[2];
1254 } else {
1255 delete $_[0]->{user_data}->{$_[1]};
1256 }
1257 } # set_user_data
1258
1259 package Whatpm::WebIDL::Definitions;
1260 push our @ISA, 'Whatpm::WebIDL::Node';
1261
1262 sub idl_text ($) {
1263 return join "\x0A", map {$_->idl_text} @{$_[0]->{child_nodes}};
1264 } # idl_text
1265
1266 my $xattr_defs = {
1267 Constructor => {
1268 #allow_id => 0,
1269 allow_arglist => 1,
1270 allowed_type => {Interface => 1},
1271 check => sub {
1272 my ($self, $onerror, $levels, $items, $item, $xattr, $xattr_name) = @_;
1273
1274 ## NOTE: Arguments
1275 unshift @$items,
1276 map { {node => $_, scope => $item->{scope}} }
1277 @{$xattr->{child_nodes}};
1278 },
1279 },
1280 ExceptionConsts => {
1281 allow_id => 1,
1282 #allow_arglist => 0,
1283 allowed_type => {Module => 1},
1284 check => sub {
1285 my ($self, $onerror, $levels, $items, $item, $xattr, $xattr_name) = @_;
1286
1287 my $id = $xattr->value;
1288 if (defined $id) {
1289 my $has_x;
1290 for my $x (@{$item->{node}->child_nodes}) {
1291 next unless $x->isa ('Whatpm::WebIDL::Exception');
1292 if ($x->node_name eq $id) {
1293 $has_x = 1;
1294 last;
1295 }
1296 }
1297 unless ($has_x) {
1298 $onerror->(type => 'exception not defined',
1299 value => $id,
1300 node => $xattr,
1301 level => $levels->{must});
1302 }
1303 } else {
1304 $onerror->(type => 'xattr id missing',
1305 text => $xattr_name,
1306 node => $xattr,
1307 level => $levels->{must});
1308 }
1309 },
1310 },
1311 IndexGetter => {
1312 #allow_id => 0,
1313 #allow_arglist => 0,
1314 allowed_type => {Operation => 1},
1315 check => sub {
1316 my ($self, $onerror, $levels, $items, $item, $xattr, $xattr_name) = @_;
1317
1318 if (@{$item->{node}->{child_nodes}} != 1 or
1319 $item->{node}->{child_nodes}->[0]->type_text ne 'unsigned long') {
1320 $onerror->(type => 'wrong signature accessor',
1321 text => $xattr_name,
1322 node => $xattr,
1323 level => $levels->{fact});
1324 }
1325 if ($item->{defined_accessors}->{$xattr_name}) {
1326 $onerror->(type => 'duplicate accessor',
1327 text => $xattr_name,
1328 node => $xattr,
1329 level => $levels->{undefined});
1330 }
1331 $item->{defined_accessors}->{$xattr_name} = 1;
1332 },
1333 },
1334 IndexSetter => {
1335 #allow_id => 0,
1336 #allow_arglist => 0,
1337 allowed_type => {Operation => 1},
1338 check => sub {
1339 my ($self, $onerror, $levels, $items, $item, $xattr, $xattr_name) = @_;
1340
1341 if (@{$item->{node}->{child_nodes}} != 2 or
1342 $item->{node}->{child_nodes}->[0]->type_text ne 'unsigned long') {
1343 $onerror->(type => 'wrong signature accessor',
1344 text => $xattr_name,
1345 node => $xattr,
1346 level => $levels->{fact});
1347 }
1348 if ($item->{defined_accessors}->{$xattr_name}) {
1349 $onerror->(type => 'duplicate accessor',
1350 text => $xattr_name,
1351 node => $xattr,
1352 level => $levels->{undefined});
1353 }
1354 $item->{defined_accessors}->{$xattr_name} = 1;
1355 },
1356 },
1357 NameGetter => {
1358 #allow_id => 0,
1359 #allow_arglist => 0,
1360 allowed_type => {Operation => 1},
1361 check => sub {
1362 my ($self, $onerror, $levels, $items, $item, $xattr, $xattr_name) = @_;
1363
1364 if (@{$item->{node}->{child_nodes}} != 1 or
1365 $item->{node}->{child_nodes}->[0]->type_text ne 'DOMString') {
1366 $onerror->(type => 'wrong signature accessor',
1367 text => $xattr_name,
1368 node => $xattr,
1369 level => $levels->{fact});
1370 }
1371 if ($item->{defined_accessors}->{$xattr_name}) {
1372 $onerror->(type => 'duplicate accessor',
1373 text => $xattr_name,
1374 node => $xattr,
1375 level => $levels->{undefined});
1376 }
1377 $item->{defined_accessors}->{$xattr_name} = 1;
1378 },
1379 },
1380 NameSetter => {
1381 #allow_id => 0,
1382 #allow_arglist => 0,
1383 allowed_type => {Operation => 1},
1384 check => sub {
1385 my ($self, $onerror, $levels, $items, $item, $xattr, $xattr_name) = @_;
1386
1387 if (@{$item->{node}->{child_nodes}} != 2 or
1388 $item->{node}->{child_nodes}->[0]->type ne '::DOMString::') {
1389 $onerror->(type => 'wrong signature accessor',
1390 text => $xattr_name,
1391 node => $xattr,
1392 level => $levels->{fact});
1393 }
1394 if ($item->{defined_accessors}->{$xattr_name}) {
1395 $onerror->(type => 'duplicate accessor',
1396 text => $xattr_name,
1397 node => $xattr,
1398 level => $levels->{undefined});
1399 }
1400 $item->{defined_accessors}->{$xattr_name} = 1;
1401 },
1402 },
1403 Null => {
1404 allow_id => 1,
1405 #allow_arglist => 0,
1406 allowed_type => {Argument => 1, Attribute => 1},
1407 ## ISSUE: Is this allwoed for extended attribute's arguments?
1408 check => sub {
1409 my ($self, $onerror, $levels, $items, $item, $xattr, $xattr_name) = @_;
1410
1411 ## ISSUE: [Null=Empty,Null=Null]
1412
1413 if ($item->{node}->type ne '::DOMString::') {
1414 $onerror->(type => 'xattr for wrong type',
1415 text => $xattr_name,
1416 node => $xattr,
1417 level => $levels->{fact});
1418 }
1419
1420 my $id = $xattr->value;
1421 if (defined $id) {
1422 if ($id eq 'Null' or $id eq 'Empty') {
1423 #
1424 } else {
1425 $onerror->(type => 'xattr id value not allowed',
1426 text => $xattr_name,
1427 value => $id,
1428 node => $xattr,
1429 level => $levels->{must});
1430 }
1431 } else {
1432 $onerror->(type => 'xattr id missing',
1433 text => $xattr_name,
1434 node => $xattr,
1435 level => $levels->{must});
1436 }
1437 },
1438 },
1439 }; # $xattr_defs
1440
1441 sub check ($$;$) {
1442 my ($self, $onerror, $levels) = @_;
1443
1444 $levels ||= $default_levels;
1445
1446 my $check_const_value = sub ($) {
1447 my $item = shift;
1448
1449 my $type = $item->{node}->type;
1450 my $value = $item->{node}->value;
1451
1452 ## NOTE: Although it is not explicitly spelled out in the spec,
1453 ## it can be assumed, imho, that "any" type accepts all of these
1454 ## constant values.
1455 ## ISSUE: Should I ask the editor to clarify the spec about this?
1456
1457 if ($value->[0] eq 'TRUE' or $value->[0] eq 'FALSE') {
1458 if ($type eq '::boolean::') {
1459 #
1460 } elsif ($type eq '::any::') {
1461 #
1462 } else {
1463 $onerror->(type => 'const type mismatch',
1464 level => 'm',
1465 node => $item->{node},
1466 text => $item->{node}->type_text,
1467 value => $value->[0]);
1468 }
1469 } elsif ($value->[0] eq 'integer') {
1470 if ($type eq '::octet::') {
1471 if ($value->[1] < 0 or 255 < $value->[1]) {
1472 $onerror->(type => 'const value out of range',
1473 level => 'm',
1474 node => $item->{node},
1475 value => $value->[1]);
1476 }
1477 } elsif ($type eq '::short::') {
1478 if ($value->[1] < -32768 or 32767 < $value->[1]) {
1479 $onerror->(type => 'const value out of range',
1480 level => 'm',
1481 node => $item->{node},
1482 value => $value->[1]);
1483 }
1484 } elsif ($type eq '::unsigned short::') {
1485 if ($value->[1] < 0 or 65535 < $value->[1]) {
1486 $onerror->(type => 'const value out of range',
1487 level => 'm',
1488 node => $item->{node},
1489 value => $value->[1]);
1490 }
1491 } elsif ($type eq '::long::') {
1492 if ($value->[1] < -2147483648 or 2147483647 < $value->[1]) {
1493 $onerror->(type => 'const value out of range',
1494 level => 'm',
1495 node => $item->{node},
1496 value => $value->[1]);
1497 }
1498 } elsif ($type eq '::unsigned long::') {
1499 if ($value->[1] < 0 or 4294967295 < $value->[1]) {
1500 $onerror->(type => 'const value out of range',
1501 level => 'm',
1502 node => $item->{node},
1503 value => $value->[1]);
1504 }
1505 } elsif ($type eq '::long long::') {
1506 if ($value->[1] < -9223372036854775808 or
1507 9223372036854775807 < $value->[1]) {
1508 $onerror->(type => 'const value out of range',
1509 level => 'm',
1510 node => $item->{node},
1511 value => $value->[1]);
1512 }
1513 } elsif ($type eq '::unsigned long long::') {
1514 if ($value->[1] < 0 or 18446744073709551615 < $value->[1]) {
1515 $onerror->(type => 'const value out of range',
1516 level => 'm',
1517 node => $item->{node},
1518 value => $value->[1]);
1519 }
1520 } elsif ($type eq '::any::') {
1521 if ($value->[1] < -9223372036854775808 or
1522 18446744073709551615 < $value->[1]) {
1523 $onerror->(type => 'const value out of range',
1524 level => 'm',
1525 node => $item->{node},
1526 value => $value->[1]);
1527 }
1528 } else {
1529 $onerror->(type => 'const type mismatch',
1530 level => 'm',
1531 node => $item->{node},
1532 text => $item->{node}->type_text,
1533 value => $value->[1]);
1534 }
1535 } elsif ($value->[0] eq 'float') {
1536 if ($type eq '::float::' or $type eq '::any::') {
1537 #
1538 } else {
1539 $onerror->(type => 'const type mismatch',
1540 level => 'm',
1541 node => $item->{node},
1542 text => $item->{node}->type_text,
1543 value => $value->[1]);
1544 }
1545 }
1546 ## NOTE: Otherwise, an error of the implementation or the application.
1547 }; # $check_const_value
1548
1549 my $defined_qnames = {};
1550 my $resolve = sub ($$) {
1551 my $i_sn = shift;
1552 my $scope = shift;
1553
1554 if ($i_sn =~ /\A::([^:]+)::\z/ or
1555 $i_sn =~ /^::::sequence::::/) {
1556 return undef;
1557 } elsif ($i_sn =~ /::DOMString\z/ or
1558 $i_sn =~ /::DOMString::::\z/) {
1559 return undef;
1560 } elsif ($i_sn =~ /^::/) {
1561 if ($defined_qnames->{$i_sn}) {
1562 return $defined_qnames->{$i_sn};
1563 } else {
1564 return undef;
1565 }
1566 } else {
1567 if ($defined_qnames->{$scope . $i_sn}) {
1568 return $defined_qnames->{$scope . $i_sn};
1569 } elsif ($defined_qnames->{'::' . $i_sn}) {
1570 return $defined_qnames->{'::' . $i_sn};
1571 } else {
1572 return undef;
1573 }
1574 }
1575 }; # $resolve
1576
1577 my $items = [map { {node => $_, scope => '::'} } @{$self->{child_nodes}}];
1578
1579 while (@$items) {
1580 my $item = shift @$items;
1581 if ($item->{node}->isa ('Whatpm::WebIDL::Definition') and
1582 not $item->{defined_members} # Const in Interface does not have that
1583 ) {
1584 if ($item->{node}->isa ('Whatpm::WebIDL::Module')) {
1585 unshift @$items,
1586 map {
1587 {node => $_, parent => $item->{node},
1588 scope => $item->{scope} . $item->{node}->node_name . '::'}
1589 }
1590 @{$item->{node}->{child_nodes}};
1591 } else {
1592 unless ($item->{parent}) {
1593 $onerror->(type => 'non-module definition',
1594 level => 's',
1595 node => $item->{node});
1596 }
1597 }
1598
1599 if ($item->{node}->isa ('Whatpm::WebIDL::Interface')) {
1600 for my $i_sn (@{$item->{node}->{inheritances}}) {
1601 my $def = $resolve->($i_sn, $item->{scope});
1602
1603 unless ($def and $def->{node}->isa ('Whatpm::WebIDL::Interface')) {
1604 $i_sn =~ s/::DOMString::::\z/::DOMString/;
1605 $onerror->(type => 'interface not defined',
1606 level => 'm',
1607 node => $item->{node},
1608 text => $i_sn);
1609 }
1610 }
1611
1612 my $defined_members = {};
1613 my $defined_accessors = {};
1614 unshift @$items,
1615 map { {node => $_, defined_members => $defined_members,
1616 defined_accessors => $defined_accessors,
1617 scope => $item->{scope}} }
1618 @{$item->{node}->{child_nodes}};
1619 } elsif ($item->{node}->isa ('Whatpm::WebIDL::Exception')) {
1620 my $defined_members = {};
1621 unshift @$items,
1622 map { {node => $_, defined_members => $defined_members,
1623 scope => $item->{scope}} }
1624 @{$item->{node}->{child_nodes}};
1625 } elsif ($item->{node}->isa ('Whatpm::WebIDL::Const')) {
1626 $check_const_value->($item);
1627 } elsif ($item->{node}->isa ('Whatpm::WebIDL::Typedef')) {
1628 my $name = $item->{node}->node_name;
1629 my $type = $item->{node}->type;
1630 if ($name eq '::DOMString::' or
1631 $type eq '::DOMString::' or
1632 $type =~ /::DOMString::::\z/) {
1633 $onerror->(type => 'typedef ignored',
1634 level => 'i',
1635 node => $item->{node});
1636 }
1637 ## ISSUE: Refernece to a non-defined interface is not non-conforming.
1638
1639 ## ISSUE: What does "ignored" mean?
1640 ## "typedef dom::DOMString a; typedef long a;" is conforming?
1641 ## "typedef dom::DOMString b; interface c { attribute b d; };" is?
1642
1643 ## ISSUE: Is "sequence<undefinedtype>" conforming?
1644
1645 if ($type =~ /\A::([^:]+)::\z/) {
1646 $item->{allow_null} = {
1647 boolean => 1, octet => 1, short => 1, 'unsigned short' => 1,
1648 long => 1, 'unsigned long' => 1, 'long long' => 1,
1649 'unsigned long long' => 1, float => 1,
1650 }->{$1};
1651 } elsif ($type =~ /^::::sequence::::/) {
1652 $item->{allow_null} = 1;
1653 } else {
1654 my $def = $resolve->($type, $item->{scope});
1655 $item->{allow_null} = $def->{allow_null};
1656 }
1657 } elsif ($item->{node}->isa ('Whatpm::WebIDL::Valuetype')) {
1658 my $name = $item->{node}->node_name;
1659 if ($name eq '::DOMString::') {
1660 $onerror->(type => 'ignored valuetype',
1661 level => 'i',
1662 node => $item->{node});
1663 } else {
1664 my $type = $item->{node}->type;
1665 if ($type =~ /\A::[^:]+::\z/) {
1666 #
1667 } elsif ($type =~ /^::::sequence::::/) {
1668 ## ISSUE: Is "sequence<unknowntype>" conforming?
1669 } else {
1670 my $def = $resolve->($type, $item->{scope});
1671 if ($def and
1672 $def->{allow_null}) {
1673 #
1674 } else {
1675 ## ISSUE: It is not explicitly specified that ScopedName
1676 ## must refer a defined type.
1677 $onerror->(type => 'not boxable type',
1678 level => 'm',
1679 node => $item->{node},
1680 text => $item->{node}->type_text);
1681 }
1682 }
1683 }
1684 }
1685
1686 my $qname = $item->{node}->qualified_name;
1687 if ($defined_qnames->{$qname}) {
1688 ## NOTE: "The identifier of a definition MUST be locally unique":
1689 ## Redundant with another requirement below.
1690
1691 ## ISSUE: |interface x; interface x {};| is non-conforming
1692 ## according to the current spec text.
1693
1694 ## ISSUE: |interface x;| with no |interface x {};| is conforming
1695 ## according to the current spec text.
1696
1697 $onerror->(type => 'duplicate qname',
1698 level => 'm',
1699 node => $item->{node});
1700 } else {
1701 $defined_qnames->{$qname} = $item;
1702 ## NOTE: This flag must be turned on AFTER inheritance check is
1703 ## performed (c.f. |interface x : x {};|).
1704 }
1705 } elsif ($item->{node}->isa ('Whatpm::WebIDL::InterfaceMember')) {
1706 if ($item->{node}->isa ('Whatpm::WebIDL::Operation')) {
1707 ## NOTE: Arguments
1708 unshift @$items,
1709 map { {node => $_, scope => $item->{scope}} }
1710 @{$item->{node}->{child_nodes}};
1711 } else {
1712 my $name = $item->{node}->node_name;
1713 if ($item->{defined_members}->{$name}) {
1714 $onerror->(type => 'duplicate member',
1715 level => 'm',
1716 node => $item->{node});
1717 ## ISSUE: Whether the example below is conforming or not
1718 ## is ambigious:
1719 ## |interface a { attribute any b; any b (); };|
1720 } else {
1721 $item->{defined_members}->{$name} = 1;
1722 }
1723 }
1724
1725 if ($item->{node}->isa ('Whatpm::WebIDL::Attribute') or
1726 $item->{node}->isa ('Whatpm::WebIDL::Operation')) {
1727 my $type = $item->{node}->type;
1728 if ($type =~ /\A::[^:]+::\z/) { # note that sequence<> not allowed
1729 #
1730 } else { # scoped name
1731 my $def = $resolve->($type, $item->{scope});
1732
1733 unless ($def and
1734 ($def->{node}->isa ('Whatpm::WebIDL::Interface') or
1735 $def->{node}->isa ('Whatpm::WebIDL::Typedef') or
1736 $def->{node}->isa ('Whatpm::WebIDL::Valuetype'))) {
1737 $onerror->(type => 'type not defined',
1738 level => 'm',
1739 node => $item->{node},
1740 text => $item->{node}->type_text);
1741 }
1742 }
1743
1744 for (@{$item->{node}->{raises} or []}, # for operations
1745 @{$item->{node}->{getraises} or []}, # for attributes
1746 @{$item->{node}->{setraises} or []}) { # for attributes
1747 my $def = $resolve->($_, $item->{scope});
1748
1749 unless ($def and
1750 $def->{node}->isa ('Whatpm::WebIDL::Exception')) {
1751 $onerror->(type => 'exception not defined',
1752 level => 'm',
1753 node => $item->{node},
1754 text => $_);
1755 }
1756 }
1757
1758 ## ISSUE: readonly setraises is not disallowed
1759 ## ISSUE: raises (x,x) and raises (x,::x) and etc. are not disallowed
1760 } elsif ($item->{node}->isa ('Whatpm::WebIDL::Const')) {
1761 $check_const_value->($item);
1762 }
1763 } elsif ($item->{node}->isa ('Whatpm::WebIDL::Argument') or
1764 $item->{node}->isa ('Whatpm::WebIDL::ExceptionMember')) {
1765 ## ISSUE: No uniqueness constraints for arguments in an operation,
1766 ## so we don't check it for arguments.
1767
1768 ## ISSUE: For extended attributes, semantics of the argument
1769 ## (e.g. the identifier is given by the |identifier| in the |Argument|)
1770 ## is not explicitly defined. In addition, no uniqueness constraint
1771 ## is defined, so we don't check it for arguments.
1772
1773 my $name = $item->{node}->node_name;
1774 if ($item->{defined_members}->{$name}) {
1775 $onerror->(type => 'duplicate member',
1776 level => 'm',
1777 node => $item->{node});
1778 } else {
1779 $item->{defined_members}->{$name} = 1;
1780 }
1781
1782 my $type = $item->{node}->type;
1783 if ($type =~ /\A::[^:]+::\z/) { # note that sequence<> not allowed
1784 #
1785 } else { # scoped name
1786 my $def = $resolve->($type, $item->{scope});
1787
1788 unless ($def and
1789 ($def->{node}->isa ('Whatpm::WebIDL::Interface') or
1790 $def->{node}->isa ('Whatpm::WebIDL::Typedef') or
1791 $def->{node}->isa ('Whatpm::WebIDL::Valuetype'))) {
1792 $onerror->(type => 'type not defined',
1793 level => 'm',
1794 node => $item->{node},
1795 text => $item->{node}->type_text);
1796 }
1797 }
1798 }
1799
1800 my $xattrs = $item->{node}->{xattrs} || [];
1801 X: for my $xattr (@$xattrs) {
1802 my $xattr_name = $xattr->node_name;
1803 my $xattr_def = $xattr_defs->{$xattr_name};
1804
1805 unless ($xattr_def) {
1806 $onerror->(type => 'unknown xattr',
1807 text => $xattr_name,
1808 node => $xattr,
1809 level => $levels->{uncertain});
1810 next X;
1811 }
1812
1813 A: {
1814 for my $cls (keys %{$xattr_def->{allowed_type} or {}}) {
1815 if ($item->{node}->isa ('Whatpm::WebIDL::' . $cls)) {
1816 last A;
1817 }
1818 }
1819
1820 $onerror->(type => 'xattr not applicable',
1821 text => $xattr_name,
1822 level => $levels->{fact},
1823 node => $xattr);
1824
1825 next X;
1826 } # A
1827
1828 if (not $xattr_def->{allow_id} and defined $xattr->value) {
1829 $onerror->(type => 'xattr id not allowed',
1830 text => $xattr_name,
1831 node => $xattr,
1832 level => $levels->{must});
1833 }
1834 if (not $xattr_def->{allow_arglist} and $xattr->has_argument_list) {
1835 $onerror->(type => 'xattr arglist not allowed',
1836 text => $xattr_name,
1837 node => $xattr,
1838 level => $levels->{must});
1839 }
1840
1841 $xattr_def->{check}->($self, $onerror, $levels, $items, $item,
1842 $xattr, $xattr_name);
1843
1844
1845 if ({
1846 Undefined => 1,
1847 }->{$xattr_name}) {
1848 if ($item->{node}->isa ('Whatpm::WebIDL::Attribute') or
1849 $item->{node}->isa ('Whatpm::WebIDL::Argument')) {
1850
1851 next;
1852 } else {
1853 #
1854 }
1855
1856 } elsif ($xattr_name eq 'PutForwards') {
1857 if ($item->{node}->isa ('Whatpm::WebIDL::Attribute')) {
1858
1859 next;
1860 } else {
1861 #
1862 }
1863 } elsif ($xattr_name eq 'Variadic') {
1864 if ($item->{node}->isa ('Whatpm::WebIDL::Operation')) {
1865
1866 next;
1867 } else {
1868 #
1869 }
1870 } elsif ({NamedConstructor => 1,
1871 }->{$xattr_name}) {
1872 if ($item->{node}->isa ('Whatpm::WebIDL::Interface')) {
1873
1874 next;
1875 } else {
1876 #
1877 }
1878 } elsif ({
1879 NativeObject => 1,
1880 Stringifies => 1,
1881 }->{$xattr_name}) {
1882 if ($item->{node}->isa ('Whatpm::WebIDL::Interface')) {
1883
1884 next;
1885 } else {
1886 #
1887 }
1888 } elsif ($xattr_name eq 'NoInterfaceObject') {
1889 if ($item->{node}->isa ('Whatpm::WebIDL::Interface') or
1890 $item->{node}->isa ('Whatpm::WebIDL::Exception')) {
1891
1892 next;
1893 } else {
1894 #
1895 }
1896
1897
1898 }
1899
1900 }
1901 }
1902 } # check
1903
1904 package Whatpm::WebIDL::Definition;
1905 push our @ISA, 'Whatpm::WebIDL::Node';
1906
1907 sub new ($$) {
1908 return bless {child_nodes => [], node_name => ''.$_[1]}, $_[0];
1909 } # new
1910
1911 sub set_extended_attribute_node ($$) {
1912 my $self = shift;
1913 ## TODO: check
1914 push @{$self->{xattrs} ||= []}, shift;
1915 } # set_extended_attribute_node
1916
1917 sub _xattrs_text ($) {
1918 my $self = shift;
1919
1920 unless ($self->{xattrs} and
1921 @{$self->{xattrs}}) {
1922 return '';
1923 }
1924
1925 my $r = '[';
1926 $r .= join ', ', map {$_->idl_text} @{$self->{xattrs}};
1927 $r .= ']';
1928 return $r;
1929 } # _xattrs_text
1930
1931 sub qualified_name ($) {
1932 my $self = shift;
1933
1934 my $parent = $self->{parent_node};
1935 if ($parent and $parent->isa ('Whatpm::WebIDL::Definition')) {
1936 return $parent->qualified_name . '::' . $self->{node_name};
1937 } else {
1938 return '::' . $self->{node_name};
1939 }
1940 } # qualified_name
1941
1942 sub type ($;$) {
1943 if (@_ > 1) {
1944 if (defined $_[1]) {
1945 $_[0]->{type} = $_[1];
1946 } else {
1947 $_[0]->{type} = '::any::';
1948 }
1949 }
1950 return $_[0]->{type};
1951 } # type
1952
1953 my $serialize_type;
1954 $serialize_type = sub ($) {
1955 my $type = shift;
1956 if ($type =~ s/^::::sequence:::://) {
1957 return 'sequence<' . $serialize_type->($type) . '>';
1958 } elsif ($type =~ /\A::([^:]+)::\z/) {
1959 return $1;
1960 } else {
1961 $type =~ s/::DOMString::::\z/::DOMString/;
1962 return $type; ## TODO: escape identifiers...
1963 }
1964 }; # $serialize_type
1965
1966 sub type_text ($) {
1967 my $type = $_[0]->{type};
1968 return undef unless defined $type;
1969
1970 return $serialize_type->($type);
1971 } # type_text
1972
1973 package Whatpm::WebIDL::Module;
1974 push our @ISA, 'Whatpm::WebIDL::Definition';
1975
1976 sub idl_text ($) {
1977 my $self = shift;
1978 my $r = $self->_xattrs_text;
1979 $r .= ' ' if length $r;
1980 $r .= 'module ' . $self->node_name . " {\x0A\x0A"; ## TODO: escape
1981 for (@{$self->{child_nodes}}) {
1982 $r .= $_->idl_text;
1983 }
1984 $r .= "\x0A};\x0A";
1985 return $r;
1986 } # idl_text
1987
1988 package Whatpm::WebIDL::Interface;
1989 push our @ISA, 'Whatpm::WebIDL::Definition';
1990
1991 sub new ($$) {
1992 my $self = shift->SUPER::new (@_);
1993 $self->{inheritances} = [];
1994 return $self;
1995 } # new
1996
1997 sub append_inheritance ($$) {
1998 my $self = shift;
1999 my $scoped_name = shift;
2000 push @{$self->{inheritances}}, $scoped_name;
2001 } # append_inheritance
2002
2003 sub idl_text ($) {
2004 my $self = shift;
2005 my $r = $self->_xattrs_text;
2006 $r .= ' ' if length $r;
2007 $r .= 'interface ' . $self->node_name;
2008
2009 if ($self->{is_forward_declaration}) {
2010 $r .= ";\x0A";
2011 return $r;
2012 }
2013
2014 if (@{$self->{inheritances}}) {
2015 $r .= ' : '; ## TODO: ...
2016 $r .= join ', ', map {$serialize_type->($_)} @{$self->{inheritances}};
2017 }
2018 $r .= " {\x0A"; ## TODO: escape
2019 for (@{$self->{child_nodes}}) {
2020 $r .= ' ' . $_->idl_text;
2021 }
2022 $r .= "};\x0A";
2023 return $r;
2024 } # idl_text
2025
2026 sub is_forward_declaration ($;$) {
2027 if (@_ > 1) {
2028 if ($_[1]) {
2029 $_[0]->{is_forward_declaration} = 1;
2030 } else {
2031 delete $_[0]->{is_forward_declaration};
2032 }
2033 }
2034
2035 return $_[0]->{is_forward_declaration};
2036 } # is_forward_declaration
2037
2038 package Whatpm::WebIDL::Exception;
2039 push our @ISA, 'Whatpm::WebIDL::Definition';
2040
2041 sub idl_text ($) {
2042 my $self = shift;
2043 my $r = $self->_xattrs_text;
2044 $r .= ' ' if length $r;
2045 $r .= 'exception ' . $self->node_name . " {\x0A"; ## TODO: escape
2046 for (@{$self->{child_nodes}}) {
2047 $r .= ' ' . $_->idl_text;
2048 }
2049 $r .= "};\x0A";
2050 return $r;
2051 } # idl_text
2052
2053 package Whatpm::WebIDL::Typedef;
2054 push our @ISA, 'Whatpm::WebIDL::Definition';
2055
2056 sub new ($$) {
2057 my $self = shift->SUPER::new (@_);
2058 $self->{type} = '::any::';
2059 return $self;
2060 } # new
2061
2062 sub idl_text ($) {
2063 my $self = shift;
2064 my $r = $self->_xattrs_text;
2065 $r .= ' ' if length $r;
2066 my $node_name = $self->node_name;
2067 $node_name = 'DOMString' if $node_name eq '::DOMString::';
2068 ## TODO: escape
2069 $r .= 'typedef ' . $self->type_text . ' ' . $node_name . ";\x0A";
2070 return $r;
2071 } # idl_text
2072
2073 package Whatpm::WebIDL::Valuetype;
2074 push our @ISA, 'Whatpm::WebIDL::Definition';
2075
2076 sub new ($$) {
2077 my $self = shift->SUPER::new (@_);
2078 $self->{type} = '::boolean::';
2079 return $self;
2080 } # new
2081
2082 sub idl_text ($) {
2083 my $self = shift;
2084 my $r = $self->_xattrs_text;
2085 $r .= ' ' if length $r;
2086 my $name = $self->node_name;
2087 $name = 'DOMString' if $name eq '::DOMString::';
2088 ## TODO: escape
2089 $r .= 'valuetype ' . $name . ' ' . $self->type_text . ";\x0A";
2090 return $r;
2091 } # idl_text
2092
2093 package Whatpm::WebIDL::InterfaceMember;
2094 push our @ISA, 'Whatpm::WebIDL::Node';
2095
2096 sub new ($$) {
2097 return bless {node_name => ''.$_[1]}, $_[0];
2098 } # new
2099
2100 sub child_nodes ($) { return [] }
2101
2102 *_xattrs_text = \&Whatpm::WebIDL::Definition::_xattrs_text;
2103
2104 *set_extended_attribute_node = \&Whatpm::WebIDL::Definition::set_extended_attribute_node;
2105
2106 *type = \&Whatpm::WebIDL::Definition::type;
2107
2108 *type_text = \&Whatpm::WebIDL::Definition::type_text;
2109
2110 package Whatpm::WebIDL::Const;
2111 push our @ISA, 'Whatpm::WebIDL::Definition', 'Whatpm::WebIDL::InterfaceMember';
2112
2113 sub new ($$) {
2114 my $self = shift->SUPER::new (@_); # Definition->new should be called
2115 $self->{type} = '::boolean::';
2116 $self->{value} = ['FALSE'];
2117 return $self;
2118 } # new
2119
2120 sub value ($;$) {
2121 if (@_ > 1) {
2122 if (defined $_[1]) {
2123 $_[0]->{value} = $_[1];
2124 } else {
2125 $_[0]->{value} = ['FALSE'];
2126 }
2127 }
2128
2129 return $_[0]->{value};
2130 } # value
2131
2132 sub value_text ($) {
2133 my $value = $_[0]->{value};
2134
2135 if ($value->[0] eq 'TRUE' or $value->[0] eq 'FALSE') {
2136 return $value->[0];
2137 } elsif ($value->[0] eq 'integer' or $value->[0] eq 'float') {
2138 ## TODO: format
2139 return $value->[1];
2140 } else {
2141 return undef;
2142 }
2143 } # value_text
2144
2145 sub idl_text ($) {
2146 my $self = shift;
2147 my $r = $self->_xattrs_text;
2148 $r .= ' ' if length $r;
2149 $r .= 'const ' . $self->type_text . ' ' . $self->node_name . ' = ' . $self->value_text . ";\x0A"; ## TODO: escape
2150 return $r;
2151 } # idl_text
2152
2153 package Whatpm::WebIDL::Attribute;
2154 push our @ISA, 'Whatpm::WebIDL::InterfaceMember';
2155
2156 sub new ($$) {
2157 my $self = shift->SUPER::new (@_);
2158 $self->{type} = '::any::';
2159 $self->{getraises} = [];
2160 $self->{setraises} = [];
2161 return $self;
2162 } # new
2163
2164 sub append_getraises ($$) {
2165 ## TODO: error check, etc.
2166 push @{$_[0]->{getraises}}, $_[1];
2167 } # append_getraises
2168
2169 sub append_setraises ($$) {
2170 ## TODO: error check, etc.
2171 push @{$_[0]->{setraises}}, $_[1];
2172 } # append_setraises
2173
2174 sub readonly ($;$) {
2175 if (@_ > 1) {
2176 $_[0]->{readonly} = $_[1];
2177 }
2178
2179 return $_[0]->{readonly};
2180 } # readonly
2181
2182 sub idl_text ($) {
2183 my $self = shift;
2184 my $r = $self->_xattrs_text;
2185 $r .= ' ' if length $r;
2186 $r .= ($self->readonly ? 'readonly ' : '') . 'attribute ' . $self->type_text . ' ' . $self->node_name;
2187 ## TODO: escape
2188 if (@{$self->{getraises}}) {
2189 $r .= ' getraises (';
2190 ## todo: ...
2191 $r .= join ', ', map {$serialize_type->($_)} @{$self->{getraises}};
2192 $r .= ')';
2193 }
2194 if (@{$self->{setraises}}) {
2195 $r .= ' setraises (';
2196 ## todo: ...
2197 $r .= join ', ', map {$serialize_type->($_)} @{$self->{setraises}};
2198 $r .= ')';
2199 }
2200 $r .= ";\x0A";
2201 return $r;
2202 } # idl_text
2203
2204 package Whatpm::WebIDL::Operation;
2205 push our @ISA, 'Whatpm::WebIDL::InterfaceMember';
2206
2207 sub new ($$) {
2208 my $self = shift->SUPER::new (@_);
2209 $self->{type} = '::any::';
2210 $self->{child_nodes} = [];
2211 $self->{raises} = [];
2212 return $self;
2213 } # new
2214
2215 sub append_raises ($$) {
2216 ## TODO: error check, etc.
2217 push @{$_[0]->{raises}}, $_[1];
2218 } # append_raises
2219
2220 sub idl_text ($) {
2221 my $self = shift;
2222 my $r = $self->_xattrs_text;
2223 $r .= ' ' if length $r;
2224 $r .= $self->type_text . ' ' . $self->node_name . ' ('; ## TODO: escape
2225 $r .= join ', ', map {$_->idl_text} @{$self->{child_nodes}};
2226 $r .= ')';
2227 if (@{$self->{raises}}) {
2228 $r .= ' raises (';
2229 ## todo: ...
2230 $r .= join ', ', map {$serialize_type->($_)} @{$self->{raises}};
2231 $r .= ')';
2232 }
2233 $r .= ";\x0A";
2234 return $r;
2235 } # idl_text
2236
2237 package Whatpm::WebIDL::Argument;
2238 push our @ISA, 'Whatpm::WebIDL::Node';
2239
2240 sub new ($$) {
2241 return bless {node_name => ''.$_[1], type => '::any::'}, $_[0];
2242 } # new
2243
2244 sub idl_text ($) {
2245 my $self = shift;
2246 my $r = $self->_xattrs_text;
2247 $r .= ' ' if length $r;
2248 $r .= 'in ' . $self->type_text . ' ' . $self->node_name; ## TODO: escape
2249 return $r;
2250 } # idl_text
2251
2252 *_xattrs_text = \&Whatpm::WebIDL::Definition::_xattrs_text;
2253
2254 *set_extended_attribute_node = \&Whatpm::WebIDL::Definition::set_extended_attribute_node;
2255
2256 *type = \&Whatpm::WebIDL::Definition::type;
2257
2258 *type_text = \&Whatpm::WebIDL::Definition::type_text;
2259
2260 package Whatpm::WebIDL::ExceptionMember;
2261 push our @ISA, 'Whatpm::WebIDL::Node';
2262
2263 sub new ($$) {
2264 return bless {node_name => ''.$_[1], type => '::any::'}, $_[0];
2265 } # new
2266
2267 sub idl_text ($) {
2268 my $self = shift;
2269 my $r = $self->_xattrs_text;
2270 $r .= ' ' if length $r;
2271 $r .= $self->type_text . ' ' . $self->node_name . ";\x0A"; ## TODO: escape
2272 return $r;
2273 } # idl_text
2274
2275 *_xattrs_text = \&Whatpm::WebIDL::Definition::_xattrs_text;
2276
2277 *set_extended_attribute_node = \&Whatpm::WebIDL::Definition::set_extended_attribute_node;
2278
2279 *type = \&Whatpm::WebIDL::Definition::type;
2280
2281 *type_text = \&Whatpm::WebIDL::Definition::type_text;
2282
2283 package Whatpm::WebIDL::ExtendedAttribute;
2284 push our @ISA, 'Whatpm::WebIDL::Node';
2285
2286 sub new ($$) {
2287 return bless {child_nodes => [], node_name => ''.$_[1]};
2288 } # new
2289
2290 sub has_argument_list ($;$) {
2291 if (@_ > 1) {
2292 if ($_[1]) {
2293 $_[0]->{has_argument_list} = 1;
2294 } else {
2295 delete $_[0]->{has_argument_list};
2296 }
2297 }
2298
2299 return ($_[0]->{has_argument_list} or scalar @{$_[0]->{child_nodes}});
2300 } # has_argument_list
2301
2302 sub idl_text ($) {
2303 my $self = shift;
2304 my $r = $self->node_name; ## TODO:] esceape
2305 if (defined $self->{value}) {
2306 $r .= '=' . $self->{value}; ## TODO: escape
2307 }
2308 if ($self->has_argument_list) {
2309 $r .= ' (';
2310 $r .= join ', ', map {$_->idl_text} @{$self->{child_nodes}};
2311 $r .= ')';
2312 }
2313 return $r;
2314 } # idl_text
2315
2316 sub value ($;$) {
2317 if (@_ > 1) {
2318 if (defined $_[1]) {
2319 $_[0]->{value} = ''.$_[1];
2320 } else {
2321 delete $_[0]->{value};
2322 }
2323 }
2324
2325 return $_[0]->{value};
2326 } # value
2327
2328 1;

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24