/[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.16 - (hide annotations) (download)
Tue Sep 16 10:43:18 2008 UTC (16 years, 1 month ago) by wakaba
Branch: MAIN
Changes since 1.15: +289 -81 lines
++ whatpm/t/webidl/ChangeLog	16 Sep 2008 10:42:14 -0000
	* webidl-exception.dat: Test results updated.

	* webidl-interface.dat: New test cases for the reminding extended
	attributes are added.

2008-09-16  Wakaba  <wakaba@suika.fam.cx>

++ whatpm/Whatpm/ChangeLog	16 Sep 2008 10:41:47 -0000
	* WebIDL.pm: Support for the reminding extended attributes are
	added.  It does not satisfy the definition that a forward
	interface declaration has an extended attribute.  It seems that
	unless explicitly allowed multiple extended attributes with the
	same name is not allowed, though it is not explicitly mentioned in
	the spec.

2008-09-16  Wakaba  <wakaba@suika.fam.cx>

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 wakaba 1.16 allow_multiple => 1,
1272 wakaba 1.15 check => sub {
1273 wakaba 1.16 my ($self, $onerror, $levels, $items, $item, $xattr, $xattr_name,
1274     $resolve, $constructors) = @_;
1275 wakaba 1.15
1276     ## NOTE: Arguments
1277     unshift @$items,
1278     map { {node => $_, scope => $item->{scope}} }
1279     @{$xattr->{child_nodes}};
1280     },
1281     },
1282     ExceptionConsts => {
1283     allow_id => 1,
1284     #allow_arglist => 0,
1285     allowed_type => {Module => 1},
1286 wakaba 1.16 #allow_multiple => 0,
1287 wakaba 1.15 check => sub {
1288 wakaba 1.16 my ($self, $onerror, $levels, $items, $item, $xattr, $xattr_name,
1289     $resolve, $constructors) = @_;
1290 wakaba 1.15
1291     my $id = $xattr->value;
1292     if (defined $id) {
1293     my $has_x;
1294     for my $x (@{$item->{node}->child_nodes}) {
1295     next unless $x->isa ('Whatpm::WebIDL::Exception');
1296     if ($x->node_name eq $id) {
1297     $has_x = 1;
1298     last;
1299     }
1300     }
1301     unless ($has_x) {
1302     $onerror->(type => 'exception not defined',
1303     value => $id,
1304     node => $xattr,
1305     level => $levels->{must});
1306     }
1307     } else {
1308     $onerror->(type => 'xattr id missing',
1309     text => $xattr_name,
1310     node => $xattr,
1311     level => $levels->{must});
1312     }
1313     },
1314     },
1315     IndexGetter => {
1316     #allow_id => 0,
1317     #allow_arglist => 0,
1318     allowed_type => {Operation => 1},
1319 wakaba 1.16 #allow_multiple => 0,
1320 wakaba 1.15 check => sub {
1321 wakaba 1.16 my ($self, $onerror, $levels, $items, $item, $xattr, $xattr_name,
1322     $resolve, $constructors) = @_;
1323 wakaba 1.15
1324     if (@{$item->{node}->{child_nodes}} != 1 or
1325     $item->{node}->{child_nodes}->[0]->type_text ne 'unsigned long') {
1326     $onerror->(type => 'wrong signature accessor',
1327     text => $xattr_name,
1328     node => $xattr,
1329     level => $levels->{fact});
1330     }
1331 wakaba 1.16 if ($item->{defined_accessors}->{$xattr_name} and
1332     $item->{defined_accessors}->{$xattr_name} ne $item->{node}) {
1333 wakaba 1.15 $onerror->(type => 'duplicate accessor',
1334     text => $xattr_name,
1335     node => $xattr,
1336     level => $levels->{undefined});
1337     }
1338 wakaba 1.16 $item->{defined_accessors}->{$xattr_name} = $item->{node};
1339 wakaba 1.15 },
1340     },
1341     IndexSetter => {
1342     #allow_id => 0,
1343     #allow_arglist => 0,
1344     allowed_type => {Operation => 1},
1345 wakaba 1.16 #allow_multiple => 0,
1346 wakaba 1.15 check => sub {
1347 wakaba 1.16 my ($self, $onerror, $levels, $items, $item, $xattr, $xattr_name,
1348     $resolve, $constructors) = @_;
1349 wakaba 1.15
1350     if (@{$item->{node}->{child_nodes}} != 2 or
1351     $item->{node}->{child_nodes}->[0]->type_text ne 'unsigned long') {
1352     $onerror->(type => 'wrong signature accessor',
1353     text => $xattr_name,
1354     node => $xattr,
1355     level => $levels->{fact});
1356     }
1357 wakaba 1.16 if ($item->{defined_accessors}->{$xattr_name} and
1358     $item->{defined_accessors}->{$xattr_name} ne $item->{node}) {
1359 wakaba 1.15 $onerror->(type => 'duplicate accessor',
1360     text => $xattr_name,
1361     node => $xattr,
1362     level => $levels->{undefined});
1363     }
1364 wakaba 1.16 $item->{defined_accessors}->{$xattr_name} = $item->{node};
1365 wakaba 1.15 },
1366     },
1367     NameGetter => {
1368     #allow_id => 0,
1369     #allow_arglist => 0,
1370     allowed_type => {Operation => 1},
1371 wakaba 1.16 #allow_multiple => 0,
1372 wakaba 1.15 check => sub {
1373 wakaba 1.16 my ($self, $onerror, $levels, $items, $item, $xattr, $xattr_name,
1374     $resolve, $constructors) = @_;
1375 wakaba 1.15
1376     if (@{$item->{node}->{child_nodes}} != 1 or
1377     $item->{node}->{child_nodes}->[0]->type_text ne 'DOMString') {
1378     $onerror->(type => 'wrong signature accessor',
1379     text => $xattr_name,
1380     node => $xattr,
1381     level => $levels->{fact});
1382     }
1383 wakaba 1.16 if ($item->{defined_accessors}->{$xattr_name} and
1384     $item->{defined_accessors}->{$xattr_name} ne $item->{node}) {
1385 wakaba 1.15 $onerror->(type => 'duplicate accessor',
1386     text => $xattr_name,
1387     node => $xattr,
1388     level => $levels->{undefined});
1389     }
1390 wakaba 1.16 $item->{defined_accessors}->{$xattr_name} = $item->{node};
1391 wakaba 1.15 },
1392     },
1393     NameSetter => {
1394     #allow_id => 0,
1395     #allow_arglist => 0,
1396     allowed_type => {Operation => 1},
1397 wakaba 1.16 #allow_multiple => 0,
1398 wakaba 1.15 check => sub {
1399 wakaba 1.16 my ($self, $onerror, $levels, $items, $item, $xattr, $xattr_name,
1400     $resolve, $constructors) = @_;
1401 wakaba 1.15
1402     if (@{$item->{node}->{child_nodes}} != 2 or
1403     $item->{node}->{child_nodes}->[0]->type ne '::DOMString::') {
1404     $onerror->(type => 'wrong signature accessor',
1405     text => $xattr_name,
1406     node => $xattr,
1407     level => $levels->{fact});
1408     }
1409 wakaba 1.16 if ($item->{defined_accessors}->{$xattr_name} and
1410     $item->{defined_accessors}->{$xattr_name} ne $item->{node}) {
1411 wakaba 1.15 $onerror->(type => 'duplicate accessor',
1412     text => $xattr_name,
1413     node => $xattr,
1414     level => $levels->{undefined});
1415     }
1416 wakaba 1.16 $item->{defined_accessors}->{$xattr_name} = $item->{node};
1417 wakaba 1.15 },
1418     },
1419     Null => {
1420     allow_id => 1,
1421     #allow_arglist => 0,
1422     allowed_type => {Argument => 1, Attribute => 1},
1423     ## ISSUE: Is this allwoed for extended attribute's arguments?
1424 wakaba 1.16 #allow_multiple => 0,
1425 wakaba 1.15 check => sub {
1426 wakaba 1.16 my ($self, $onerror, $levels, $items, $item, $xattr, $xattr_name,
1427     $resolve, $constructors) = @_;
1428 wakaba 1.15
1429 wakaba 1.16 ## ISSUE: [Null=Empty] readonly attribute is not disallowed
1430 wakaba 1.15
1431     if ($item->{node}->type ne '::DOMString::') {
1432     $onerror->(type => 'xattr for wrong type',
1433     text => $xattr_name,
1434     node => $xattr,
1435     level => $levels->{fact});
1436     }
1437    
1438     my $id = $xattr->value;
1439     if (defined $id) {
1440     if ($id eq 'Null' or $id eq 'Empty') {
1441     #
1442     } else {
1443     $onerror->(type => 'xattr id value not allowed',
1444     text => $xattr_name,
1445     value => $id,
1446     node => $xattr,
1447     level => $levels->{must});
1448     }
1449     } else {
1450     $onerror->(type => 'xattr id missing',
1451     text => $xattr_name,
1452     node => $xattr,
1453     level => $levels->{must});
1454     }
1455     },
1456     },
1457 wakaba 1.16 PutForwards => {
1458     allow_id => 1,
1459     #allow_arglist => 0,
1460     allowed_type => {Attribute => 1},
1461     #allow_multiple => 0,
1462     check => sub {
1463     my ($self, $onerror, $levels, $items, $item, $xattr, $xattr_name,
1464     $resolve, $constructors) = @_;
1465    
1466     ## NOTE: Direct or indirect cirlic reference is not an error.
1467    
1468     unless ($item->{node}->readonly) {
1469     $onerror->(type => 'attr not readonly',
1470     text => $xattr_name,
1471     node => $xattr,
1472     level => $levels->{fact});
1473     }
1474    
1475     my $type_item = $resolve->($item->{node}->type, $item->{scope});
1476     if ($type_item and
1477     $type_item->{node}->isa ('Whatpm::WebIDL::Interface')) {
1478     my $id = $xattr->value;
1479     if (defined $id) {
1480     ## ISSUE: "attribute that exists on the interface and which
1481     ## has the same type as this attribute.": "same type"?
1482     ## ISSUE: must be read-write attribute?
1483    
1484     my $check = sub ($) {
1485     my $type_item = shift;
1486    
1487     A: {
1488     for my $attr (@{$type_item->{node}->{child_nodes}}) {
1489     next unless $attr->isa ('Whatpm::WebIDL::Attribute');
1490     if ($attr->node_name eq $id) {
1491     last A;
1492     }
1493     }
1494    
1495     $onerror->(type => 'referenced attr not defined',
1496     text => $xattr_name,
1497     value => $id,
1498     node => $xattr,
1499     level => $levels->{must});
1500     } # A
1501     }; # $check
1502    
1503     if ($type_item->{node}->is_forward_declaration) {
1504     ## NOTE: Checked later.
1505     push @$items, {code => sub {
1506     $check->($resolve->($item->{node}->type, $item->{scope}));
1507     }};
1508     } else {
1509     $check->($type_item);
1510     }
1511     } else {
1512     $onerror->(type => 'xattr id missing',
1513     text => $xattr_name,
1514     node => $xattr,
1515     level => $levels->{must});
1516     }
1517     } else {
1518     ## NOTE: Builtin types, or undefined interface
1519     $onerror->(type => 'attr type not interface',
1520     text => $xattr_name,
1521     node => $xattr,
1522     level => $levels->{fact});
1523     }
1524     },
1525     },
1526     Stringifies => {
1527     allow_id => 1,
1528     #allow_arglist => 0,
1529     allowed_type => {Interface => 1},
1530     #allow_multiple => 0,
1531     check => sub {
1532     my ($self, $onerror, $levels, $items, $item, $xattr, $xattr_name,
1533     $resolve, $constructors) = @_;
1534    
1535     my $id = $xattr->value;
1536     if (defined $id) {
1537     A: {
1538     for my $attr (@{$item->{node}->{child_nodes}}) {
1539     next unless $attr->isa ('Whatpm::WebIDL::Attribute');
1540     if ($attr->node_name eq $id) {
1541     last A;
1542     }
1543     }
1544    
1545     $onerror->(type => 'referenced attr not defined',
1546     text => $xattr_name,
1547     value => $id,
1548     node => $xattr,
1549     level => $levels->{must});
1550     } # A
1551     }
1552     },
1553     },
1554     Variadic => {
1555     #allow_id => 0,
1556     #allow_arglist => 0,
1557     allowed_type => {Argument => 1},
1558     #allow_multiple => 0,
1559     check => sub {
1560     my ($self, $onerror, $levels, $items, $item, $xattr, $xattr_name,
1561     $resolve, $constructors) = @_;
1562    
1563     ## ISSUE: is this allowed on extended attribute arguuments?
1564    
1565     ## NOTE: If this is not the final argument, then an error will
1566     ## be raised later.
1567     $item->{has_arg_xattr}->{variadic} = 1;
1568     },
1569     },
1570    
1571     ## ECMAScript specific extended attributes
1572     NamedConstructor => {
1573     allow_id => 1,
1574     allow_arglist => 1,
1575     allowed_type => {Interface => 1, Exception => 1},
1576     allow_multiple => 1,
1577     check => sub {
1578     my ($self, $onerror, $levels, $items, $item, $xattr, $xattr_name,
1579     $resolve, $constructors) = @_;
1580    
1581     ## NOTE: [NamedConstructor=a,NamedConstructor=a] is not disallowed.
1582    
1583     my $id = $xattr->value;
1584     if (defined $id) {
1585     if ($constructors->{$id} and
1586     $constructors->{$id}->{node} ne $item->{node}) {
1587     $onerror->(type => 'duplicate constructor name',
1588     value => $id,
1589     node => $xattr,
1590     level => $levels->{must});
1591     } else {
1592     $constructors->{$id} = {node => $item->{node},
1593     is_named_constructor => $xattr};
1594     }
1595     } else {
1596     $onerror->(type => 'xattr id missing',
1597     text => $xattr_name,
1598     node => $xattr,
1599     level => $levels->{must});
1600     }
1601     },
1602     },
1603     NativeObject => {
1604     allow_id => 1,
1605     #allow_arglist => 0,
1606     allowed_type => {Interface => 1},
1607     #allow_multiple => 0,
1608     check => sub {
1609     my ($self, $onerror, $levels, $items, $item, $xattr, $xattr_name,
1610     $resolve, $constructors) = @_;
1611    
1612     my $id = $xattr->value;
1613     if (defined $id and $id ne 'FunctionOnly') {
1614     $onerror->(type => 'xattr id value not allowed',
1615     text => $xattr_name,
1616     value => $id,
1617     node => $xattr,
1618     level => $levels->{must});
1619     }
1620    
1621     ## TODO: We should warn if the condition in the section 4.5 is not met.
1622     },
1623     },
1624     NoInterfaceObject => {
1625     #allow_id => 0,
1626     #allow_arglist => 0,
1627     allowed_type => {Interface => 1, Exception => 1},
1628     #allow_multiple => 0,
1629     check => sub {
1630     my ($self, $onerror, $levels, $items, $item, $xattr, $xattr_name,
1631     $resolve, $constructors) = @_;
1632    
1633     ## ISSUE: [Constructor, NoInterfaceObject]
1634     },
1635     },
1636     Undefined => {
1637     allow_id => 1,
1638     #allow_arglist => 0,
1639     allowed_type => {Argument => 1, Attribute => 1},
1640     ## ISSUE: Is this allwoed for extended attribute's arguments?
1641     #allow_multiple => 0,
1642     #check : is set later
1643     },
1644 wakaba 1.15 }; # $xattr_defs
1645 wakaba 1.16 $xattr_defs->{Undefined}->{check} = $xattr_defs->{Null}->{check};
1646 wakaba 1.15
1647     sub check ($$;$) {
1648     my ($self, $onerror, $levels) = @_;
1649    
1650     $levels ||= $default_levels;
1651 wakaba 1.8
1652 wakaba 1.11 my $check_const_value = sub ($) {
1653     my $item = shift;
1654    
1655     my $type = $item->{node}->type;
1656     my $value = $item->{node}->value;
1657    
1658     ## NOTE: Although it is not explicitly spelled out in the spec,
1659     ## it can be assumed, imho, that "any" type accepts all of these
1660     ## constant values.
1661     ## ISSUE: Should I ask the editor to clarify the spec about this?
1662    
1663     if ($value->[0] eq 'TRUE' or $value->[0] eq 'FALSE') {
1664     if ($type eq '::boolean::') {
1665     #
1666     } elsif ($type eq '::any::') {
1667     #
1668     } else {
1669     $onerror->(type => 'const type mismatch',
1670     level => 'm',
1671     node => $item->{node},
1672     text => $item->{node}->type_text,
1673     value => $value->[0]);
1674     }
1675     } elsif ($value->[0] eq 'integer') {
1676     if ($type eq '::octet::') {
1677     if ($value->[1] < 0 or 255 < $value->[1]) {
1678     $onerror->(type => 'const value out of range',
1679     level => 'm',
1680     node => $item->{node},
1681     value => $value->[1]);
1682     }
1683     } elsif ($type eq '::short::') {
1684     if ($value->[1] < -32768 or 32767 < $value->[1]) {
1685     $onerror->(type => 'const value out of range',
1686     level => 'm',
1687     node => $item->{node},
1688     value => $value->[1]);
1689     }
1690     } elsif ($type eq '::unsigned short::') {
1691     if ($value->[1] < 0 or 65535 < $value->[1]) {
1692     $onerror->(type => 'const value out of range',
1693     level => 'm',
1694     node => $item->{node},
1695     value => $value->[1]);
1696     }
1697     } elsif ($type eq '::long::') {
1698     if ($value->[1] < -2147483648 or 2147483647 < $value->[1]) {
1699     $onerror->(type => 'const value out of range',
1700     level => 'm',
1701     node => $item->{node},
1702     value => $value->[1]);
1703     }
1704     } elsif ($type eq '::unsigned long::') {
1705     if ($value->[1] < 0 or 4294967295 < $value->[1]) {
1706     $onerror->(type => 'const value out of range',
1707     level => 'm',
1708     node => $item->{node},
1709     value => $value->[1]);
1710     }
1711     } elsif ($type eq '::long long::') {
1712     if ($value->[1] < -9223372036854775808 or
1713     9223372036854775807 < $value->[1]) {
1714     $onerror->(type => 'const value out of range',
1715     level => 'm',
1716     node => $item->{node},
1717     value => $value->[1]);
1718     }
1719     } elsif ($type eq '::unsigned long long::') {
1720     if ($value->[1] < 0 or 18446744073709551615 < $value->[1]) {
1721     $onerror->(type => 'const value out of range',
1722     level => 'm',
1723     node => $item->{node},
1724     value => $value->[1]);
1725     }
1726     } elsif ($type eq '::any::') {
1727     if ($value->[1] < -9223372036854775808 or
1728     18446744073709551615 < $value->[1]) {
1729     $onerror->(type => 'const value out of range',
1730     level => 'm',
1731     node => $item->{node},
1732     value => $value->[1]);
1733     }
1734     } else {
1735     $onerror->(type => 'const type mismatch',
1736     level => 'm',
1737     node => $item->{node},
1738     text => $item->{node}->type_text,
1739     value => $value->[1]);
1740     }
1741     } elsif ($value->[0] eq 'float') {
1742     if ($type eq '::float::' or $type eq '::any::') {
1743     #
1744     } else {
1745     $onerror->(type => 'const type mismatch',
1746     level => 'm',
1747     node => $item->{node},
1748     text => $item->{node}->type_text,
1749     value => $value->[1]);
1750     }
1751     }
1752     ## NOTE: Otherwise, an error of the implementation or the application.
1753     }; # $check_const_value
1754    
1755 wakaba 1.12 my $defined_qnames = {};
1756     my $resolve = sub ($$) {
1757     my $i_sn = shift;
1758     my $scope = shift;
1759    
1760 wakaba 1.16 if ($i_sn =~ /\A::(?>[^:]+)::\z/ or
1761 wakaba 1.13 $i_sn =~ /^::::sequence::::/) {
1762     return undef;
1763     } elsif ($i_sn =~ /::DOMString\z/ or
1764     $i_sn =~ /::DOMString::::\z/) {
1765 wakaba 1.12 return undef;
1766     } elsif ($i_sn =~ /^::/) {
1767     if ($defined_qnames->{$i_sn}) {
1768     return $defined_qnames->{$i_sn};
1769     } else {
1770     return undef;
1771     }
1772     } else {
1773     if ($defined_qnames->{$scope . $i_sn}) {
1774     return $defined_qnames->{$scope . $i_sn};
1775     } elsif ($defined_qnames->{'::' . $i_sn}) {
1776     return $defined_qnames->{'::' . $i_sn};
1777     } else {
1778     return undef;
1779     }
1780     }
1781     }; # $resolve
1782    
1783 wakaba 1.10 my $items = [map { {node => $_, scope => '::'} } @{$self->{child_nodes}}];
1784 wakaba 1.9
1785 wakaba 1.16 my $constructors = {};
1786     ## NOTE: Items are: identifier => {node => $interface_or_exception,
1787     ## is_named_constructor => $named_constructor_xattr/undef}.
1788    
1789 wakaba 1.9 while (@$items) {
1790     my $item = shift @$items;
1791 wakaba 1.16 if (not $item->{node}) {
1792     $item->{code}->();
1793     next;
1794     } elsif ($item->{node}->isa ('Whatpm::WebIDL::Definition') and
1795     not $item->{defined_members}) {
1796     ## NOTE: Const in Interface does not have this.
1797 wakaba 1.13 if ($item->{node}->isa ('Whatpm::WebIDL::Module')) {
1798     unshift @$items,
1799     map {
1800     {node => $_, parent => $item->{node},
1801     scope => $item->{scope} . $item->{node}->node_name . '::'}
1802     }
1803     @{$item->{node}->{child_nodes}};
1804     } else {
1805     unless ($item->{parent}) {
1806     $onerror->(type => 'non-module definition',
1807     level => 's',
1808     node => $item->{node});
1809     }
1810     }
1811    
1812 wakaba 1.9 if ($item->{node}->isa ('Whatpm::WebIDL::Interface')) {
1813 wakaba 1.10 for my $i_sn (@{$item->{node}->{inheritances}}) {
1814 wakaba 1.12 my $def = $resolve->($i_sn, $item->{scope});
1815    
1816     unless ($def and $def->{node}->isa ('Whatpm::WebIDL::Interface')) {
1817 wakaba 1.13 $i_sn =~ s/::DOMString::::\z/::DOMString/;
1818 wakaba 1.12 $onerror->(type => 'interface not defined',
1819     level => 'm',
1820     node => $item->{node},
1821     text => $i_sn);
1822 wakaba 1.10 }
1823     }
1824    
1825     my $defined_members = {};
1826 wakaba 1.15 my $defined_accessors = {};
1827 wakaba 1.9 unshift @$items,
1828 wakaba 1.12 map { {node => $_, defined_members => $defined_members,
1829 wakaba 1.15 defined_accessors => $defined_accessors,
1830 wakaba 1.12 scope => $item->{scope}} }
1831 wakaba 1.9 @{$item->{node}->{child_nodes}};
1832     } elsif ($item->{node}->isa ('Whatpm::WebIDL::Exception')) {
1833 wakaba 1.13 my $defined_members = {};
1834 wakaba 1.9 unshift @$items,
1835 wakaba 1.13 map { {node => $_, defined_members => $defined_members,
1836 wakaba 1.12 scope => $item->{scope}} }
1837 wakaba 1.9 @{$item->{node}->{child_nodes}};
1838 wakaba 1.11 } elsif ($item->{node}->isa ('Whatpm::WebIDL::Const')) {
1839     $check_const_value->($item);
1840 wakaba 1.13 } elsif ($item->{node}->isa ('Whatpm::WebIDL::Typedef')) {
1841     my $name = $item->{node}->node_name;
1842     my $type = $item->{node}->type;
1843     if ($name eq '::DOMString::' or
1844     $type eq '::DOMString::' or
1845     $type =~ /::DOMString::::\z/) {
1846     $onerror->(type => 'typedef ignored',
1847     level => 'i',
1848 wakaba 1.11 node => $item->{node});
1849     }
1850 wakaba 1.13 ## ISSUE: Refernece to a non-defined interface is not non-conforming.
1851    
1852     ## ISSUE: What does "ignored" mean?
1853     ## "typedef dom::DOMString a; typedef long a;" is conforming?
1854     ## "typedef dom::DOMString b; interface c { attribute b d; };" is?
1855    
1856     ## ISSUE: Is "sequence<undefinedtype>" conforming?
1857    
1858     if ($type =~ /\A::([^:]+)::\z/) {
1859     $item->{allow_null} = {
1860     boolean => 1, octet => 1, short => 1, 'unsigned short' => 1,
1861     long => 1, 'unsigned long' => 1, 'long long' => 1,
1862     'unsigned long long' => 1, float => 1,
1863     }->{$1};
1864     } elsif ($type =~ /^::::sequence::::/) {
1865     $item->{allow_null} = 1;
1866     } else {
1867     my $def = $resolve->($type, $item->{scope});
1868     $item->{allow_null} = $def->{allow_null};
1869     }
1870     } elsif ($item->{node}->isa ('Whatpm::WebIDL::Valuetype')) {
1871     my $name = $item->{node}->node_name;
1872     if ($name eq '::DOMString::') {
1873     $onerror->(type => 'ignored valuetype',
1874     level => 'i',
1875 wakaba 1.9 node => $item->{node});
1876 wakaba 1.13 } else {
1877     my $type = $item->{node}->type;
1878     if ($type =~ /\A::[^:]+::\z/) {
1879     #
1880     } elsif ($type =~ /^::::sequence::::/) {
1881     ## ISSUE: Is "sequence<unknowntype>" conforming?
1882     } else {
1883     my $def = $resolve->($type, $item->{scope});
1884     if ($def and
1885     $def->{allow_null}) {
1886     #
1887     } else {
1888     ## ISSUE: It is not explicitly specified that ScopedName
1889     ## must refer a defined type.
1890     $onerror->(type => 'not boxable type',
1891     level => 'm',
1892     node => $item->{node},
1893     text => $item->{node}->type_text);
1894     }
1895     }
1896 wakaba 1.9 }
1897     }
1898    
1899     my $qname = $item->{node}->qualified_name;
1900     if ($defined_qnames->{$qname}) {
1901     ## NOTE: "The identifier of a definition MUST be locally unique":
1902     ## Redundant with another requirement below.
1903    
1904 wakaba 1.10 ## ISSUE: |interface x; interface x {};| is non-conforming
1905     ## according to the current spec text.
1906    
1907     ## ISSUE: |interface x;| with no |interface x {};| is conforming
1908     ## according to the current spec text.
1909    
1910 wakaba 1.9 $onerror->(type => 'duplicate qname',
1911     level => 'm',
1912     node => $item->{node});
1913 wakaba 1.16
1914     if ($item->{node}->isa ('Whatpm::WebIDL::Interface') and
1915     $defined_qnames->{$qname}->{node}->isa
1916     ('Whatpm::WebIDL::Interface') and
1917     $defined_qnames->{$qname}->{node}->is_forward_declaration) {
1918     $defined_qnames->{$qname} = $item;
1919     }
1920 wakaba 1.9 } else {
1921 wakaba 1.10 $defined_qnames->{$qname} = $item;
1922     ## NOTE: This flag must be turned on AFTER inheritance check is
1923     ## performed (c.f. |interface x : x {};|).
1924 wakaba 1.9 }
1925     } elsif ($item->{node}->isa ('Whatpm::WebIDL::InterfaceMember')) {
1926     if ($item->{node}->isa ('Whatpm::WebIDL::Operation')) {
1927 wakaba 1.12 ## NOTE: Arguments
1928 wakaba 1.16 my $has_arg_xattr = {};
1929 wakaba 1.9 unshift @$items,
1930 wakaba 1.16 map { {node => $_, scope => $item->{scope},
1931     has_arg_xattr => $has_arg_xattr} }
1932 wakaba 1.9 @{$item->{node}->{child_nodes}};
1933 wakaba 1.10 } else {
1934     my $name = $item->{node}->node_name;
1935     if ($item->{defined_members}->{$name}) {
1936 wakaba 1.13 $onerror->(type => 'duplicate member',
1937 wakaba 1.10 level => 'm',
1938     node => $item->{node});
1939     ## ISSUE: Whether the example below is conforming or not
1940     ## is ambigious:
1941     ## |interface a { attribute any b; any b (); };|
1942     } else {
1943     $item->{defined_members}->{$name} = 1;
1944     }
1945 wakaba 1.12 }
1946 wakaba 1.11
1947 wakaba 1.12 if ($item->{node}->isa ('Whatpm::WebIDL::Attribute') or
1948     $item->{node}->isa ('Whatpm::WebIDL::Operation')) {
1949     my $type = $item->{node}->type;
1950     if ($type =~ /\A::[^:]+::\z/) { # note that sequence<> not allowed
1951     #
1952     } else { # scoped name
1953     my $def = $resolve->($type, $item->{scope});
1954    
1955     unless ($def and
1956     ($def->{node}->isa ('Whatpm::WebIDL::Interface') or
1957     $def->{node}->isa ('Whatpm::WebIDL::Typedef') or
1958     $def->{node}->isa ('Whatpm::WebIDL::Valuetype'))) {
1959     $onerror->(type => 'type not defined',
1960     level => 'm',
1961     node => $item->{node},
1962     text => $item->{node}->type_text);
1963     }
1964 wakaba 1.11 }
1965 wakaba 1.12
1966     for (@{$item->{node}->{raises} or []}, # for operations
1967     @{$item->{node}->{getraises} or []}, # for attributes
1968     @{$item->{node}->{setraises} or []}) { # for attributes
1969     my $def = $resolve->($_, $item->{scope});
1970    
1971     unless ($def and
1972     $def->{node}->isa ('Whatpm::WebIDL::Exception')) {
1973     $onerror->(type => 'exception not defined',
1974     level => 'm',
1975     node => $item->{node},
1976     text => $_);
1977     }
1978     }
1979    
1980     ## ISSUE: readonly setraises is not disallowed
1981     ## ISSUE: raises (x,x) and raises (x,::x) and etc. are not disallowed
1982     } elsif ($item->{node}->isa ('Whatpm::WebIDL::Const')) {
1983     $check_const_value->($item);
1984 wakaba 1.9 }
1985 wakaba 1.13 } elsif ($item->{node}->isa ('Whatpm::WebIDL::Argument') or
1986     $item->{node}->isa ('Whatpm::WebIDL::ExceptionMember')) {
1987 wakaba 1.16 if ($item->{has_arg_xattr}->{variadic} and
1988     $item->{node}->isa ('Whatpm::WebIDL::Argument')) {
1989     $onerror->(type => 'argument after variadic',
1990     node => $item->{node},
1991     level => $levels->{fact});
1992     }
1993    
1994 wakaba 1.15 ## ISSUE: No uniqueness constraints for arguments in an operation,
1995     ## so we don't check it for arguments.
1996    
1997     ## ISSUE: For extended attributes, semantics of the argument
1998     ## (e.g. the identifier is given by the |identifier| in the |Argument|)
1999     ## is not explicitly defined. In addition, no uniqueness constraint
2000     ## is defined, so we don't check it for arguments.
2001 wakaba 1.12
2002 wakaba 1.13 my $name = $item->{node}->node_name;
2003     if ($item->{defined_members}->{$name}) {
2004     $onerror->(type => 'duplicate member',
2005     level => 'm',
2006     node => $item->{node});
2007     } else {
2008     $item->{defined_members}->{$name} = 1;
2009     }
2010    
2011 wakaba 1.12 my $type = $item->{node}->type;
2012     if ($type =~ /\A::[^:]+::\z/) { # note that sequence<> not allowed
2013     #
2014     } else { # scoped name
2015     my $def = $resolve->($type, $item->{scope});
2016    
2017     unless ($def and
2018     ($def->{node}->isa ('Whatpm::WebIDL::Interface') or
2019     $def->{node}->isa ('Whatpm::WebIDL::Typedef') or
2020     $def->{node}->isa ('Whatpm::WebIDL::Valuetype'))) {
2021     $onerror->(type => 'type not defined',
2022     level => 'm',
2023     node => $item->{node},
2024     text => $item->{node}->type_text);
2025     }
2026 wakaba 1.13 }
2027 wakaba 1.9 }
2028    
2029     my $xattrs = $item->{node}->{xattrs} || [];
2030 wakaba 1.16 my $has_xattr;
2031 wakaba 1.15 X: for my $xattr (@$xattrs) {
2032 wakaba 1.9 my $xattr_name = $xattr->node_name;
2033 wakaba 1.15 my $xattr_def = $xattr_defs->{$xattr_name};
2034    
2035     unless ($xattr_def) {
2036     $onerror->(type => 'unknown xattr',
2037     text => $xattr_name,
2038     node => $xattr,
2039     level => $levels->{uncertain});
2040     next X;
2041     }
2042    
2043     A: {
2044     for my $cls (keys %{$xattr_def->{allowed_type} or {}}) {
2045     if ($item->{node}->isa ('Whatpm::WebIDL::' . $cls)) {
2046 wakaba 1.16 if ($cls eq 'Interface' and
2047     $item->{node}->is_forward_declaration) {
2048     #
2049     } else {
2050     last A;
2051     }
2052 wakaba 1.15 }
2053     }
2054    
2055     $onerror->(type => 'xattr not applicable',
2056     text => $xattr_name,
2057 wakaba 1.16 node => $xattr,
2058     level => $levels->{fact});
2059 wakaba 1.15 next X;
2060     } # A
2061    
2062 wakaba 1.16 if ($has_xattr->{$xattr_name} and not $xattr_def->{allow_multiple}) {
2063     ## ISSUE: Whether multiple extended attributes with the same
2064     ## name are allowed is not explicitly specified in the spec.
2065     ## It is, however, specified that some extended attributes may
2066     ## be specified more than once.
2067     $onerror->(type => 'duplicate xattr',
2068     text => $xattr_name,
2069     node => $xattr,
2070     level => $levels->{warn});
2071     }
2072     $has_xattr->{$xattr_name} = 1;
2073    
2074 wakaba 1.15 if (not $xattr_def->{allow_id} and defined $xattr->value) {
2075     $onerror->(type => 'xattr id not allowed',
2076     text => $xattr_name,
2077     node => $xattr,
2078     level => $levels->{must});
2079     }
2080     if (not $xattr_def->{allow_arglist} and $xattr->has_argument_list) {
2081     $onerror->(type => 'xattr arglist not allowed',
2082     text => $xattr_name,
2083     node => $xattr,
2084     level => $levels->{must});
2085     }
2086    
2087     $xattr_def->{check}->($self, $onerror, $levels, $items, $item,
2088 wakaba 1.16 $xattr, $xattr_name, $resolve, $constructors);
2089     }
2090 wakaba 1.15
2091 wakaba 1.16 if (not $has_xattr->{NoInterfaceObject} and
2092     (($item->{node}->isa ('Whatpm::WebIDL::Interface') and
2093     not $item->{node}->is_forward_declaration) or
2094     $item->{node}->isa ('Whatpm::WebIDL::Exception'))) {
2095     my $id = $item->{node}->node_name;
2096     if ($constructors->{$id} and
2097     $constructors->{$id}->{is_named_constructor}) {
2098     $onerror->(type => 'duplicate constructor name',
2099     value => $id,
2100     node => $constructors->{$id}->{is_named_constructor},
2101     level => $levels->{must});
2102     } else {
2103     ## NOTE: Duplication is not checked in this case, since any
2104     ## duplication of interface/exception identifiers are detected
2105     ## by the parser.
2106     $constructors->{$id} = {node => $item->{node}};
2107 wakaba 1.9 }
2108 wakaba 1.8 }
2109     }
2110     } # check
2111    
2112     package Whatpm::WebIDL::Definition;
2113     push our @ISA, 'Whatpm::WebIDL::Node';
2114    
2115     sub new ($$) {
2116     return bless {child_nodes => [], node_name => ''.$_[1]}, $_[0];
2117     } # new
2118    
2119 wakaba 1.4 sub set_extended_attribute_node ($$) {
2120     my $self = shift;
2121     ## TODO: check
2122     push @{$self->{xattrs} ||= []}, shift;
2123     } # set_extended_attribute_node
2124    
2125     sub _xattrs_text ($) {
2126     my $self = shift;
2127    
2128     unless ($self->{xattrs} and
2129     @{$self->{xattrs}}) {
2130     return '';
2131     }
2132    
2133     my $r = '[';
2134     $r .= join ', ', map {$_->idl_text} @{$self->{xattrs}};
2135     $r .= ']';
2136     return $r;
2137     } # _xattrs_text
2138    
2139 wakaba 1.9 sub qualified_name ($) {
2140     my $self = shift;
2141    
2142     my $parent = $self->{parent_node};
2143     if ($parent and $parent->isa ('Whatpm::WebIDL::Definition')) {
2144     return $parent->qualified_name . '::' . $self->{node_name};
2145     } else {
2146     return '::' . $self->{node_name};
2147     }
2148     } # qualified_name
2149    
2150 wakaba 1.1 sub type ($;$) {
2151     if (@_ > 1) {
2152     if (defined $_[1]) {
2153     $_[0]->{type} = $_[1];
2154     } else {
2155 wakaba 1.11 $_[0]->{type} = '::any::';
2156 wakaba 1.1 }
2157     }
2158     return $_[0]->{type};
2159     } # type
2160    
2161 wakaba 1.6 my $serialize_type;
2162     $serialize_type = sub ($) {
2163     my $type = shift;
2164 wakaba 1.11 if ($type =~ s/^::::sequence:::://) {
2165     return 'sequence<' . $serialize_type->($type) . '>';
2166     } elsif ($type =~ /\A::([^:]+)::\z/) {
2167     return $1;
2168 wakaba 1.6 } else {
2169 wakaba 1.13 $type =~ s/::DOMString::::\z/::DOMString/;
2170 wakaba 1.10 return $type; ## TODO: escape identifiers...
2171 wakaba 1.6 }
2172     }; # $serialize_type
2173    
2174 wakaba 1.1 sub type_text ($) {
2175     my $type = $_[0]->{type};
2176     return undef unless defined $type;
2177 wakaba 1.6
2178     return $serialize_type->($type);
2179 wakaba 1.1 } # type_text
2180 wakaba 1.6
2181 wakaba 1.1 package Whatpm::WebIDL::Module;
2182     push our @ISA, 'Whatpm::WebIDL::Definition';
2183    
2184     sub idl_text ($) {
2185 wakaba 1.4 my $self = shift;
2186     my $r = $self->_xattrs_text;
2187     $r .= ' ' if length $r;
2188 wakaba 1.8 $r .= 'module ' . $self->node_name . " {\x0A\x0A"; ## TODO: escape
2189 wakaba 1.4 for (@{$self->{child_nodes}}) {
2190 wakaba 1.1 $r .= $_->idl_text;
2191     }
2192     $r .= "\x0A};\x0A";
2193     return $r;
2194     } # idl_text
2195    
2196     package Whatpm::WebIDL::Interface;
2197     push our @ISA, 'Whatpm::WebIDL::Definition';
2198    
2199     sub new ($$) {
2200     my $self = shift->SUPER::new (@_);
2201     $self->{inheritances} = [];
2202     return $self;
2203     } # new
2204    
2205     sub append_inheritance ($$) {
2206     my $self = shift;
2207     my $scoped_name = shift;
2208     push @{$self->{inheritances}}, $scoped_name;
2209     } # append_inheritance
2210    
2211     sub idl_text ($) {
2212 wakaba 1.3 my $self = shift;
2213 wakaba 1.4 my $r = $self->_xattrs_text;
2214     $r .= ' ' if length $r;
2215 wakaba 1.9 $r .= 'interface ' . $self->node_name;
2216 wakaba 1.8
2217     if ($self->{is_forward_declaration}) {
2218     $r .= ";\x0A";
2219     return $r;
2220     }
2221    
2222 wakaba 1.3 if (@{$self->{inheritances}}) {
2223     $r .= ' : '; ## TODO: ...
2224 wakaba 1.10 $r .= join ', ', map {$serialize_type->($_)} @{$self->{inheritances}};
2225 wakaba 1.3 }
2226     $r .= " {\x0A"; ## TODO: escape
2227     for (@{$self->{child_nodes}}) {
2228 wakaba 1.1 $r .= ' ' . $_->idl_text;
2229     }
2230     $r .= "};\x0A";
2231     return $r;
2232     } # idl_text
2233    
2234 wakaba 1.8 sub is_forward_declaration ($;$) {
2235     if (@_ > 1) {
2236     if ($_[1]) {
2237     $_[0]->{is_forward_declaration} = 1;
2238     } else {
2239     delete $_[0]->{is_forward_declaration};
2240     }
2241     }
2242    
2243     return $_[0]->{is_forward_declaration};
2244     } # is_forward_declaration
2245    
2246 wakaba 1.1 package Whatpm::WebIDL::Exception;
2247     push our @ISA, 'Whatpm::WebIDL::Definition';
2248    
2249 wakaba 1.3 sub idl_text ($) {
2250 wakaba 1.4 my $self = shift;
2251     my $r = $self->_xattrs_text;
2252     $r .= ' ' if length $r;
2253 wakaba 1.9 $r .= 'exception ' . $self->node_name . " {\x0A"; ## TODO: escape
2254 wakaba 1.4 for (@{$self->{child_nodes}}) {
2255 wakaba 1.3 $r .= ' ' . $_->idl_text;
2256     }
2257     $r .= "};\x0A";
2258     return $r;
2259     } # idl_text
2260    
2261 wakaba 1.1 package Whatpm::WebIDL::Typedef;
2262     push our @ISA, 'Whatpm::WebIDL::Definition';
2263    
2264     sub new ($$) {
2265     my $self = shift->SUPER::new (@_);
2266 wakaba 1.11 $self->{type} = '::any::';
2267 wakaba 1.1 return $self;
2268     } # new
2269    
2270     sub idl_text ($) {
2271 wakaba 1.4 my $self = shift;
2272     my $r = $self->_xattrs_text;
2273     $r .= ' ' if length $r;
2274 wakaba 1.13 my $node_name = $self->node_name;
2275     $node_name = 'DOMString' if $node_name eq '::DOMString::';
2276 wakaba 1.1 ## TODO: escape
2277 wakaba 1.13 $r .= 'typedef ' . $self->type_text . ' ' . $node_name . ";\x0A";
2278 wakaba 1.4 return $r;
2279 wakaba 1.1 } # idl_text
2280    
2281     package Whatpm::WebIDL::Valuetype;
2282     push our @ISA, 'Whatpm::WebIDL::Definition';
2283    
2284     sub new ($$) {
2285     my $self = shift->SUPER::new (@_);
2286 wakaba 1.11 $self->{type} = '::boolean::';
2287 wakaba 1.1 return $self;
2288     } # new
2289    
2290     sub idl_text ($) {
2291 wakaba 1.4 my $self = shift;
2292     my $r = $self->_xattrs_text;
2293     $r .= ' ' if length $r;
2294 wakaba 1.14 my $name = $self->node_name;
2295     $name = 'DOMString' if $name eq '::DOMString::';
2296 wakaba 1.1 ## TODO: escape
2297 wakaba 1.14 $r .= 'valuetype ' . $name . ' ' . $self->type_text . ";\x0A";
2298 wakaba 1.4 return $r;
2299 wakaba 1.1 } # idl_text
2300    
2301     package Whatpm::WebIDL::InterfaceMember;
2302 wakaba 1.8 push our @ISA, 'Whatpm::WebIDL::Node';
2303 wakaba 1.1
2304     sub new ($$) {
2305     return bless {node_name => ''.$_[1]}, $_[0];
2306     } # new
2307    
2308 wakaba 1.8 sub child_nodes ($) { return [] }
2309    
2310 wakaba 1.4 *_xattrs_text = \&Whatpm::WebIDL::Definition::_xattrs_text;
2311    
2312     *set_extended_attribute_node = \&Whatpm::WebIDL::Definition::set_extended_attribute_node;
2313    
2314 wakaba 1.1 *type = \&Whatpm::WebIDL::Definition::type;
2315    
2316     *type_text = \&Whatpm::WebIDL::Definition::type_text;
2317    
2318     package Whatpm::WebIDL::Const;
2319     push our @ISA, 'Whatpm::WebIDL::Definition', 'Whatpm::WebIDL::InterfaceMember';
2320    
2321     sub new ($$) {
2322     my $self = shift->SUPER::new (@_); # Definition->new should be called
2323 wakaba 1.11 $self->{type} = '::boolean::';
2324 wakaba 1.1 $self->{value} = ['FALSE'];
2325     return $self;
2326     } # new
2327    
2328     sub value ($;$) {
2329     if (@_ > 1) {
2330 wakaba 1.11 if (defined $_[1]) {
2331     $_[0]->{value} = $_[1];
2332     } else {
2333     $_[0]->{value} = ['FALSE'];
2334     }
2335 wakaba 1.1 }
2336    
2337     return $_[0]->{value};
2338     } # value
2339    
2340     sub value_text ($) {
2341     my $value = $_[0]->{value};
2342    
2343     if ($value->[0] eq 'TRUE' or $value->[0] eq 'FALSE') {
2344     return $value->[0];
2345     } elsif ($value->[0] eq 'integer' or $value->[0] eq 'float') {
2346     ## TODO: format
2347     return $value->[1];
2348     } else {
2349     return undef;
2350     }
2351     } # value_text
2352    
2353     sub idl_text ($) {
2354 wakaba 1.4 my $self = shift;
2355     my $r = $self->_xattrs_text;
2356     $r .= ' ' if length $r;
2357     $r .= 'const ' . $self->type_text . ' ' . $self->node_name . ' = ' . $self->value_text . ";\x0A"; ## TODO: escape
2358     return $r;
2359 wakaba 1.1 } # idl_text
2360    
2361     package Whatpm::WebIDL::Attribute;
2362     push our @ISA, 'Whatpm::WebIDL::InterfaceMember';
2363    
2364     sub new ($$) {
2365     my $self = shift->SUPER::new (@_);
2366 wakaba 1.11 $self->{type} = '::any::';
2367 wakaba 1.2 $self->{getraises} = [];
2368     $self->{setraises} = [];
2369 wakaba 1.1 return $self;
2370     } # new
2371    
2372 wakaba 1.2 sub append_getraises ($$) {
2373     ## TODO: error check, etc.
2374     push @{$_[0]->{getraises}}, $_[1];
2375     } # append_getraises
2376    
2377     sub append_setraises ($$) {
2378     ## TODO: error check, etc.
2379     push @{$_[0]->{setraises}}, $_[1];
2380     } # append_setraises
2381    
2382 wakaba 1.1 sub readonly ($;$) {
2383     if (@_ > 1) {
2384     $_[0]->{readonly} = $_[1];
2385     }
2386    
2387     return $_[0]->{readonly};
2388     } # readonly
2389    
2390     sub idl_text ($) {
2391 wakaba 1.2 my $self = shift;
2392 wakaba 1.4 my $r = $self->_xattrs_text;
2393     $r .= ' ' if length $r;
2394     $r .= ($self->readonly ? 'readonly ' : '') . 'attribute ' . $self->type_text . ' ' . $self->node_name;
2395 wakaba 1.1 ## TODO: escape
2396 wakaba 1.2 if (@{$self->{getraises}}) {
2397     $r .= ' getraises (';
2398     ## todo: ...
2399 wakaba 1.12 $r .= join ', ', map {$serialize_type->($_)} @{$self->{getraises}};
2400 wakaba 1.2 $r .= ')';
2401     }
2402     if (@{$self->{setraises}}) {
2403     $r .= ' setraises (';
2404     ## todo: ...
2405 wakaba 1.12 $r .= join ', ', map {$serialize_type->($_)} @{$self->{setraises}};
2406 wakaba 1.2 $r .= ')';
2407     }
2408     $r .= ";\x0A";
2409     return $r;
2410 wakaba 1.1 } # idl_text
2411    
2412     package Whatpm::WebIDL::Operation;
2413     push our @ISA, 'Whatpm::WebIDL::InterfaceMember';
2414    
2415     sub new ($$) {
2416     my $self = shift->SUPER::new (@_);
2417 wakaba 1.11 $self->{type} = '::any::';
2418 wakaba 1.1 $self->{child_nodes} = [];
2419 wakaba 1.2 $self->{raises} = [];
2420 wakaba 1.1 return $self;
2421     } # new
2422    
2423 wakaba 1.2 sub append_raises ($$) {
2424     ## TODO: error check, etc.
2425     push @{$_[0]->{raises}}, $_[1];
2426     } # append_raises
2427    
2428 wakaba 1.1 sub idl_text ($) {
2429 wakaba 1.2 my $self = shift;
2430 wakaba 1.4 my $r = $self->_xattrs_text;
2431     $r .= ' ' if length $r;
2432     $r .= $self->type_text . ' ' . $self->node_name . ' ('; ## TODO: escape
2433 wakaba 1.2 $r .= join ', ', map {$_->idl_text} @{$self->{child_nodes}};
2434     $r .= ')';
2435     if (@{$self->{raises}}) {
2436     $r .= ' raises (';
2437     ## todo: ...
2438 wakaba 1.12 $r .= join ', ', map {$serialize_type->($_)} @{$self->{raises}};
2439 wakaba 1.2 $r .= ')';
2440     }
2441 wakaba 1.1 $r .= ";\x0A";
2442     return $r;
2443     } # idl_text
2444    
2445     package Whatpm::WebIDL::Argument;
2446 wakaba 1.8 push our @ISA, 'Whatpm::WebIDL::Node';
2447 wakaba 1.1
2448     sub new ($$) {
2449 wakaba 1.11 return bless {node_name => ''.$_[1], type => '::any::'}, $_[0];
2450 wakaba 1.1 } # new
2451    
2452     sub idl_text ($) {
2453 wakaba 1.4 my $self = shift;
2454     my $r = $self->_xattrs_text;
2455     $r .= ' ' if length $r;
2456     $r .= 'in ' . $self->type_text . ' ' . $self->node_name; ## TODO: escape
2457     return $r;
2458 wakaba 1.3 } # idl_text
2459    
2460 wakaba 1.4 *_xattrs_text = \&Whatpm::WebIDL::Definition::_xattrs_text;
2461    
2462     *set_extended_attribute_node = \&Whatpm::WebIDL::Definition::set_extended_attribute_node;
2463    
2464 wakaba 1.3 *type = \&Whatpm::WebIDL::Definition::type;
2465    
2466     *type_text = \&Whatpm::WebIDL::Definition::type_text;
2467    
2468     package Whatpm::WebIDL::ExceptionMember;
2469 wakaba 1.8 push our @ISA, 'Whatpm::WebIDL::Node';
2470 wakaba 1.3
2471     sub new ($$) {
2472 wakaba 1.11 return bless {node_name => ''.$_[1], type => '::any::'}, $_[0];
2473 wakaba 1.3 } # new
2474    
2475     sub idl_text ($) {
2476 wakaba 1.4 my $self = shift;
2477     my $r = $self->_xattrs_text;
2478     $r .= ' ' if length $r;
2479     $r .= $self->type_text . ' ' . $self->node_name . ";\x0A"; ## TODO: escape
2480     return $r;
2481 wakaba 1.1 } # idl_text
2482    
2483 wakaba 1.4 *_xattrs_text = \&Whatpm::WebIDL::Definition::_xattrs_text;
2484    
2485     *set_extended_attribute_node = \&Whatpm::WebIDL::Definition::set_extended_attribute_node;
2486    
2487 wakaba 1.1 *type = \&Whatpm::WebIDL::Definition::type;
2488    
2489     *type_text = \&Whatpm::WebIDL::Definition::type_text;
2490 wakaba 1.9
2491 wakaba 1.4 package Whatpm::WebIDL::ExtendedAttribute;
2492 wakaba 1.8 push our @ISA, 'Whatpm::WebIDL::Node';
2493 wakaba 1.4
2494     sub new ($$) {
2495     return bless {child_nodes => [], node_name => ''.$_[1]};
2496     } # new
2497    
2498 wakaba 1.15 sub has_argument_list ($;$) {
2499     if (@_ > 1) {
2500     if ($_[1]) {
2501     $_[0]->{has_argument_list} = 1;
2502     } else {
2503     delete $_[0]->{has_argument_list};
2504     }
2505     }
2506    
2507     return ($_[0]->{has_argument_list} or scalar @{$_[0]->{child_nodes}});
2508     } # has_argument_list
2509    
2510 wakaba 1.4 sub idl_text ($) {
2511     my $self = shift;
2512     my $r = $self->node_name; ## TODO:] esceape
2513     if (defined $self->{value}) {
2514     $r .= '=' . $self->{value}; ## TODO: escape
2515     }
2516 wakaba 1.15 if ($self->has_argument_list) {
2517 wakaba 1.4 $r .= ' (';
2518     $r .= join ', ', map {$_->idl_text} @{$self->{child_nodes}};
2519     $r .= ')';
2520     }
2521     return $r;
2522     } # idl_text
2523    
2524     sub value ($;$) {
2525     if (@_ > 1) {
2526     if (defined $_[1]) {
2527     $_[0]->{value} = ''.$_[1];
2528     } else {
2529     delete $_[0]->{value};
2530     }
2531     }
2532    
2533     return $_[0]->{value};
2534     } # value
2535 wakaba 1.1
2536     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24