/[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 - (hide annotations) (download)
Tue Sep 16 04:20:52 2008 UTC (16 years, 1 month 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 wakaba 1.1 package Whatpm::WebIDL;
2     use strict;
3    
4     package Whatpm::WebIDL::Parser;
5    
6 wakaba 1.11 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 wakaba 1.1 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 wakaba 1.15 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 wakaba 1.1 sub new ($) {
23     my $self = bless {
24     must_level => 'm',
25     should_level => 's',
26     warn_level => 'w',
27     info_level => 'i',
28 wakaba 1.15 level => $default_levels,
29 wakaba 1.1 }, $_[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 wakaba 1.9 $defs->set_user_data (manakai_source_line => 1);
39     $defs->set_user_data (manakai_source_column => 1);
40 wakaba 1.1
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 wakaba 1.4 my $prev_xattrs = [];
103 wakaba 1.1 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 wakaba 1.10 ## NOTE: Convert a |ScopedName| into a "scoped name".
126    
127 wakaba 1.1 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 wakaba 1.10 my $identifier = $token->{value};
134     $identifier =~ s/^_//;
135     push @$name, $identifier;
136 wakaba 1.1 $token = $get_next_token->();
137     while ($token->{type} eq '::') {
138 wakaba 1.2 $token = $get_next_token->();
139 wakaba 1.1 if ($token->{type} eq 'identifier') {
140 wakaba 1.10 my $identifier = $token->{value};
141     $identifier =~ s/^_//;
142     push @$name, $identifier;
143 wakaba 1.1 $token = $get_next_token->();
144     } elsif ($token->{type} eq 'DOMString') {
145 wakaba 1.13 push @$name, 'DOMString', '', '';
146 wakaba 1.1 $token = $get_next_token->();
147     last;
148     }
149     }
150     } elsif ($token->{type} eq '::') {
151     push @$name, '';
152     while ($token->{type} eq '::') {
153 wakaba 1.2 $token = $get_next_token->();
154 wakaba 1.1 if ($token->{type} eq 'identifier') {
155 wakaba 1.10 my $identifier = $token->{value};
156     $identifier =~ s/^_//;
157     push @$name, $identifier;
158 wakaba 1.1 $token = $get_next_token->();
159     } elsif ($token->{type} eq 'DOMString') {
160 wakaba 1.13 push @$name, 'DOMString', '', '';
161 wakaba 1.1 $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 wakaba 1.10 return join '::', @$name;
176 wakaba 1.1 }; # $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 wakaba 1.11 $r = '::'.$token->{type}.'::';
186 wakaba 1.1 $token = $get_next_token->();
187     } elsif ($token->{type} eq 'unsigned') {
188     $token = $get_next_token->();
189     if ($token->{type} eq 'short') {
190 wakaba 1.11 $r = '::unsigned '.$token->{type}.'::';
191 wakaba 1.1 $token = $get_next_token->();
192     } elsif ($token->{type} eq 'long') {
193     $token = $get_next_token->();
194     if ($token->{type} eq 'long') {
195 wakaba 1.11 $r = '::unsigned long long::';
196 wakaba 1.1 $token = $get_next_token->();
197     } else {
198 wakaba 1.11 $r = '::unsigned long::';
199 wakaba 1.1 # 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 wakaba 1.11 $r = '::long long::';
209 wakaba 1.1 $token = $get_next_token->();
210     } else {
211 wakaba 1.11 $r = '::long::';
212 wakaba 1.1 # 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 wakaba 1.11 $r = '::::sequence::::' . $type;
235 wakaba 1.1 $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 wakaba 1.4 $xattrs = [];
262 wakaba 1.1 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 wakaba 1.5 $state = 'before block semicolon';
273 wakaba 1.1 } 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 wakaba 1.15 my $id = $token->{value};
286     $id =~ s/^_//;
287     push @current, Whatpm::WebIDL::ExtendedAttribute->new ($id);
288 wakaba 1.9 $current[-1]->set_user_data (manakai_source_line => $line);
289     $current[-1]->set_user_data (manakai_source_column => $column);
290 wakaba 1.4 push @$xattrs, $current[-1];
291 wakaba 1.1 $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 wakaba 1.4 $next_state = 'before definitions'; ## TODO:
299 wakaba 1.1 }
300     } elsif ($state eq 'after xattr') {
301     if ($token->{type} eq '=') {
302     $token = $get_next_token->();
303     $state = 'before xattrarg';
304 wakaba 1.4 } elsif ($token->{type} eq '(') {
305 wakaba 1.15 $current[-1]->has_argument_list (1);
306 wakaba 1.4 $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 wakaba 1.1 } else {
317 wakaba 1.4 push @$prev_xattrs, $xattrs;
318 wakaba 1.1 # reconsume
319     $state = 'after xattrarg';
320     }
321     } elsif ($state eq 'before xattrarg') {
322     if ($token->{type} eq 'identifier') {
323 wakaba 1.9 my $identifier = $token->{value};
324     $identifier =~ s/^_//;
325     $current[-1]->value ($identifier);
326 wakaba 1.1 $token = $get_next_token->();
327 wakaba 1.4 if ($token->{type} eq '(') {
328 wakaba 1.15 $current[-1]->has_argument_list (1);
329 wakaba 1.4 $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 wakaba 1.1 } 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 wakaba 1.4 pop @current; # xattr
353     $xattrs = pop @$prev_xattrs;
354 wakaba 1.1 if ($token->{type} eq ',') {
355     $token = $get_next_token->();
356     $state = 'before xattr';
357     } elsif ($token->{type} eq ']') {
358     $token = $get_next_token->();
359 wakaba 1.4 if ($current[-1]->isa ('Whatpm::WebIDL::Definitions') or
360     $current[-1]->isa ('Whatpm::WebIDL::Module')) {
361 wakaba 1.9 $state = 'before def';
362 wakaba 1.4 } 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 wakaba 1.1 } else {
373     $onerror->(type => 'after xattr', level => 'm', token => $token);
374     # reconsume
375     $state = 'ignore';
376     $nest_level = 0;
377 wakaba 1.4 $next_state = 'before definitions'; ## TODO:
378 wakaba 1.1 }
379     } elsif ($state eq 'before def') {
380     if ($token->{type} eq 'module') {
381     $token = $get_next_token->();
382     if ($token->{type} eq 'identifier') {
383 wakaba 1.9 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 wakaba 1.4 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
389 wakaba 1.1 $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 wakaba 1.9 my $identifier = $token->{value};
401     $identifier =~ s/^_//;
402     push @current, Whatpm::WebIDL::Interface->new ($identifier);
403 wakaba 1.8 $current[-1]->set_user_data (manakai_source_line => $line);
404     $current[-1]->set_user_data (manakai_source_column => $column);
405 wakaba 1.4 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
406 wakaba 1.1 $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 wakaba 1.9 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 wakaba 1.4 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
423 wakaba 1.1 $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 wakaba 1.9 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 wakaba 1.4 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
444 wakaba 1.1 $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 wakaba 1.9 $current[-1]->set_user_data (manakai_source_line => $line);
450     $current[-1]->set_user_data (manakai_source_column => $column);
451 wakaba 1.4 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
452 wakaba 1.1 $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 wakaba 1.11 $current[-1]->type ('::::sequence::::::unsigned short::');
463 wakaba 1.1 $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 wakaba 1.14 $onerror->(type => 'valuetype DOMString',
483     level => $self->{must_level});
484 wakaba 1.8 pop @current; # valuetype
485 wakaba 1.1 #
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 wakaba 1.8 } elsif ($token->{type} eq ';') {
523     $current[-1]->is_forward_declaration (1);
524     # reconsume
525     $state = 'before semicolon';
526     $next_state = 'before interface member';
527 wakaba 1.1 } 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 wakaba 1.2 } 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 wakaba 1.1 } 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 wakaba 1.8 pop @current; # interface
593 wakaba 1.1 # 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 wakaba 1.4 $xattrs = [];
611 wakaba 1.1 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 wakaba 1.5 $state = 'before block semicolon';
618 wakaba 1.1 } 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 wakaba 1.3 # reconsume
670     $state = 'before exception member type';
671 wakaba 1.1 } 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 wakaba 1.8 pop @current; # valuetype
721 wakaba 1.1 # 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 wakaba 1.8 pop @current; # valuetype
729 wakaba 1.1 # 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 wakaba 1.9 $current[-1]->set_user_data (manakai_source_line => $line);
762     $current[-1]->set_user_data (manakai_source_column => $column);
763 wakaba 1.1 $current[-1]->type ($current_type);
764 wakaba 1.4 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
765 wakaba 1.1 $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 wakaba 1.9 $current[-1]->set_user_data (manakai_source_line => $line);
772     $current[-1]->set_user_data (manakai_source_column => $column);
773 wakaba 1.4 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
774 wakaba 1.1 $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 wakaba 1.9 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 wakaba 1.1 $current[-1]->type ($current_type);
793 wakaba 1.4 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
794 wakaba 1.1 $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 wakaba 1.11 } 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 wakaba 1.1 #
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 wakaba 1.3 } 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 wakaba 1.1 } 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 wakaba 1.10 my $identifier = $token->{value};
936     $identifier =~ s/^_//;
937     push @current, Whatpm::WebIDL::Attribute->new ($identifier);
938 wakaba 1.9 $current[-1]->set_user_data (manakai_source_line => $line);
939     $current[-1]->set_user_data (manakai_source_column => $column);
940 wakaba 1.1 $current[-1]->readonly ($read_only);
941     $current[-1]->type ($current_type);
942 wakaba 1.4 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
943 wakaba 1.1 $token = $get_next_token->();
944     if ($token->{type} eq 'getraises') {
945     $token = $get_next_token->();
946 wakaba 1.2 $state = 'after raises';
947     $next_state = '*getraises';
948 wakaba 1.1 next;
949     } elsif ($token->{type} eq 'setraises') {
950     $token = $get_next_token->();
951 wakaba 1.2 $state = 'after raises';
952     $next_state = '*setraises';
953 wakaba 1.1 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 wakaba 1.3 $next_state = 'before interface member';
969     } elsif ($state eq 'before exception member identifier') {
970     if ($token->{type} eq 'identifier') {
971 wakaba 1.13 my $identifier = $token->{value};
972     $identifier =~ s/^_//;
973     push @current, Whatpm::WebIDL::ExceptionMember->new ($identifier);
974 wakaba 1.9 $current[-1]->set_user_data (manakai_source_line => $line);
975     $current[-1]->set_user_data (manakai_source_column => $column);
976 wakaba 1.3 $current[-1]->type ($current_type);
977 wakaba 1.4 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
978 wakaba 1.3 $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 wakaba 1.1 } 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 wakaba 1.9 $current[-1]->set_user_data (manakai_source_line => $line);
993     $current[-1]->set_user_data (manakai_source_column => $column);
994 wakaba 1.1 $current[-1]->type ($current_type);
995 wakaba 1.4 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
996 wakaba 1.1 $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 wakaba 1.2 $state = 'before raises';
1002     $next_state = '*raises';
1003 wakaba 1.1 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 wakaba 1.3 $next_state = 'before interface member';
1023 wakaba 1.1 } elsif ($state eq 'before argument identifier') {
1024     if ($token->{type} eq 'identifier') {
1025 wakaba 1.15 my $id = $token->{value};
1026     $id =~ s/^_//;
1027     my $arg = Whatpm::WebIDL::Argument->new ($id);
1028 wakaba 1.9 $arg->set_user_data (manakai_source_line => $line);
1029     $arg->set_user_data (manakai_source_column => $column);
1030 wakaba 1.1 $arg->type ($current_type);
1031 wakaba 1.4 $arg->set_extended_attribute_node ($_) for @$xattrs;
1032 wakaba 1.1 $current[-1]->append_child ($arg);
1033     $token = $get_next_token->();
1034     if ($token->{type} eq ')') {
1035     $token = $get_next_token->();
1036 wakaba 1.4 if ($current[-1]->isa ('Whatpm::WebIDL::Operation')) {
1037     $state = 'before raises';
1038     } else {
1039     $state = 'after xattrarg';
1040     }
1041 wakaba 1.1 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 wakaba 1.4 $xattrs = [];
1061 wakaba 1.1 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 wakaba 1.2 $next_state = '*raises';
1084 wakaba 1.1 } else {
1085     # reconsume
1086     $state = 'before semicolon';
1087     $next_state = 'before interface member';
1088     }
1089     } elsif ($state eq 'after raises') {
1090 wakaba 1.2 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 wakaba 1.1 } 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 wakaba 1.5 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 wakaba 1.1 } else {
1115 wakaba 1.5 $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 wakaba 1.1 pop @current;
1125 wakaba 1.5 $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 wakaba 1.1 $onerror->(type => 'before semicolon', level => 'm',
1138     token => $token);
1139 wakaba 1.5 pop @current; # avoid appended by 'ignore'
1140 wakaba 1.1 # reconsume
1141     $state = 'ignore';
1142     $nest_level = 0;
1143     }
1144     } elsif ($state eq 'ignore') {
1145 wakaba 1.5 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 wakaba 1.1 $token = $get_next_token->();
1156 wakaba 1.5 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 wakaba 1.1 } 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 wakaba 1.5 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 wakaba 1.1 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 wakaba 1.5 $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 wakaba 1.1 }
1202    
1203     $get_type = undef; # unlink loop
1204    
1205     return $defs;
1206     } # parse_char_string
1207    
1208 wakaba 1.8 package Whatpm::WebIDL::Node;
1209 wakaba 1.1
1210 wakaba 1.9 require Scalar::Util;
1211    
1212 wakaba 1.1 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 wakaba 1.9 $child->{parent_node} = $self;
1226     Scalar::Util::weaken ($child->{parent_node});
1227    
1228 wakaba 1.1 return $child;
1229     } # append_child
1230    
1231 wakaba 1.8 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 wakaba 1.1 sub node_name ($) {
1240 wakaba 1.8 return $_[0]->{node_name}; # may be undef
1241 wakaba 1.1 } # node_name
1242    
1243 wakaba 1.9 sub parent_node ($) {
1244     return $_[0]->{parent_node};
1245     } # parent_node
1246    
1247 wakaba 1.8 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 wakaba 1.15 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 wakaba 1.8
1446 wakaba 1.11 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 wakaba 1.12 my $defined_qnames = {};
1550     my $resolve = sub ($$) {
1551     my $i_sn = shift;
1552     my $scope = shift;
1553    
1554 wakaba 1.13 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 wakaba 1.12 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 wakaba 1.10 my $items = [map { {node => $_, scope => '::'} } @{$self->{child_nodes}}];
1578 wakaba 1.9
1579     while (@$items) {
1580     my $item = shift @$items;
1581 wakaba 1.10 if ($item->{node}->isa ('Whatpm::WebIDL::Definition') and
1582 wakaba 1.13 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 wakaba 1.9 if ($item->{node}->isa ('Whatpm::WebIDL::Interface')) {
1600 wakaba 1.10 for my $i_sn (@{$item->{node}->{inheritances}}) {
1601 wakaba 1.12 my $def = $resolve->($i_sn, $item->{scope});
1602    
1603     unless ($def and $def->{node}->isa ('Whatpm::WebIDL::Interface')) {
1604 wakaba 1.13 $i_sn =~ s/::DOMString::::\z/::DOMString/;
1605 wakaba 1.12 $onerror->(type => 'interface not defined',
1606     level => 'm',
1607     node => $item->{node},
1608     text => $i_sn);
1609 wakaba 1.10 }
1610     }
1611    
1612     my $defined_members = {};
1613 wakaba 1.15 my $defined_accessors = {};
1614 wakaba 1.9 unshift @$items,
1615 wakaba 1.12 map { {node => $_, defined_members => $defined_members,
1616 wakaba 1.15 defined_accessors => $defined_accessors,
1617 wakaba 1.12 scope => $item->{scope}} }
1618 wakaba 1.9 @{$item->{node}->{child_nodes}};
1619     } elsif ($item->{node}->isa ('Whatpm::WebIDL::Exception')) {
1620 wakaba 1.13 my $defined_members = {};
1621 wakaba 1.9 unshift @$items,
1622 wakaba 1.13 map { {node => $_, defined_members => $defined_members,
1623 wakaba 1.12 scope => $item->{scope}} }
1624 wakaba 1.9 @{$item->{node}->{child_nodes}};
1625 wakaba 1.11 } elsif ($item->{node}->isa ('Whatpm::WebIDL::Const')) {
1626     $check_const_value->($item);
1627 wakaba 1.13 } 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 wakaba 1.11 node => $item->{node});
1636     }
1637 wakaba 1.13 ## 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 wakaba 1.9 node => $item->{node});
1663 wakaba 1.13 } 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 wakaba 1.9 }
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 wakaba 1.10 ## 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 wakaba 1.9 $onerror->(type => 'duplicate qname',
1698     level => 'm',
1699     node => $item->{node});
1700     } else {
1701 wakaba 1.10 $defined_qnames->{$qname} = $item;
1702     ## NOTE: This flag must be turned on AFTER inheritance check is
1703     ## performed (c.f. |interface x : x {};|).
1704 wakaba 1.9 }
1705     } elsif ($item->{node}->isa ('Whatpm::WebIDL::InterfaceMember')) {
1706     if ($item->{node}->isa ('Whatpm::WebIDL::Operation')) {
1707 wakaba 1.12 ## NOTE: Arguments
1708 wakaba 1.9 unshift @$items,
1709 wakaba 1.12 map { {node => $_, scope => $item->{scope}} }
1710 wakaba 1.9 @{$item->{node}->{child_nodes}};
1711 wakaba 1.10 } else {
1712     my $name = $item->{node}->node_name;
1713     if ($item->{defined_members}->{$name}) {
1714 wakaba 1.13 $onerror->(type => 'duplicate member',
1715 wakaba 1.10 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 wakaba 1.12 }
1724 wakaba 1.11
1725 wakaba 1.12 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 wakaba 1.11 }
1743 wakaba 1.12
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 wakaba 1.9 }
1763 wakaba 1.13 } elsif ($item->{node}->isa ('Whatpm::WebIDL::Argument') or
1764     $item->{node}->isa ('Whatpm::WebIDL::ExceptionMember')) {
1765 wakaba 1.15 ## 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 wakaba 1.12
1773 wakaba 1.13 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 wakaba 1.12 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 wakaba 1.13 }
1798 wakaba 1.9 }
1799    
1800     my $xattrs = $item->{node}->{xattrs} || [];
1801 wakaba 1.15 X: for my $xattr (@$xattrs) {
1802 wakaba 1.9 my $xattr_name = $xattr->node_name;
1803 wakaba 1.15 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 wakaba 1.10 if ({
1846 wakaba 1.15 Undefined => 1,
1847 wakaba 1.10 }->{$xattr_name}) {
1848 wakaba 1.12 if ($item->{node}->isa ('Whatpm::WebIDL::Attribute') or
1849     $item->{node}->isa ('Whatpm::WebIDL::Argument')) {
1850    
1851     next;
1852     } else {
1853     #
1854     }
1855 wakaba 1.15
1856     } elsif ($xattr_name eq 'PutForwards') {
1857     if ($item->{node}->isa ('Whatpm::WebIDL::Attribute')) {
1858 wakaba 1.12
1859     next;
1860     } else {
1861     #
1862     }
1863 wakaba 1.15 } elsif ($xattr_name eq 'Variadic') {
1864     if ($item->{node}->isa ('Whatpm::WebIDL::Operation')) {
1865 wakaba 1.12
1866     next;
1867     } else {
1868     #
1869     }
1870 wakaba 1.15 } elsif ({NamedConstructor => 1,
1871     }->{$xattr_name}) {
1872     if ($item->{node}->isa ('Whatpm::WebIDL::Interface')) {
1873 wakaba 1.12
1874     next;
1875     } else {
1876     #
1877     }
1878     } elsif ({
1879 wakaba 1.15 NativeObject => 1,
1880 wakaba 1.13 Stringifies => 1,
1881 wakaba 1.12 }->{$xattr_name}) {
1882 wakaba 1.10 if ($item->{node}->isa ('Whatpm::WebIDL::Interface')) {
1883    
1884     next;
1885     } else {
1886     #
1887     }
1888 wakaba 1.13 } 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 wakaba 1.15
1897    
1898 wakaba 1.9 }
1899 wakaba 1.8
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 wakaba 1.4 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 wakaba 1.9 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 wakaba 1.1 sub type ($;$) {
1943     if (@_ > 1) {
1944     if (defined $_[1]) {
1945     $_[0]->{type} = $_[1];
1946     } else {
1947 wakaba 1.11 $_[0]->{type} = '::any::';
1948 wakaba 1.1 }
1949     }
1950     return $_[0]->{type};
1951     } # type
1952    
1953 wakaba 1.6 my $serialize_type;
1954     $serialize_type = sub ($) {
1955     my $type = shift;
1956 wakaba 1.11 if ($type =~ s/^::::sequence:::://) {
1957     return 'sequence<' . $serialize_type->($type) . '>';
1958     } elsif ($type =~ /\A::([^:]+)::\z/) {
1959     return $1;
1960 wakaba 1.6 } else {
1961 wakaba 1.13 $type =~ s/::DOMString::::\z/::DOMString/;
1962 wakaba 1.10 return $type; ## TODO: escape identifiers...
1963 wakaba 1.6 }
1964     }; # $serialize_type
1965    
1966 wakaba 1.1 sub type_text ($) {
1967     my $type = $_[0]->{type};
1968     return undef unless defined $type;
1969 wakaba 1.6
1970     return $serialize_type->($type);
1971 wakaba 1.1 } # type_text
1972 wakaba 1.6
1973 wakaba 1.1 package Whatpm::WebIDL::Module;
1974     push our @ISA, 'Whatpm::WebIDL::Definition';
1975    
1976     sub idl_text ($) {
1977 wakaba 1.4 my $self = shift;
1978     my $r = $self->_xattrs_text;
1979     $r .= ' ' if length $r;
1980 wakaba 1.8 $r .= 'module ' . $self->node_name . " {\x0A\x0A"; ## TODO: escape
1981 wakaba 1.4 for (@{$self->{child_nodes}}) {
1982 wakaba 1.1 $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 wakaba 1.3 my $self = shift;
2005 wakaba 1.4 my $r = $self->_xattrs_text;
2006     $r .= ' ' if length $r;
2007 wakaba 1.9 $r .= 'interface ' . $self->node_name;
2008 wakaba 1.8
2009     if ($self->{is_forward_declaration}) {
2010     $r .= ";\x0A";
2011     return $r;
2012     }
2013    
2014 wakaba 1.3 if (@{$self->{inheritances}}) {
2015     $r .= ' : '; ## TODO: ...
2016 wakaba 1.10 $r .= join ', ', map {$serialize_type->($_)} @{$self->{inheritances}};
2017 wakaba 1.3 }
2018     $r .= " {\x0A"; ## TODO: escape
2019     for (@{$self->{child_nodes}}) {
2020 wakaba 1.1 $r .= ' ' . $_->idl_text;
2021     }
2022     $r .= "};\x0A";
2023     return $r;
2024     } # idl_text
2025    
2026 wakaba 1.8 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 wakaba 1.1 package Whatpm::WebIDL::Exception;
2039     push our @ISA, 'Whatpm::WebIDL::Definition';
2040    
2041 wakaba 1.3 sub idl_text ($) {
2042 wakaba 1.4 my $self = shift;
2043     my $r = $self->_xattrs_text;
2044     $r .= ' ' if length $r;
2045 wakaba 1.9 $r .= 'exception ' . $self->node_name . " {\x0A"; ## TODO: escape
2046 wakaba 1.4 for (@{$self->{child_nodes}}) {
2047 wakaba 1.3 $r .= ' ' . $_->idl_text;
2048     }
2049     $r .= "};\x0A";
2050     return $r;
2051     } # idl_text
2052    
2053 wakaba 1.1 package Whatpm::WebIDL::Typedef;
2054     push our @ISA, 'Whatpm::WebIDL::Definition';
2055    
2056     sub new ($$) {
2057     my $self = shift->SUPER::new (@_);
2058 wakaba 1.11 $self->{type} = '::any::';
2059 wakaba 1.1 return $self;
2060     } # new
2061    
2062     sub idl_text ($) {
2063 wakaba 1.4 my $self = shift;
2064     my $r = $self->_xattrs_text;
2065     $r .= ' ' if length $r;
2066 wakaba 1.13 my $node_name = $self->node_name;
2067     $node_name = 'DOMString' if $node_name eq '::DOMString::';
2068 wakaba 1.1 ## TODO: escape
2069 wakaba 1.13 $r .= 'typedef ' . $self->type_text . ' ' . $node_name . ";\x0A";
2070 wakaba 1.4 return $r;
2071 wakaba 1.1 } # 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 wakaba 1.11 $self->{type} = '::boolean::';
2079 wakaba 1.1 return $self;
2080     } # new
2081    
2082     sub idl_text ($) {
2083 wakaba 1.4 my $self = shift;
2084     my $r = $self->_xattrs_text;
2085     $r .= ' ' if length $r;
2086 wakaba 1.14 my $name = $self->node_name;
2087     $name = 'DOMString' if $name eq '::DOMString::';
2088 wakaba 1.1 ## TODO: escape
2089 wakaba 1.14 $r .= 'valuetype ' . $name . ' ' . $self->type_text . ";\x0A";
2090 wakaba 1.4 return $r;
2091 wakaba 1.1 } # idl_text
2092    
2093     package Whatpm::WebIDL::InterfaceMember;
2094 wakaba 1.8 push our @ISA, 'Whatpm::WebIDL::Node';
2095 wakaba 1.1
2096     sub new ($$) {
2097     return bless {node_name => ''.$_[1]}, $_[0];
2098     } # new
2099    
2100 wakaba 1.8 sub child_nodes ($) { return [] }
2101    
2102 wakaba 1.4 *_xattrs_text = \&Whatpm::WebIDL::Definition::_xattrs_text;
2103    
2104     *set_extended_attribute_node = \&Whatpm::WebIDL::Definition::set_extended_attribute_node;
2105    
2106 wakaba 1.1 *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 wakaba 1.11 $self->{type} = '::boolean::';
2116 wakaba 1.1 $self->{value} = ['FALSE'];
2117     return $self;
2118     } # new
2119    
2120     sub value ($;$) {
2121     if (@_ > 1) {
2122 wakaba 1.11 if (defined $_[1]) {
2123     $_[0]->{value} = $_[1];
2124     } else {
2125     $_[0]->{value} = ['FALSE'];
2126     }
2127 wakaba 1.1 }
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 wakaba 1.4 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 wakaba 1.1 } # 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 wakaba 1.11 $self->{type} = '::any::';
2159 wakaba 1.2 $self->{getraises} = [];
2160     $self->{setraises} = [];
2161 wakaba 1.1 return $self;
2162     } # new
2163    
2164 wakaba 1.2 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 wakaba 1.1 sub readonly ($;$) {
2175     if (@_ > 1) {
2176     $_[0]->{readonly} = $_[1];
2177     }
2178    
2179     return $_[0]->{readonly};
2180     } # readonly
2181    
2182     sub idl_text ($) {
2183 wakaba 1.2 my $self = shift;
2184 wakaba 1.4 my $r = $self->_xattrs_text;
2185     $r .= ' ' if length $r;
2186     $r .= ($self->readonly ? 'readonly ' : '') . 'attribute ' . $self->type_text . ' ' . $self->node_name;
2187 wakaba 1.1 ## TODO: escape
2188 wakaba 1.2 if (@{$self->{getraises}}) {
2189     $r .= ' getraises (';
2190     ## todo: ...
2191 wakaba 1.12 $r .= join ', ', map {$serialize_type->($_)} @{$self->{getraises}};
2192 wakaba 1.2 $r .= ')';
2193     }
2194     if (@{$self->{setraises}}) {
2195     $r .= ' setraises (';
2196     ## todo: ...
2197 wakaba 1.12 $r .= join ', ', map {$serialize_type->($_)} @{$self->{setraises}};
2198 wakaba 1.2 $r .= ')';
2199     }
2200     $r .= ";\x0A";
2201     return $r;
2202 wakaba 1.1 } # 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 wakaba 1.11 $self->{type} = '::any::';
2210 wakaba 1.1 $self->{child_nodes} = [];
2211 wakaba 1.2 $self->{raises} = [];
2212 wakaba 1.1 return $self;
2213     } # new
2214    
2215 wakaba 1.2 sub append_raises ($$) {
2216     ## TODO: error check, etc.
2217     push @{$_[0]->{raises}}, $_[1];
2218     } # append_raises
2219    
2220 wakaba 1.1 sub idl_text ($) {
2221 wakaba 1.2 my $self = shift;
2222 wakaba 1.4 my $r = $self->_xattrs_text;
2223     $r .= ' ' if length $r;
2224     $r .= $self->type_text . ' ' . $self->node_name . ' ('; ## TODO: escape
2225 wakaba 1.2 $r .= join ', ', map {$_->idl_text} @{$self->{child_nodes}};
2226     $r .= ')';
2227     if (@{$self->{raises}}) {
2228     $r .= ' raises (';
2229     ## todo: ...
2230 wakaba 1.12 $r .= join ', ', map {$serialize_type->($_)} @{$self->{raises}};
2231 wakaba 1.2 $r .= ')';
2232     }
2233 wakaba 1.1 $r .= ";\x0A";
2234     return $r;
2235     } # idl_text
2236    
2237     package Whatpm::WebIDL::Argument;
2238 wakaba 1.8 push our @ISA, 'Whatpm::WebIDL::Node';
2239 wakaba 1.1
2240     sub new ($$) {
2241 wakaba 1.11 return bless {node_name => ''.$_[1], type => '::any::'}, $_[0];
2242 wakaba 1.1 } # new
2243    
2244     sub idl_text ($) {
2245 wakaba 1.4 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 wakaba 1.3 } # idl_text
2251    
2252 wakaba 1.4 *_xattrs_text = \&Whatpm::WebIDL::Definition::_xattrs_text;
2253    
2254     *set_extended_attribute_node = \&Whatpm::WebIDL::Definition::set_extended_attribute_node;
2255    
2256 wakaba 1.3 *type = \&Whatpm::WebIDL::Definition::type;
2257    
2258     *type_text = \&Whatpm::WebIDL::Definition::type_text;
2259    
2260     package Whatpm::WebIDL::ExceptionMember;
2261 wakaba 1.8 push our @ISA, 'Whatpm::WebIDL::Node';
2262 wakaba 1.3
2263     sub new ($$) {
2264 wakaba 1.11 return bless {node_name => ''.$_[1], type => '::any::'}, $_[0];
2265 wakaba 1.3 } # new
2266    
2267     sub idl_text ($) {
2268 wakaba 1.4 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 wakaba 1.1 } # idl_text
2274    
2275 wakaba 1.4 *_xattrs_text = \&Whatpm::WebIDL::Definition::_xattrs_text;
2276    
2277     *set_extended_attribute_node = \&Whatpm::WebIDL::Definition::set_extended_attribute_node;
2278    
2279 wakaba 1.1 *type = \&Whatpm::WebIDL::Definition::type;
2280    
2281     *type_text = \&Whatpm::WebIDL::Definition::type_text;
2282 wakaba 1.9
2283 wakaba 1.4 package Whatpm::WebIDL::ExtendedAttribute;
2284 wakaba 1.8 push our @ISA, 'Whatpm::WebIDL::Node';
2285 wakaba 1.4
2286     sub new ($$) {
2287     return bless {child_nodes => [], node_name => ''.$_[1]};
2288     } # new
2289    
2290 wakaba 1.15 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 wakaba 1.4 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 wakaba 1.15 if ($self->has_argument_list) {
2309 wakaba 1.4 $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 wakaba 1.1
2328     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24