/[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.10 - (hide annotations) (download)
Sat Aug 2 12:51:52 2008 UTC (16 years, 3 months ago) by wakaba
Branch: MAIN
Changes since 1.9: +97 -28 lines
++ whatpm/t/ChangeLog	2 Aug 2008 12:51:15 -0000
	* WebIDL.t: Check whether |text| argument of the error
	is correct or not.

2008-08-02  Wakaba  <wakaba@suika.fam.cx>

++ whatpm/t/webidl/ChangeLog	2 Aug 2008 12:51:46 -0000
	* webidl-interface.dat: More test data for interface inheritances
	and interface member identifier duplications.

2008-08-02  Wakaba  <wakaba@suika.fam.cx>

++ whatpm/Whatpm/ChangeLog	2 Aug 2008 12:50:36 -0000
	* WebIDL.pm ($get_scoped_name): Now scoped names are stored
	in its stringified format ("scoped name" as defined in the
	spec).  Note that future version of this module should not use
	array references for type values and the |type_text| attribute
	should be made obsolete.
	(parse_char_string): Unescape attribute names.
	(check): Support for checking of whether inherited interfaces
	are actually defined or not.  Support for checking of whether
	interface member identifiers are duplicated or not.
	($serialize_type): Scoped names are returned as is.  A future
	version of this code should escape identifiers other than "DOMString",
	otherwise the idl_text would be non-conforming.

2008-08-02  Wakaba  <wakaba@suika.fam.cx>

1 wakaba 1.1 package Whatpm::WebIDL;
2     use strict;
3    
4     package Whatpm::WebIDL::Parser;
5    
6     my $integer = qr/-?0([0-7]*|[Xx][0-9A-Fa-f]+)|[1-9][0-9]*/;
7     my $float = qr/-?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+/;
8     my $identifier = qr/[A-Z_a-z][0-9A-Z_a-z]*/;
9     my $whitespace = qr<[\t\n\r ]+|[\t\n\r ]*((//.*|/\*.*?\*/)[\t\n\r ]*)+>;
10    
11     sub new ($) {
12     my $self = bless {
13     must_level => 'm',
14     should_level => 's',
15     warn_level => 'w',
16     info_level => 'i',
17     }, $_[0];
18     return $self;
19     } # new
20    
21     sub parse_char_string ($$;$) {
22     my $self = shift;
23     my $s = ref $_[0] ? $_[0] : \($_[0]);
24    
25     my $defs = Whatpm::WebIDL::Definitions->new;
26 wakaba 1.9 $defs->set_user_data (manakai_source_line => 1);
27     $defs->set_user_data (manakai_source_column => 1);
28 wakaba 1.1
29     pos ($$s) = 0;
30     my $line = 1;
31     my $column = 0;
32    
33     my $get_next_token = sub {
34     if (length $$s <= pos $$s) {
35     return {type => 'eof'};
36     }
37    
38     while ($$s =~ /\G($whitespace)/gc) {
39     my $v = $1;
40     while ($v =~ s/^[^\x0D\x0A]*(?>\x0D\x0A?|\x0A)//) {
41     $line++;
42     $column = 0;
43     }
44     $column += length $v;
45     }
46    
47     if (length $$s <= pos $$s) {
48     return {type => 'eof'};
49     }
50    
51     ## ISSUE: "must"s in "A. IDL grammer" are not "MUST"s.
52    
53     if ($$s =~ /\G($identifier)/gc) {
54     my $v = $1;
55     $column += length $v;
56     if ({
57     module => 1, interface => 1, exception => 1, typedef => 1,
58     valuetype => 1, DOMString => 1, sequence => 1, unsigned => 1,
59     short => 1, const => 1, TRUE => 1, FALSE => 1, readonly => 1,
60     attribute => 1, getraises => 1, setraises => 1, raises => 1, in => 1,
61     any => 1, boolean => 1, octet => 1, float => 1, Object => 1,
62     short => 1, long => 1, void => 1,
63     }->{$v}) {
64     return {type => $v};
65     } else {
66     return {type => 'identifier', value => $v};
67     }
68     } elsif ($$s =~ /\G($float)/gc) { ## ISSUE: negative number
69     $column += length $1;
70     return {type => 'float', value => $1};
71     } elsif ($$s =~ /\G($integer)/gc) { ## ISSUE: negative nmber
72     $column += length $1;
73     return {type => 'integer', value => $1};
74     } elsif ($$s =~ /\G::/gcs) {
75     $column += 2;
76     return {type => '::'};
77     } elsif ($$s =~ /\G(.)/gcs) { ## NOTE: Should never be a newline char.
78     $column++;
79     return {type => $1};
80     } else {
81     die "get_next_token: non-possible case: " . substr ($$s, pos $$s, 20);
82     }
83     }; # $get_next_token
84    
85     my $state = 'before definitions';
86     my $token = $get_next_token->();
87     my $nest_level = 0;
88     my $next_state;
89     my $xattrs;
90 wakaba 1.4 my $prev_xattrs = [];
91 wakaba 1.1 my $last_xattr;
92     my $read_only;
93     my $current_type;
94     my @current = ($defs);
95    
96     my $_onerror = $_[1] || sub {
97     my %opt = @_;
98     my $r = 'Line ' . $opt{line} . ' column ' . $opt{column} . ': ';
99    
100     if ($opt{token}) {
101     $r .= 'Token ' . (defined $opt{token}->{value} ? $opt{token}->{value} : $opt{token}->{type}) . ': ';
102     }
103    
104     $r .= $opt{type} . ';' . $opt{level};
105    
106     warn $r . "\n";
107     }; # $_onerror
108     my $onerror = sub {
109     $_onerror->(line => $line, column => $column, token => $token, @_);
110     }; # $onerror
111    
112     my $get_scoped_name = sub {
113 wakaba 1.10 ## NOTE: Convert a |ScopedName| into a "scoped name".
114    
115 wakaba 1.1 my $name = [];
116    
117     ## NOTE: "DOMString" is not a scoped name, while "::DOMString"
118     ## and "x::DOMString" are.
119    
120     if ($token->{type} eq 'identifier') {
121 wakaba 1.10 my $identifier = $token->{value};
122     $identifier =~ s/^_//;
123     push @$name, $identifier;
124 wakaba 1.1 $token = $get_next_token->();
125     while ($token->{type} eq '::') {
126 wakaba 1.2 $token = $get_next_token->();
127 wakaba 1.1 if ($token->{type} eq 'identifier') {
128 wakaba 1.10 my $identifier = $token->{value};
129     $identifier =~ s/^_//;
130     push @$name, $identifier;
131 wakaba 1.1 $token = $get_next_token->();
132     } elsif ($token->{type} eq 'DOMString') {
133 wakaba 1.10 push @$name, 'DOMString';
134 wakaba 1.1 $token = $get_next_token->();
135     last;
136     }
137     }
138     } elsif ($token->{type} eq '::') {
139     push @$name, '';
140     while ($token->{type} eq '::') {
141 wakaba 1.2 $token = $get_next_token->();
142 wakaba 1.1 if ($token->{type} eq 'identifier') {
143 wakaba 1.10 my $identifier = $token->{value};
144     $identifier =~ s/^_//;
145     push @$name, $identifier;
146 wakaba 1.1 $token = $get_next_token->();
147     } elsif ($token->{type} eq 'DOMString') {
148 wakaba 1.10 push @$name, 'DOMString';
149 wakaba 1.1 $token = $get_next_token->();
150     last;
151     } else {
152     last;
153     }
154     }
155    
156     if (@$name == 1) {
157     return undef;
158     }
159     } else {
160     # reconsume
161     return undef;
162     }
163 wakaba 1.10 return join '::', @$name;
164 wakaba 1.1 }; # $get_scoped_name
165    
166     my $get_type;
167     $get_type = sub {
168     my $r;
169     if ({
170     void => 1, any => 1, boolean => 1, octet => 1, float => 1,
171     DOMString => 1, Object => 1, short => 1,
172     }->{$token->{type}}) {
173     $r = ['::'.$token->{type}.'::'];
174     $token = $get_next_token->();
175     } elsif ($token->{type} eq 'unsigned') {
176     $token = $get_next_token->();
177     if ($token->{type} eq 'short') {
178     $r = ['::unsigned '.$token->{type}.'::'];
179     $token = $get_next_token->();
180     } elsif ($token->{type} eq 'long') {
181     $token = $get_next_token->();
182     if ($token->{type} eq 'long') {
183     $r = ['::unsigned long long::'];
184     $token = $get_next_token->();
185     } else {
186     $r = ['::unsigned long::'];
187     # reconsume
188     }
189     } else {
190     $onerror->(type => 'unsigned', level => $self->{must_level});
191     return undef;
192     }
193     } elsif ($token->{type} eq 'long') {
194     $token = $get_next_token->();
195     if ($token->{type} eq 'long') {
196     $r = ['::long long::'];
197     $token = $get_next_token->();
198     } else {
199     $r = ['::long::'];
200     # reconsume
201     }
202     } elsif ($token->{type} eq '::' or $token->{type} eq 'identifier') {
203     $r = $get_scoped_name->();
204     if (defined $r) {
205     # next token
206     } else { # "::" not followed by identifier or "DOMString"
207     $onerror->(type => 'scoped name', level => $self->{must_level});
208     return undef;
209     }
210     } elsif ($token->{type} eq 'sequence') {
211     $token = $get_next_token->();
212     if ($token->{type} eq '<') {
213     $token = $get_next_token->();
214     if ({
215     void => 1, any => 1, boolean => 1, octet => 1, float => 1,
216     DOMString => 1, Object => 1, short => 1, long => 1, unsigned => 1,
217     sequence => 1, '::' => 1, identifier => 1,
218     }->{$token->{type}}) {
219     my $type = $get_type->();
220     if (defined $type) {
221     if ($token->{type} eq '>') {
222     $r = ['::sequence::', $type];
223     $token = $get_next_token->();
224     } else {
225     $onerror->(type => 'sequence gt', level => $self->{must_level});
226     return undef;
227     }
228     } else {
229     # error reported
230     return undef;
231     }
232     } else {
233     $onerror->(type => 'sequence type', level => $self->{must_level});
234     return undef;
235     }
236     } else {
237     $onerror->(type => 'sequence lt', level => $self->{must_level});
238     return undef;
239     }
240     } else {
241     die "get_type: bad token: $token->{type}";
242     }
243    
244     return $r;
245     }; # $get_type
246    
247     while (1) {
248     if ($state eq 'before definitions') {
249 wakaba 1.4 $xattrs = [];
250 wakaba 1.1 if ($token->{type} eq '[') {
251     $token = $get_next_token->();
252     $state = 'before xattr';
253     $next_state = 'before def';
254     } elsif ({module => 1, interface => 1, exception => 1,
255     typedef => 1, valuetype => 1, const => 1}->{$token->{type}}) {
256     # reconsume
257     $state = 'before def';
258     } elsif ($token->{type} eq '}' and @current > 1) {
259     $token = $get_next_token->();
260 wakaba 1.5 $state = 'before block semicolon';
261 wakaba 1.1 } elsif ($token->{type} eq 'eof') {
262     last;
263     } else {
264     $onerror->(type => 'before definitions', level => 'm',
265     token => $token);
266     # reconsume
267     $state = 'ignore';
268     $nest_level = 0;
269     $next_state = 'before definitions';
270     }
271     } elsif ($state eq 'before xattr') {
272     if ($token->{type} eq 'identifier') {
273     ## TODO: _escape
274     ## ISSUE: Duplicate attributes
275     ## ISSUE: Unkown attributes
276 wakaba 1.4 push @current, Whatpm::WebIDL::ExtendedAttribute->new ($token->{value});
277 wakaba 1.9 $current[-1]->set_user_data (manakai_source_line => $line);
278     $current[-1]->set_user_data (manakai_source_column => $column);
279 wakaba 1.4 push @$xattrs, $current[-1];
280 wakaba 1.1 $token = $get_next_token->();
281     $state = 'after xattr';
282     } else {
283     $onerror->(type => 'before xattr', level => 'm', token => $token);
284     # reconsume
285     $state = 'ignore';
286     $nest_level = 0;
287 wakaba 1.4 $next_state = 'before definitions'; ## TODO:
288 wakaba 1.1 }
289     } elsif ($state eq 'after xattr') {
290     if ($token->{type} eq '=') {
291     $token = $get_next_token->();
292     $state = 'before xattrarg';
293 wakaba 1.4 } elsif ($token->{type} eq '(') {
294     $token = $get_next_token->();
295     if ($token->{type} eq ')') {
296     $token = $get_next_token->();
297     push @$prev_xattrs, $xattrs;
298     $state = 'after xattrarg';
299     } else {
300     push @$prev_xattrs, $xattrs;
301     # reconsume
302     $state = 'before argument';
303     }
304 wakaba 1.1 } else {
305 wakaba 1.4 push @$prev_xattrs, $xattrs;
306 wakaba 1.1 # reconsume
307     $state = 'after xattrarg';
308     }
309     } elsif ($state eq 'before xattrarg') {
310     if ($token->{type} eq 'identifier') {
311 wakaba 1.9 my $identifier = $token->{value};
312     $identifier =~ s/^_//;
313     $current[-1]->value ($identifier);
314 wakaba 1.1 $token = $get_next_token->();
315 wakaba 1.4 if ($token->{type} eq '(') {
316     $token = $get_next_token->();
317     if ($token->{type} eq ')') {
318     push @$prev_xattrs, $xattrs;
319     $token = $get_next_token->();
320     $state = 'after xattrarg';
321     } else {
322     push @$prev_xattrs, $xattrs;
323     # reconsume
324     $state = 'before argument';
325     }
326     } else {
327     push @$prev_xattrs, $xattrs;
328     # reconsume
329     $state = 'after xattrarg';
330     }
331 wakaba 1.1 } else {
332     $onerror->(type => 'after xattrarg', level => 'm', token => $token);
333     # reconsume
334     $state = 'ignore';
335     $nest_level = 0;
336     $next_state = 'before definitions';
337     }
338     } elsif ($state eq 'after xattrarg') {
339 wakaba 1.4 pop @current; # xattr
340     $xattrs = pop @$prev_xattrs;
341 wakaba 1.1 if ($token->{type} eq ',') {
342     $token = $get_next_token->();
343     $state = 'before xattr';
344     } elsif ($token->{type} eq ']') {
345     $token = $get_next_token->();
346 wakaba 1.4 if ($current[-1]->isa ('Whatpm::WebIDL::Definitions') or
347     $current[-1]->isa ('Whatpm::WebIDL::Module')) {
348 wakaba 1.9 $state = 'before def';
349 wakaba 1.4 } elsif ($current[-1]->isa ('Whatpm::WebIDL::Interface')) {
350     $state = 'before interface member';
351     } elsif ($current[-1]->isa ('Whatpm::WebIDL::Exception')) {
352     $state = 'before exception member';
353     } elsif ($current[-1]->isa ('Whatpm::WebIDL::Operation') or
354     $current[-1]->isa ('Whatpm::WebIDL::ExtendedAttribute')) {
355     $state = 'before argument in';
356     } else {
357     die "$0: Unknown xattr context: " . ref $current[-1];
358     }
359 wakaba 1.1 } else {
360     $onerror->(type => 'after xattr', level => 'm', token => $token);
361     # reconsume
362     $state = 'ignore';
363     $nest_level = 0;
364 wakaba 1.4 $next_state = 'before definitions'; ## TODO:
365 wakaba 1.1 }
366     } elsif ($state eq 'before def') {
367     if ($token->{type} eq 'module') {
368     $token = $get_next_token->();
369     if ($token->{type} eq 'identifier') {
370 wakaba 1.9 my $identifier = $token->{value};
371     $identifier =~ s/^_//;
372     push @current, Whatpm::WebIDL::Module->new ($identifier);
373     $current[-1]->set_user_data (manakai_source_line => $line);
374     $current[-1]->set_user_data (manakai_source_column => $column);
375 wakaba 1.4 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
376 wakaba 1.1 $token = $get_next_token->();
377     $state = 'before module block';
378     next;
379     } else {
380     $onerror->(type => 'module identifier',
381     level => $self->{must_level});
382     #
383     }
384     } elsif ($token->{type} eq 'interface') {
385     $token = $get_next_token->();
386     if ($token->{type} eq 'identifier') {
387 wakaba 1.9 my $identifier = $token->{value};
388     $identifier =~ s/^_//;
389     push @current, Whatpm::WebIDL::Interface->new ($identifier);
390 wakaba 1.8 $current[-1]->set_user_data (manakai_source_line => $line);
391     $current[-1]->set_user_data (manakai_source_column => $column);
392 wakaba 1.4 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
393 wakaba 1.1 $token = $get_next_token->();
394     $state = 'before interface inheritance';
395     next;
396     } else {
397     $onerror->(type => 'interface identifier',
398     level => $self->{must_level});
399     #
400     }
401     } elsif ($token->{type} eq 'exception') {
402     $token = $get_next_token->();
403     if ($token->{type} eq 'identifier') {
404 wakaba 1.9 my $identifier = $token->{value};
405     $identifier =~ s/^_//;
406     push @current, Whatpm::WebIDL::Exception->new ($identifier);
407     $current[-1]->set_user_data (manakai_source_line => $line);
408     $current[-1]->set_user_data (manakai_source_column => $column);
409 wakaba 1.4 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
410 wakaba 1.1 $token = $get_next_token->();
411     $state = 'before exception block';
412     next;
413     } else {
414     $onerror->(type => 'exception identifier',
415     level => $self->{must_level});
416     #
417     }
418     } elsif ($token->{type} eq 'typedef') {
419     $token = $get_next_token->();
420     $state = 'before typedef type';
421     next;
422     } elsif ($token->{type} eq 'valuetype') {
423     $token = $get_next_token->();
424     if ($token->{type} eq 'identifier') {
425 wakaba 1.9 my $identifier = $token->{value};
426     $identifier =~ s/^_//;
427     push @current, Whatpm::WebIDL::Valuetype->new ($identifier);
428     $current[-1]->set_user_data (manakai_source_line => $line);
429     $current[-1]->set_user_data (manakai_source_column => $column);
430 wakaba 1.4 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
431 wakaba 1.1 $token = $get_next_token->();
432     $state = 'before boxed type';
433     next;
434     } elsif ($token->{type} eq 'DOMString') {
435     push @current, Whatpm::WebIDL::Valuetype->new ('::DOMString::');
436 wakaba 1.9 $current[-1]->set_user_data (manakai_source_line => $line);
437     $current[-1]->set_user_data (manakai_source_column => $column);
438 wakaba 1.4 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
439 wakaba 1.1 $token = $get_next_token->();
440     if ($token->{type} eq 'sequence') {
441     $token = $get_next_token->();
442     if ($token->{type} eq '<') {
443     $token = $get_next_token->();
444     if ($token->{type} eq 'unsigned') {
445     $token = $get_next_token->();
446     if ($token->{type} eq 'short') {
447     $token = $get_next_token->();
448     if ($token->{type} eq '>') {
449     $current[-1]->type
450     (['::sequence::', ['::unsigned short::']]);
451     $token = $get_next_token->();
452     $state = 'before semicolon';
453     $next_state = 'before definitions';
454     next;
455     } else {
456     $onerror->(type => 'valuetype DOMString sequence lt unsigned short gt',
457     level => $self->{must_level});
458     #
459     }
460     } else {
461     $onerror->(type => 'valuetype DOMString sequence lt unsigned short',
462     level => $self->{must_level});
463     #
464     }
465     } else {
466     $onerror->(type => 'valuetype DOMString sequence lt unsigned',
467     level => $self->{must_level});
468     #
469     }
470     } else {
471     $onerror->(type => 'valuetype DOMString sequence lt',
472     level => $self->{must_level});
473     #
474     }
475     } else {
476     $onerror->(type => 'valuetype DOMString sequence',
477     level => $self->{must_level});
478     #
479     }
480 wakaba 1.8 pop @current; # valuetype
481 wakaba 1.1 #
482     } else {
483     $onerror->(type => 'valuetype identifier',
484     level => $self->{must_level});
485     #
486     }
487     } elsif ($token->{type} eq 'const') {
488     $token = $get_next_token->();
489     $state = 'before const type';
490     $next_state = 'before definitions';
491     next;
492     } else {
493     $onerror->(type => 'before definition', level => 'm',
494     token => $token);
495     # reconsume
496     #
497     }
498     $state = 'ignore';
499     $nest_level = 0;
500     $next_state = 'before definitions';
501     } elsif ($state eq 'before module block') {
502     if ($token->{type} eq '{') {
503     $token = $get_next_token->();
504     $state = 'before definitions';
505     } else {
506     $onerror->(type => 'before module block', level => 'm',
507     token => $token);
508     pop @current; # module
509     # reconsume
510     $state = 'ignore';
511     $nest_level = 0;
512     $next_state = 'before definitions';
513     }
514     } elsif ($state eq 'before interface inheritance') {
515     if ($token->{type} eq ':') {
516     $token = $get_next_token->();
517     $state = 'before parent interface name';
518 wakaba 1.8 } elsif ($token->{type} eq ';') {
519     $current[-1]->is_forward_declaration (1);
520     # reconsume
521     $state = 'before semicolon';
522     $next_state = 'before interface member';
523 wakaba 1.1 } else {
524     # reconsume
525     $state = 'before interface block';
526     }
527     } elsif ($state eq 'before parent interface name') {
528     my $name = $get_scoped_name->();
529     if (defined $name) {
530     $current[-1]->append_inheritance ($name);
531    
532     if ($token->{type} eq ',') {
533     $token = $get_next_token->();
534     # stay in the state
535     } else {
536     # reconsume
537     $state = 'before interface block';
538     }
539     } else {
540     $onerror->(type => 'scoped name', level => $self->{must_level});
541     # reconsume
542     $state = 'ignore';
543     $nest_level = 0;
544     }
545 wakaba 1.2 } elsif ($state eq 'before exception name') {
546     my $name = $get_scoped_name->();
547     if (defined $name) {
548     my $method = $next_state eq '*raises' ? 'append_raises' :
549     $next_state eq '*getraises' ? 'append_getraises' :
550     'append_setraises';
551     $current[-1]->$method ($name);
552    
553     if ($token->{type} eq ',') {
554     $token = $get_next_token->();
555     # stay in the state
556     } elsif ($token->{type} eq ')') {
557     $token = $get_next_token->();
558     if ($next_state eq '*getraises' and $token->{type} eq 'setraises') {
559     $token = $get_next_token->();
560     $state = 'after raises';
561     $next_state = '*setraises';
562     } else {
563     # reprocess
564     $state = 'before semicolon';
565     $next_state = 'before interface member';
566     }
567     } else {
568     $onerror->(type => 'after exception name',
569     level => $self->{must_level});
570     # reconsume
571     $state = 'ignore';
572     $nest_level = 0;
573     }
574     } else {
575     $onerror->(type => 'scoped name', level => $self->{must_level});
576     # reconsume
577     $state = 'ignore';
578     $nest_level = 0;
579     }
580 wakaba 1.1 } elsif ($state eq 'before interface block') {
581     if ($token->{type} eq '{') {
582     $token = $get_next_token->();
583     $state = 'before members';
584     $next_state = 'before interface member';
585     } else {
586     $onerror->(type => 'before interface block',
587     level => $self->{must_level});
588 wakaba 1.8 pop @current; # interface
589 wakaba 1.1 # reconsume
590     $state = 'ignore';
591     $nest_level = 0;
592     }
593     } elsif ($state eq 'before exception block') {
594     if ($token->{type} eq '{') {
595     $token = $get_next_token->();
596     $state = 'before members';
597     $next_state = 'before exception member';
598     } else {
599     $onerror->(type => 'before exception block',
600     level => $self->{must_level});
601     # reconsume
602     $state = 'ignore';
603     $nest_level = 0;
604     }
605     } elsif ($state eq 'before members') {
606 wakaba 1.4 $xattrs = [];
607 wakaba 1.1 if ($token->{type} eq '[') {
608     $token = $get_next_token->();
609     $state = 'before xattr';
610     #$next_state = $next_state; # 'before interface member' or ...
611     } elsif ($token->{type} eq '}') {
612     $token = $get_next_token->();
613 wakaba 1.5 $state = 'before block semicolon';
614 wakaba 1.1 } else {
615     # reconsume
616     $state = $next_state; # ... 'before exception member'
617     }
618     } elsif ($state eq 'before interface member') {
619     if ($token->{type} eq 'const') {
620     $token = $get_next_token->();
621     $state = 'before const type';
622     $next_state = 'before definitions';
623     } elsif ($token->{type} eq 'readonly') {
624     $read_only = 1;
625     $token = $get_next_token->();
626     if ($token->{type} eq 'attribute') {
627     $token = $get_next_token->();
628     $state = 'after attribute';
629     } else {
630     # reconsume
631     $state = 'ignore';
632     $nest_level = 0;
633     $next_state = 'before interface member';
634     }
635     } elsif ($token->{type} eq 'attribute') {
636     $read_only = 0;
637     $token = $get_next_token->();
638     $state = 'after attribute';
639     } elsif ({
640     void => 1, any => 1, boolean => 1, octet => 1, float => 1,
641     DOMString => 1, Object => 1, unsigned => 1, short => 1, long => 1,
642     '::' => 1, identifier => 1,
643     }->{$token->{type}}) {
644     # reconsume
645     $state = 'before operation type';
646     } elsif ($token->{type} eq '}') {
647     $token = $get_next_token->();
648     $state = 'before semicolon';
649     $next_state = 'before definitions';
650     } elsif ($token->{type} eq 'eof') {
651     last;
652     } else {
653     $onerror->(type => 'before interface member',
654     level => $self->{must_level});
655     # reconsume
656     $state = 'ignore';
657     $nest_level = 0;
658     }
659     } elsif ($state eq 'before exception member') {
660     if ({
661     void => 1, any => 1, boolean => 1, octet => 1, float => 1,
662     DOMString => 1, Object => 1, unsigned => 1, short => 1, long => 1,
663     '::' => 1, identifier => 1,
664     }->{$token->{type}}) {
665 wakaba 1.3 # reconsume
666     $state = 'before exception member type';
667 wakaba 1.1 } elsif ($token->{type} eq '}') {
668     $token = $get_next_token->();
669     $state = 'before semicolon';
670     $next_state = 'before definitions';
671     } elsif ($token->{type} eq 'eof') {
672     last;
673     } else {
674     $onerror->(type => 'before exception member',
675     level => $self->{must_level});
676     # reconsume
677     $state = 'ignore';
678     $nest_level = 0;
679     }
680     } elsif ($state eq 'before typedef type') {
681     if ({
682     void => 1, any => 1, boolean => 1, octet => 1, float => 1,
683     DOMString => 1, Object => 1, short => 1, long => 1, unsigned => 1,
684     sequence => 1, '::' => 1, identifier => 1,
685     }->{$token->{type}}) {
686     $current_type = $get_type->();
687     if (defined $current_type) {
688     # next token
689     $state = 'before typedef rest';
690     } else {
691     # reconsume
692     $state = 'ignore';
693     $nest_level = 0;
694     $next_state = 'before definitions';
695     }
696     } else {
697     $onerror->(type => 'before type', level => $self->{must_level});
698     # reconsume
699     $state = 'ignore';
700     $nest_level = 0;
701     $next_state = 'before definitions';
702     }
703     } elsif ($state eq 'before boxed type') {
704     if ({
705     boolean => 1, octet => 1, float => 1,
706     short => 1, long => 1, unsigned => 1,
707     sequence => 1, '::' => 1, identifier => 1,
708     }->{$token->{type}}) {
709     $current_type = $get_type->();
710     if (defined $current_type) {
711     $current[-1]->type ($current_type);
712     # next token
713     $state = 'before semicolon';
714     $next_state = 'before definitions';
715     } else {
716 wakaba 1.8 pop @current; # valuetype
717 wakaba 1.1 # reconsume
718     $state = 'ignore';
719     $nest_level = 0;
720     $next_state = 'before definitions';
721     }
722     } else {
723     $onerror->(type => 'before boxed type', level => $self->{must_level});
724 wakaba 1.8 pop @current; # valuetype
725 wakaba 1.1 # reconsume
726     $state = 'ignore';
727     $nest_level = 0;
728     $next_state = 'before definitions';
729     }
730     } elsif ($state eq 'before const type') {
731     if ({
732     any => 1, boolean => 1, octet => 1, float => 1,
733     DOMString => 1, Object => 1, short => 1, long => 1, unsigned => 1,
734     '::' => 1, identifier => 1,
735     }->{$token->{type}}) {
736     $current_type = $get_type->();
737     if (defined $current_type) {
738     # next token
739     $state = 'before const identifier';
740     } else {
741     # reconsume
742     $state = 'ignore';
743     $nest_level = 0;
744     #$next_state = $next_state;
745     }
746     } else {
747     $onerror->(type => 'before type', level => $self->{must_level});
748     # reconsume
749     $state = 'ignore';
750     $nest_level = 0;
751     #$next_state = $next_state;
752     }
753     } elsif ($state eq 'before typedef rest') {
754     if ($token->{type} eq 'identifier') {
755     ## TODO: unescape
756     push @current, Whatpm::WebIDL::Typedef->new ($token->{value});
757 wakaba 1.9 $current[-1]->set_user_data (manakai_source_line => $line);
758     $current[-1]->set_user_data (manakai_source_column => $column);
759 wakaba 1.1 $current[-1]->type ($current_type);
760 wakaba 1.4 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
761 wakaba 1.1 $token = $get_next_token->();
762     $state = 'before semicolon';
763     $next_state = 'before definitions';
764     } elsif ($token->{type} eq 'DOMString') {
765     push @current, Whatpm::WebIDL::Typedef->new ('::DOMString::');
766     $current[-1]->type ($current_type);
767 wakaba 1.9 $current[-1]->set_user_data (manakai_source_line => $line);
768     $current[-1]->set_user_data (manakai_source_column => $column);
769 wakaba 1.4 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
770 wakaba 1.1 $token = $get_next_token->();
771     $state = 'before semicolon';
772     $next_state = 'before defnitions';
773     } else {
774     $onerror->(type => 'before typedef rest',
775     level => $self->{must_level});
776     # reconsume
777     $state = 'ignore';
778     $nest_level = 0;
779     $next_state = 'before definitions';
780     }
781     } elsif ($state eq 'before const identifier') {
782     if ($token->{type} eq 'identifier') {
783 wakaba 1.9 my $identifier = $token->{value};
784     $identifier =~ s/^_//;
785     push @current, Whatpm::WebIDL::Const->new ($identifier);
786     $current[-1]->set_user_data (manakai_source_line => $line);
787     $current[-1]->set_user_data (manakai_source_column => $column);
788 wakaba 1.1 $current[-1]->type ($current_type);
789 wakaba 1.4 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
790 wakaba 1.1 $token = $get_next_token->();
791     if ($token->{type} eq '=') {
792     $token = $get_next_token->();
793     $state = 'before const expr';
794     next;
795     } else {
796     $onerror->(type => 'const eq', level => $self->{must_level});
797     #
798     }
799     } else {
800     $onerror->(type => 'const identifier', level => $self->{must_level});
801     #
802     }
803     # reconsume
804     $state = 'ignore';
805     $nest_level = 0;
806     #$next_state = $next_state;
807     } elsif ($state eq 'before const expr') {
808     if ($token->{type} eq 'TRUE' or $token->{type} eq 'FALSE') {
809     $current[-1]->value ([$token->{type}]);
810     #
811     } elsif ($token->{type} eq 'integer' or $token->{type} eq 'float') {
812     $current[-1]->value ([$token->{type}, $token->{value}]);
813     #
814     } else {
815     # reconsume
816     $state = 'ignore';
817     $nest_level = 0;
818     #$next_state = $next_state;
819     next;
820     }
821    
822     $token = $get_next_token->();
823     $state = 'before semicolon';
824     #$next_state = $next_state;
825     } elsif ($state eq 'after attribute') {
826     if ({
827     any => 1, boolean => 1, octet => 1, float => 1,
828     DOMString => 1, Object => 1, short => 1, long => 1, unsigned => 1,
829     '::' => 1, identifier => 1,
830     }->{$token->{type}}) {
831     $current_type = $get_type->();
832     if (defined $current_type) {
833     # next token
834     $state = 'before attribute identifier';
835     } else {
836     # reconsume
837     $state = 'ignore';
838     $nest_level = 0;
839     $next_state = 'before interface member';
840     }
841     } else {
842     $onerror->(type => 'before type', level => $self->{must_level});
843     # reconsume
844     $state = 'ignore';
845     $nest_level = 0;
846     $next_state = 'before interface member';
847     }
848 wakaba 1.3 } elsif ($state eq 'before exception member type') {
849     if ({
850     any => 1, boolean => 1, octet => 1, float => 1,
851     DOMString => 1, Object => 1, short => 1, long => 1, unsigned => 1,
852     '::' => 1, identifier => 1,
853     }->{$token->{type}}) {
854     $current_type = $get_type->();
855     if (defined $current_type) {
856     # next token
857     $state = 'before exception member identifier';
858     } else {
859     # reconsume
860     $state = 'ignore';
861     $nest_level = 0;
862     $next_state = 'before exception member';
863     }
864     } else {
865     $onerror->(type => 'before type', level => $self->{must_level});
866     # reconsume
867     $state = 'ignore';
868     $nest_level = 0;
869     $next_state = 'before exception member';
870     }
871 wakaba 1.1 } elsif ($state eq 'before operation type') {
872     if ({
873     any => 1, boolean => 1, octet => 1, float => 1,
874     DOMString => 1, Object => 1, short => 1, long => 1, unsigned => 1,
875     '::' => 1, identifier => 1,
876     void => 1,
877     }->{$token->{type}}) {
878     $current_type = $get_type->();
879     if (defined $current_type) {
880     # next token
881     $state = 'before operation identifier';
882     } else {
883     # reconsume
884     $state = 'ignore';
885     $nest_level = 0;
886     $next_state = 'before interface member';
887     }
888     } else {
889     $onerror->(type => 'before type', level => $self->{must_level});
890     # reconsume
891     $state = 'ignore';
892     $nest_level = 0;
893     $next_state = 'before interface member';
894     }
895     } elsif ($state eq 'before argument type') {
896     if ({
897     any => 1, boolean => 1, octet => 1, float => 1,
898     DOMString => 1, Object => 1, short => 1, long => 1, unsigned => 1,
899     '::' => 1, identifier => 1,
900     }->{$token->{type}}) {
901     $current_type = $get_type->();
902     if (defined $current_type) {
903     # next token
904     $state = 'before argument identifier';
905     } else {
906     # reconsume
907     $state = 'ignore';
908     $nest_level = 0;
909     }
910     } else {
911     $onerror->(type => 'before type', level => $self->{must_level});
912     # reconsume
913     $state = 'ignore';
914     $nest_level = 0;
915     }
916     } elsif ($state eq 'before attribute identifier') {
917     if ($token->{type} eq 'identifier') {
918 wakaba 1.10 my $identifier = $token->{value};
919     $identifier =~ s/^_//;
920     push @current, Whatpm::WebIDL::Attribute->new ($identifier);
921 wakaba 1.9 $current[-1]->set_user_data (manakai_source_line => $line);
922     $current[-1]->set_user_data (manakai_source_column => $column);
923 wakaba 1.1 $current[-1]->readonly ($read_only);
924     $current[-1]->type ($current_type);
925 wakaba 1.4 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
926 wakaba 1.1 $token = $get_next_token->();
927     if ($token->{type} eq 'getraises') {
928     $token = $get_next_token->();
929 wakaba 1.2 $state = 'after raises';
930     $next_state = '*getraises';
931 wakaba 1.1 next;
932     } elsif ($token->{type} eq 'setraises') {
933     $token = $get_next_token->();
934 wakaba 1.2 $state = 'after raises';
935     $next_state = '*setraises';
936 wakaba 1.1 next;
937     } else {
938     # reconsume
939     $state = 'before semicolon';
940     $next_state = 'before interface member';
941     next;
942     }
943     } else {
944     $onerror->(type => 'attribute identifier',
945     level => $self->{must_level});
946     #
947     }
948     # reconsume
949     $state = 'ignore';
950     $nest_level = 0;
951 wakaba 1.3 $next_state = 'before interface member';
952     } elsif ($state eq 'before exception member identifier') {
953     if ($token->{type} eq 'identifier') {
954     ## TODO: unescape
955     push @current, Whatpm::WebIDL::ExceptionMember->new ($token->{value});
956 wakaba 1.9 $current[-1]->set_user_data (manakai_source_line => $line);
957     $current[-1]->set_user_data (manakai_source_column => $column);
958 wakaba 1.3 $current[-1]->type ($current_type);
959 wakaba 1.4 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
960 wakaba 1.3 $token = $get_next_token->();
961     $state = 'before semicolon';
962     $next_state = 'before exception member';
963     } else {
964     $onerror->(type => 'exception member identifier',
965     level => $self->{must_level});
966     # reconsume
967     $state = 'ignore';
968     $nest_level = 0;
969     }
970 wakaba 1.1 } elsif ($state eq 'before operation identifier') {
971     if ($token->{type} eq 'identifier') {
972     ## TODO: unescape
973     push @current, Whatpm::WebIDL::Operation->new ($token->{value});
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.1 $current[-1]->type ($current_type);
977 wakaba 1.4 $current[-1]->set_extended_attribute_node ($_) for @$xattrs;
978 wakaba 1.1 $token = $get_next_token->();
979     if ($token->{type} eq '(') {
980     $token = $get_next_token->();
981     if ($token->{type} eq ')') {
982     $token = $get_next_token->();
983 wakaba 1.2 $state = 'before raises';
984     $next_state = '*raises';
985 wakaba 1.1 next;
986     } else {
987     # reconsume
988     $state = 'before argument';
989     next;
990     }
991     } else {
992     $onerror->(type => 'arguments lparen',
993     level => $self->{must_level});
994     #
995     }
996     } else {
997     $onerror->(type => 'operation identifier',
998     level => $self->{must_level});
999     #
1000     }
1001     # reconsume
1002     $state = 'ignore';
1003     $nest_level = 0;
1004 wakaba 1.3 $next_state = 'before interface member';
1005 wakaba 1.1 } elsif ($state eq 'before argument identifier') {
1006     if ($token->{type} eq 'identifier') {
1007     ## TODO: unescape
1008     my $arg = Whatpm::WebIDL::Argument->new ($token->{value});
1009 wakaba 1.9 $arg->set_user_data (manakai_source_line => $line);
1010     $arg->set_user_data (manakai_source_column => $column);
1011 wakaba 1.1 $arg->type ($current_type);
1012 wakaba 1.4 $arg->set_extended_attribute_node ($_) for @$xattrs;
1013 wakaba 1.1 $current[-1]->append_child ($arg);
1014     $token = $get_next_token->();
1015     if ($token->{type} eq ')') {
1016     $token = $get_next_token->();
1017 wakaba 1.4 if ($current[-1]->isa ('Whatpm::WebIDL::Operation')) {
1018     $state = 'before raises';
1019     } else {
1020     $state = 'after xattrarg';
1021     }
1022 wakaba 1.1 next;
1023     } elsif ($token->{type} eq ',') {
1024     $token = $get_next_token->();
1025     $state = 'before argument';
1026     next;
1027     } else {
1028     $onerror->(type => 'after argument',
1029     level => $self->{must_level});
1030     #
1031     }
1032     } else {
1033     $onerror->(type => 'argument identifier',
1034     level => $self->{must_level});
1035     #
1036     }
1037     # reconsume
1038     $state = 'ignore';
1039     $nest_level = 0;
1040     } elsif ($state eq 'before argument') {
1041 wakaba 1.4 $xattrs = [];
1042 wakaba 1.1 if ($token->{type} eq '[') {
1043     $token = $get_next_token->();
1044     $state = 'before xattr';
1045     $next_state = 'before argument in';
1046     } else {
1047     # reconsume
1048     $state = 'before argument in';
1049     }
1050     } elsif ($state eq 'before argument in') {
1051     if ($token->{type} eq 'in') {
1052     $token = $get_next_token->();
1053     $state = 'before argument type';
1054     } else {
1055     $onerror->(type => 'argument in',
1056     level => $self->{must_level});
1057     $state = 'ignore';
1058     $nest_level = 0;
1059     }
1060     } elsif ($state eq 'before raises') {
1061     if ($token->{type} eq 'raises') {
1062     $token = $get_next_token->();
1063     $state = 'after raises';
1064 wakaba 1.2 $next_state = '*raises';
1065 wakaba 1.1 } else {
1066     # reconsume
1067     $state = 'before semicolon';
1068     $next_state = 'before interface member';
1069     }
1070     } elsif ($state eq 'after raises') {
1071 wakaba 1.2 if ($token->{type} eq '(') {
1072     $token = $get_next_token->();
1073     $state = 'before exception name';
1074     } else {
1075     $onerror->(type => 'raises lparen',
1076     level => $self->{must_level});
1077     $state = 'ignore';
1078     $nest_level = 0;
1079     }
1080 wakaba 1.1 } elsif ($state eq 'before semicolon') {
1081     if ($token->{type} eq ';') {
1082     $current[-2]->append_child ($current[-1]);
1083     pop @current;
1084     $token = $get_next_token->();
1085 wakaba 1.5 if ($current[-1]->isa ('Whatpm::WebIDL::Definitions') or
1086     $current[-1]->isa ('Whatpm::WebIDL::Module')) {
1087     $state = 'before definitions';
1088     } elsif ($current[-1]->isa ('Whatpm::WebIDL::Interface')) {
1089     $state = 'before members';
1090     $next_state = 'before interface member';
1091     } else {
1092     $state = 'before members';
1093     $next_state = 'before exception member';
1094     }
1095 wakaba 1.1 } else {
1096 wakaba 1.5 $onerror->(type => 'before semicolon', level => 'm',
1097     token => $token);
1098     # reconsume
1099     $state = 'ignore';
1100     $nest_level = 0;
1101     }
1102     } elsif ($state eq 'before block semicolon') {
1103     if ($token->{type} eq ';') {
1104     $current[-2]->append_child ($current[-1]);
1105 wakaba 1.1 pop @current;
1106 wakaba 1.5 $token = $get_next_token->();
1107     if ($current[-1]->isa ('Whatpm::WebIDL::Definitions') or
1108     $current[-1]->isa ('Whatpm::WebIDL::Module')) {
1109     $state = 'before definitions';
1110     } elsif ($current[-1]->isa ('Whatpm::WebIDL::Interface')) {
1111     $state = 'before members';
1112     $next_state = 'before interface member';
1113     } else {
1114     $state = 'before members';
1115     $next_state = 'before exception member';
1116     }
1117     } else {
1118 wakaba 1.1 $onerror->(type => 'before semicolon', level => 'm',
1119     token => $token);
1120 wakaba 1.5 pop @current; # avoid appended by 'ignore'
1121 wakaba 1.1 # reconsume
1122     $state = 'ignore';
1123     $nest_level = 0;
1124     }
1125     } elsif ($state eq 'ignore') {
1126 wakaba 1.5 if ($nest_level == 0 and $token->{type} eq ';') {
1127     while (@current > 1) {
1128     if ($current[-1]->isa ('Whatpm::WebIDL::Interface') or
1129     $current[-1]->isa ('Whatpm::WebIDL::Exception') or
1130     $current[-1]->isa ('Whatpm::WebIDL::Module')) {
1131     last;
1132     }
1133     pop @current;
1134     }
1135    
1136 wakaba 1.1 $token = $get_next_token->();
1137 wakaba 1.5 if ($current[-1]->isa ('Whatpm::WebIDL::Definitions') or
1138     $current[-1]->isa ('Whatpm::WebIDL::Module')) {
1139     $state = 'before definitions';
1140     } elsif ($current[-1]->isa ('Whatpm::WebIDL::Interface')) {
1141     $state = 'before members';
1142     $next_state = 'before interface member';
1143     } else {
1144     $state = 'before members';
1145     $next_state = 'before exception member';
1146     }
1147 wakaba 1.1 } elsif ($token->{type} eq '{') {
1148     $nest_level++;
1149     $token = $get_next_token->();
1150     # stay in the state
1151     } elsif ($nest_level and $token->{type} eq '}') {
1152     $nest_level--;
1153     $token = $get_next_token->();
1154     # stay in the state
1155     } elsif ($token->{type} eq 'eof') {
1156 wakaba 1.5 while (@current > 1) {
1157     if ($current[-1]->isa ('Whatpm::WebIDL::Interface') or
1158     $current[-1]->isa ('Whatpm::WebIDL::Exception') or
1159     $current[-1]->isa ('Whatpm::WebIDL::Module')) {
1160     last;
1161     }
1162     pop @current;
1163     }
1164    
1165 wakaba 1.1 last;
1166     } else {
1167     # ignore the token
1168     $token = $get_next_token->();
1169     # stay in the state
1170     }
1171     } else {
1172     die "parse_char_string: unkown state: $state";
1173     }
1174     }
1175    
1176     if (@current > 1) {
1177 wakaba 1.5 $onerror->(type => 'premature end of file', level => $self->{must_level});
1178     while (@current > 1) {
1179     $current[-2]->append_child ($current[-1]);
1180     pop @current;
1181     }
1182 wakaba 1.1 }
1183    
1184     $get_type = undef; # unlink loop
1185    
1186     return $defs;
1187     } # parse_char_string
1188    
1189 wakaba 1.8 package Whatpm::WebIDL::Node;
1190 wakaba 1.1
1191 wakaba 1.9 require Scalar::Util;
1192    
1193 wakaba 1.1 sub new ($) {
1194     return bless {child_nodes => []}, $_[0];
1195     } # new
1196    
1197     sub append_child ($$) {
1198     my $self = shift;
1199     my $child = shift;
1200    
1201     ## TODO: child type
1202     ## TODO: parent check
1203    
1204     push @{$self->{child_nodes}}, $child;
1205    
1206 wakaba 1.9 $child->{parent_node} = $self;
1207     Scalar::Util::weaken ($child->{parent_node});
1208    
1209 wakaba 1.1 return $child;
1210     } # append_child
1211    
1212 wakaba 1.8 sub child_nodes ($) {
1213     return [@{$_[0]->{child_nodes}}]; # dead list
1214     } # child_nodes
1215    
1216     sub idl_text ($) {
1217     return '[[ERROR: ' . (ref $_[0]) . '->idl_text]]';
1218     } # idl_text
1219    
1220 wakaba 1.1 sub node_name ($) {
1221 wakaba 1.8 return $_[0]->{node_name}; # may be undef
1222 wakaba 1.1 } # node_name
1223    
1224 wakaba 1.9 sub parent_node ($) {
1225     return $_[0]->{parent_node};
1226     } # parent_node
1227    
1228 wakaba 1.8 sub get_user_data ($$) {
1229     return $_[0]->{user_data}->{$_[1]};
1230     } # get_user_data
1231    
1232     sub set_user_data ($$$) {
1233     if (defined $_[2]) {
1234     $_[0]->{user_data}->{$_[1]} = ''.$_[2];
1235     } else {
1236     delete $_[0]->{user_data}->{$_[1]};
1237     }
1238     } # set_user_data
1239    
1240     package Whatpm::WebIDL::Definitions;
1241     push our @ISA, 'Whatpm::WebIDL::Node';
1242    
1243     sub idl_text ($) {
1244     return join "\x0A", map {$_->idl_text} @{$_[0]->{child_nodes}};
1245     } # idl_text
1246    
1247     sub check ($$) {
1248     my ($self, $onerror) = @_;
1249    
1250 wakaba 1.10 my $items = [map { {node => $_, scope => '::'} } @{$self->{child_nodes}}];
1251 wakaba 1.9
1252     my $defined_qnames = {};
1253    
1254     while (@$items) {
1255     my $item = shift @$items;
1256 wakaba 1.10 if ($item->{node}->isa ('Whatpm::WebIDL::Definition') and
1257     not $item->{defined_members}) {
1258 wakaba 1.9 if ($item->{node}->isa ('Whatpm::WebIDL::Interface')) {
1259 wakaba 1.10 for my $i_sn (@{$item->{node}->{inheritances}}) {
1260     if ($i_sn =~ /::DOMString\z/) {
1261     #
1262     } elsif ($i_sn =~ /^::/) {
1263     if ($defined_qnames->{$i_sn}) {
1264     next;
1265     }
1266     #
1267     } else {
1268     if ($defined_qnames->{$item->{scope} . $i_sn} and
1269     $defined_qnames->{$item->{scope} . $i_sn}->{node}
1270     ->isa ('Whatpm::WebIDL::Interface')) {
1271     next;
1272     } elsif ($defined_qnames->{'::' . $i_sn} and
1273     $defined_qnames->{'::' . $i_sn}->{node}
1274     ->isa ('Whatpm::WebIDL::Interface')) {
1275     next;
1276     }
1277     #
1278     }
1279    
1280     $onerror->(type => 'interface not defined',
1281     level => 'm',
1282     node => $item->{node},
1283     text => $i_sn);
1284     }
1285    
1286     my $defined_members = {};
1287 wakaba 1.9 unshift @$items,
1288 wakaba 1.10 map { {node => $_, defined_members => $defined_members} }
1289 wakaba 1.9 @{$item->{node}->{child_nodes}};
1290    
1291     unless ($item->{parent}) {
1292     $onerror->(type => 'non-module definition',
1293     level => 's',
1294     node => $item->{node});
1295     }
1296     } elsif ($item->{node}->isa ('Whatpm::WebIDL::Exception')) {
1297     unshift @$items,
1298     map { {node => $_, parent => $item->{node}} }
1299     @{$item->{node}->{child_nodes}};
1300    
1301     unless ($item->{parent}) {
1302     $onerror->(type => 'non-module definition',
1303     level => 's',
1304     node => $item->{node});
1305     }
1306     } elsif ($item->{node}->isa ('Whatpm::WebIDL::Module')) {
1307     unshift @$items,
1308 wakaba 1.10 map {
1309     {node => $_, parent => $item->{node},
1310     scope => $item->{scope} . $item->{node}->node_name . '::'}
1311     }
1312 wakaba 1.9 @{$item->{node}->{child_nodes}};
1313     } else {
1314     unless ($item->{parent}) {
1315     $onerror->(type => 'non-module definition',
1316     level => 's',
1317     node => $item->{node});
1318     }
1319     }
1320    
1321     my $qname = $item->{node}->qualified_name;
1322     if ($defined_qnames->{$qname}) {
1323     ## NOTE: "The identifier of a definition MUST be locally unique":
1324     ## Redundant with another requirement below.
1325    
1326 wakaba 1.10 ## ISSUE: |interface x; interface x {};| is non-conforming
1327     ## according to the current spec text.
1328    
1329     ## ISSUE: |interface x;| with no |interface x {};| is conforming
1330     ## according to the current spec text.
1331    
1332 wakaba 1.9 $onerror->(type => 'duplicate qname',
1333     level => 'm',
1334     node => $item->{node});
1335     } else {
1336 wakaba 1.10 $defined_qnames->{$qname} = $item;
1337     ## NOTE: This flag must be turned on AFTER inheritance check is
1338     ## performed (c.f. |interface x : x {};|).
1339 wakaba 1.9 }
1340     } elsif ($item->{node}->isa ('Whatpm::WebIDL::InterfaceMember')) {
1341     if ($item->{node}->isa ('Whatpm::WebIDL::Operation')) {
1342     unshift @$items,
1343     map { {node => $_, parent => $item->{node}} }
1344     @{$item->{node}->{child_nodes}};
1345 wakaba 1.10 } else {
1346     my $name = $item->{node}->node_name;
1347     if ($item->{defined_members}->{$name}) {
1348     $onerror->(type => 'duplicate interface member',
1349     level => 'm',
1350     node => $item->{node});
1351     ## ISSUE: Whether the example below is conforming or not
1352     ## is ambigious:
1353     ## |interface a { attribute any b; any b (); };|
1354     } else {
1355     $item->{defined_members}->{$name} = 1;
1356     }
1357 wakaba 1.9 }
1358     }
1359    
1360     my $xattrs = $item->{node}->{xattrs} || [];
1361     for my $xattr (@$xattrs) {
1362     my $xattr_name = $xattr->node_name;
1363 wakaba 1.10 if ({
1364     Constructor => 1, NamedConstructor => 1, NativeObject => 1,
1365     NoInterfaceObject => 1, Stringifies => 1,
1366     }->{$xattr_name}) {
1367     if ($item->{node}->isa ('Whatpm::WebIDL::Interface')) {
1368    
1369     next;
1370     } else {
1371     #
1372     }
1373     } elsif ($xattr_name eq 'ExceptionConsts') {
1374 wakaba 1.9 if ($item->{node}->isa ('Whatpm::WebIDL::Module')) {
1375    
1376     next;
1377     } else {
1378     #
1379     }
1380     } else {
1381     $onerror->(type => 'unknown xattr',
1382     level => 'u',
1383     node => $xattr);
1384     next;
1385     }
1386 wakaba 1.8
1387 wakaba 1.9 $onerror->(type => 'xattr not applicable',
1388     level => 'i', ## TODO: fact_level
1389     node => $xattr);
1390 wakaba 1.8 }
1391     }
1392     } # check
1393    
1394     package Whatpm::WebIDL::Definition;
1395     push our @ISA, 'Whatpm::WebIDL::Node';
1396    
1397     sub new ($$) {
1398     return bless {child_nodes => [], node_name => ''.$_[1]}, $_[0];
1399     } # new
1400    
1401 wakaba 1.4 sub set_extended_attribute_node ($$) {
1402     my $self = shift;
1403     ## TODO: check
1404     push @{$self->{xattrs} ||= []}, shift;
1405     } # set_extended_attribute_node
1406    
1407     sub _xattrs_text ($) {
1408     my $self = shift;
1409    
1410     unless ($self->{xattrs} and
1411     @{$self->{xattrs}}) {
1412     return '';
1413     }
1414    
1415     my $r = '[';
1416     $r .= join ', ', map {$_->idl_text} @{$self->{xattrs}};
1417     $r .= ']';
1418     return $r;
1419     } # _xattrs_text
1420    
1421 wakaba 1.9 sub qualified_name ($) {
1422     my $self = shift;
1423    
1424     my $parent = $self->{parent_node};
1425     if ($parent and $parent->isa ('Whatpm::WebIDL::Definition')) {
1426     return $parent->qualified_name . '::' . $self->{node_name};
1427     } else {
1428     return '::' . $self->{node_name};
1429     }
1430     } # qualified_name
1431    
1432 wakaba 1.1 sub type ($;$) {
1433     if (@_ > 1) {
1434     if (defined $_[1]) {
1435     $_[0]->{type} = $_[1];
1436     } else {
1437     $_[0]->{type} = ['::any::'];
1438     }
1439     }
1440     return $_[0]->{type};
1441     } # type
1442    
1443 wakaba 1.6 my $serialize_type;
1444     my $serialize_type_depth = 0;
1445     $serialize_type = sub ($) {
1446     my $type = shift;
1447 wakaba 1.10 if (ref $type) {
1448     if ($type->[0] eq '::sequence::') {
1449     if ($serialize_type_depth++ == 1024) {
1450     return 'sequence<<<sequence too deep>>>';
1451     } else {
1452     return 'sequence<' . $serialize_type->($type->[1]) . '>';
1453     }
1454 wakaba 1.6 } else {
1455 wakaba 1.10 return join '::', map {
1456     /^::([^:]+)::$/ ? $1 : $_ ## TODO: escape
1457     } @{$type};
1458 wakaba 1.6 }
1459     } else {
1460 wakaba 1.10 return $type; ## TODO: escape identifiers...
1461 wakaba 1.6 }
1462     }; # $serialize_type
1463    
1464 wakaba 1.1 sub type_text ($) {
1465     my $type = $_[0]->{type};
1466     return undef unless defined $type;
1467 wakaba 1.6
1468     return $serialize_type->($type);
1469 wakaba 1.1 } # type_text
1470 wakaba 1.6
1471 wakaba 1.1 package Whatpm::WebIDL::Module;
1472     push our @ISA, 'Whatpm::WebIDL::Definition';
1473    
1474     sub idl_text ($) {
1475 wakaba 1.4 my $self = shift;
1476     my $r = $self->_xattrs_text;
1477     $r .= ' ' if length $r;
1478 wakaba 1.8 $r .= 'module ' . $self->node_name . " {\x0A\x0A"; ## TODO: escape
1479 wakaba 1.4 for (@{$self->{child_nodes}}) {
1480 wakaba 1.1 $r .= $_->idl_text;
1481     }
1482     $r .= "\x0A};\x0A";
1483     return $r;
1484     } # idl_text
1485    
1486     package Whatpm::WebIDL::Interface;
1487     push our @ISA, 'Whatpm::WebIDL::Definition';
1488    
1489     sub new ($$) {
1490     my $self = shift->SUPER::new (@_);
1491     $self->{inheritances} = [];
1492     return $self;
1493     } # new
1494    
1495     sub append_inheritance ($$) {
1496     my $self = shift;
1497     my $scoped_name = shift;
1498     push @{$self->{inheritances}}, $scoped_name;
1499     } # append_inheritance
1500    
1501     sub idl_text ($) {
1502 wakaba 1.3 my $self = shift;
1503 wakaba 1.4 my $r = $self->_xattrs_text;
1504     $r .= ' ' if length $r;
1505 wakaba 1.9 $r .= 'interface ' . $self->node_name;
1506 wakaba 1.8
1507     if ($self->{is_forward_declaration}) {
1508     $r .= ";\x0A";
1509     return $r;
1510     }
1511    
1512 wakaba 1.3 if (@{$self->{inheritances}}) {
1513     $r .= ' : '; ## TODO: ...
1514 wakaba 1.10 $r .= join ', ', map {$serialize_type->($_)} @{$self->{inheritances}};
1515 wakaba 1.3 }
1516     $r .= " {\x0A"; ## TODO: escape
1517     for (@{$self->{child_nodes}}) {
1518 wakaba 1.1 $r .= ' ' . $_->idl_text;
1519     }
1520     $r .= "};\x0A";
1521     return $r;
1522     } # idl_text
1523    
1524 wakaba 1.8 sub is_forward_declaration ($;$) {
1525     if (@_ > 1) {
1526     if ($_[1]) {
1527     $_[0]->{is_forward_declaration} = 1;
1528     } else {
1529     delete $_[0]->{is_forward_declaration};
1530     }
1531     }
1532    
1533     return $_[0]->{is_forward_declaration};
1534     } # is_forward_declaration
1535    
1536 wakaba 1.1 package Whatpm::WebIDL::Exception;
1537     push our @ISA, 'Whatpm::WebIDL::Definition';
1538    
1539 wakaba 1.3 sub idl_text ($) {
1540 wakaba 1.4 my $self = shift;
1541     my $r = $self->_xattrs_text;
1542     $r .= ' ' if length $r;
1543 wakaba 1.9 $r .= 'exception ' . $self->node_name . " {\x0A"; ## TODO: escape
1544 wakaba 1.4 for (@{$self->{child_nodes}}) {
1545 wakaba 1.3 $r .= ' ' . $_->idl_text;
1546     }
1547     $r .= "};\x0A";
1548     return $r;
1549     } # idl_text
1550    
1551 wakaba 1.1 package Whatpm::WebIDL::Typedef;
1552     push our @ISA, 'Whatpm::WebIDL::Definition';
1553    
1554     sub new ($$) {
1555     my $self = shift->SUPER::new (@_);
1556     $self->{type} = ['::any::'];
1557     return $self;
1558     } # new
1559    
1560     sub idl_text ($) {
1561 wakaba 1.4 my $self = shift;
1562     my $r = $self->_xattrs_text;
1563     $r .= ' ' if length $r;
1564 wakaba 1.1 ## TODO: escape
1565 wakaba 1.4 $r .= 'typedef ' . $self->type_text . ' ' . $self->node_name . ";\x0A";
1566     return $r;
1567 wakaba 1.1 } # idl_text
1568    
1569     package Whatpm::WebIDL::Valuetype;
1570     push our @ISA, 'Whatpm::WebIDL::Definition';
1571    
1572     sub new ($$) {
1573     my $self = shift->SUPER::new (@_);
1574     $self->{type} = ['::boolean::'];
1575     return $self;
1576     } # new
1577    
1578     sub idl_text ($) {
1579 wakaba 1.4 my $self = shift;
1580     my $r = $self->_xattrs_text;
1581     $r .= ' ' if length $r;
1582 wakaba 1.1 ## TODO: escape
1583 wakaba 1.4 $r .= 'valuetype ' . $self->node_name . ' ' . $self->type_text . ";\x0A";
1584     return $r;
1585 wakaba 1.1 } # idl_text
1586    
1587     package Whatpm::WebIDL::InterfaceMember;
1588 wakaba 1.8 push our @ISA, 'Whatpm::WebIDL::Node';
1589 wakaba 1.1
1590     sub new ($$) {
1591     return bless {node_name => ''.$_[1]}, $_[0];
1592     } # new
1593    
1594 wakaba 1.8 sub child_nodes ($) { return [] }
1595    
1596 wakaba 1.4 *_xattrs_text = \&Whatpm::WebIDL::Definition::_xattrs_text;
1597    
1598     *set_extended_attribute_node = \&Whatpm::WebIDL::Definition::set_extended_attribute_node;
1599    
1600 wakaba 1.1 *type = \&Whatpm::WebIDL::Definition::type;
1601    
1602     *type_text = \&Whatpm::WebIDL::Definition::type_text;
1603    
1604     package Whatpm::WebIDL::Const;
1605     push our @ISA, 'Whatpm::WebIDL::Definition', 'Whatpm::WebIDL::InterfaceMember';
1606    
1607     sub new ($$) {
1608     my $self = shift->SUPER::new (@_); # Definition->new should be called
1609     $self->{type} = ['::boolean::'];
1610     $self->{value} = ['FALSE'];
1611     return $self;
1612     } # new
1613    
1614     sub value ($;$) {
1615     if (@_ > 1) {
1616     $_[0]->{value} = $_[1];
1617     }
1618    
1619     return $_[0]->{value};
1620     } # value
1621    
1622     sub value_text ($) {
1623     my $value = $_[0]->{value};
1624    
1625     if ($value->[0] eq 'TRUE' or $value->[0] eq 'FALSE') {
1626     return $value->[0];
1627     } elsif ($value->[0] eq 'integer' or $value->[0] eq 'float') {
1628     ## TODO: format
1629     return $value->[1];
1630     } else {
1631     return undef;
1632     }
1633     } # value_text
1634    
1635     sub idl_text ($) {
1636 wakaba 1.4 my $self = shift;
1637     my $r = $self->_xattrs_text;
1638     $r .= ' ' if length $r;
1639     $r .= 'const ' . $self->type_text . ' ' . $self->node_name . ' = ' . $self->value_text . ";\x0A"; ## TODO: escape
1640     return $r;
1641 wakaba 1.1 } # idl_text
1642    
1643     package Whatpm::WebIDL::Attribute;
1644     push our @ISA, 'Whatpm::WebIDL::InterfaceMember';
1645    
1646     sub new ($$) {
1647     my $self = shift->SUPER::new (@_);
1648     $self->{type} = ['::any::'];
1649 wakaba 1.2 $self->{getraises} = [];
1650     $self->{setraises} = [];
1651 wakaba 1.1 return $self;
1652     } # new
1653    
1654 wakaba 1.2 sub append_getraises ($$) {
1655     ## TODO: error check, etc.
1656     push @{$_[0]->{getraises}}, $_[1];
1657     } # append_getraises
1658    
1659     sub append_setraises ($$) {
1660     ## TODO: error check, etc.
1661     push @{$_[0]->{setraises}}, $_[1];
1662     } # append_setraises
1663    
1664 wakaba 1.1 sub readonly ($;$) {
1665     if (@_ > 1) {
1666     $_[0]->{readonly} = $_[1];
1667     }
1668    
1669     return $_[0]->{readonly};
1670     } # readonly
1671    
1672     sub idl_text ($) {
1673 wakaba 1.2 my $self = shift;
1674 wakaba 1.4 my $r = $self->_xattrs_text;
1675     $r .= ' ' if length $r;
1676     $r .= ($self->readonly ? 'readonly ' : '') . 'attribute ' . $self->type_text . ' ' . $self->node_name;
1677 wakaba 1.1 ## TODO: escape
1678 wakaba 1.2 if (@{$self->{getraises}}) {
1679     $r .= ' getraises (';
1680     ## todo: ...
1681     $r .= join ', ', map {join '::', @{$_}} @{$self->{getraises}};
1682     $r .= ')';
1683     }
1684     if (@{$self->{setraises}}) {
1685     $r .= ' setraises (';
1686     ## todo: ...
1687     $r .= join ', ', map {join '::', @{$_}} @{$self->{setraises}};
1688     $r .= ')';
1689     }
1690     $r .= ";\x0A";
1691     return $r;
1692 wakaba 1.1 } # idl_text
1693    
1694     package Whatpm::WebIDL::Operation;
1695     push our @ISA, 'Whatpm::WebIDL::InterfaceMember';
1696    
1697     sub new ($$) {
1698     my $self = shift->SUPER::new (@_);
1699     $self->{type} = ['::any::'];
1700     $self->{child_nodes} = [];
1701 wakaba 1.2 $self->{raises} = [];
1702 wakaba 1.1 return $self;
1703     } # new
1704    
1705 wakaba 1.2 sub append_raises ($$) {
1706     ## TODO: error check, etc.
1707     push @{$_[0]->{raises}}, $_[1];
1708     } # append_raises
1709    
1710 wakaba 1.1 sub idl_text ($) {
1711 wakaba 1.2 my $self = shift;
1712 wakaba 1.4 my $r = $self->_xattrs_text;
1713     $r .= ' ' if length $r;
1714     $r .= $self->type_text . ' ' . $self->node_name . ' ('; ## TODO: escape
1715 wakaba 1.2 $r .= join ', ', map {$_->idl_text} @{$self->{child_nodes}};
1716     $r .= ')';
1717     if (@{$self->{raises}}) {
1718     $r .= ' raises (';
1719     ## todo: ...
1720     $r .= join ', ', map {join '::', @{$_}} @{$self->{raises}};
1721     $r .= ')';
1722     }
1723 wakaba 1.1 $r .= ";\x0A";
1724     return $r;
1725     } # idl_text
1726    
1727     package Whatpm::WebIDL::Argument;
1728 wakaba 1.8 push our @ISA, 'Whatpm::WebIDL::Node';
1729 wakaba 1.1
1730     sub new ($$) {
1731     return bless {node_name => ''.$_[1], type => ['::any::']}, $_[0];
1732     } # new
1733    
1734     sub idl_text ($) {
1735 wakaba 1.4 my $self = shift;
1736     my $r = $self->_xattrs_text;
1737     $r .= ' ' if length $r;
1738     $r .= 'in ' . $self->type_text . ' ' . $self->node_name; ## TODO: escape
1739     return $r;
1740 wakaba 1.3 } # idl_text
1741    
1742 wakaba 1.4 *_xattrs_text = \&Whatpm::WebIDL::Definition::_xattrs_text;
1743    
1744     *set_extended_attribute_node = \&Whatpm::WebIDL::Definition::set_extended_attribute_node;
1745    
1746 wakaba 1.3 *type = \&Whatpm::WebIDL::Definition::type;
1747    
1748     *type_text = \&Whatpm::WebIDL::Definition::type_text;
1749    
1750     package Whatpm::WebIDL::ExceptionMember;
1751 wakaba 1.8 push our @ISA, 'Whatpm::WebIDL::Node';
1752 wakaba 1.3
1753     sub new ($$) {
1754     return bless {node_name => ''.$_[1], type => ['::any::']}, $_[0];
1755     } # new
1756    
1757     sub idl_text ($) {
1758 wakaba 1.4 my $self = shift;
1759     my $r = $self->_xattrs_text;
1760     $r .= ' ' if length $r;
1761     $r .= $self->type_text . ' ' . $self->node_name . ";\x0A"; ## TODO: escape
1762     return $r;
1763 wakaba 1.1 } # idl_text
1764    
1765 wakaba 1.4 *_xattrs_text = \&Whatpm::WebIDL::Definition::_xattrs_text;
1766    
1767     *set_extended_attribute_node = \&Whatpm::WebIDL::Definition::set_extended_attribute_node;
1768    
1769 wakaba 1.1 *type = \&Whatpm::WebIDL::Definition::type;
1770    
1771     *type_text = \&Whatpm::WebIDL::Definition::type_text;
1772 wakaba 1.9
1773 wakaba 1.4 package Whatpm::WebIDL::ExtendedAttribute;
1774 wakaba 1.8 push our @ISA, 'Whatpm::WebIDL::Node';
1775 wakaba 1.4
1776     sub new ($$) {
1777     return bless {child_nodes => [], node_name => ''.$_[1]};
1778     } # new
1779    
1780     sub idl_text ($) {
1781     my $self = shift;
1782     my $r = $self->node_name; ## TODO:] esceape
1783     if (defined $self->{value}) {
1784     $r .= '=' . $self->{value}; ## TODO: escape
1785     }
1786     if (@{$self->{child_nodes}}) {
1787     $r .= ' (';
1788     $r .= join ', ', map {$_->idl_text} @{$self->{child_nodes}};
1789     $r .= ')';
1790     }
1791     return $r;
1792     } # idl_text
1793    
1794     sub value ($;$) {
1795     if (@_ > 1) {
1796     if (defined $_[1]) {
1797     $_[0]->{value} = ''.$_[1];
1798     } else {
1799     delete $_[0]->{value};
1800     }
1801     }
1802    
1803     return $_[0]->{value};
1804     } # value
1805 wakaba 1.1
1806     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24