/[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.9 - (hide annotations) (download)
Sat Aug 2 11:49:58 2008 UTC (16 years, 3 months ago) by wakaba
Branch: MAIN
Changes since 1.8: +145 -23 lines
++ whatpm/t/webidl/ChangeLog	2 Aug 2008 11:49:09 -0000
	* webidl-defs.dat: More test data.

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

++ whatpm/Whatpm/ChangeLog	2 Aug 2008 11:48:45 -0000
	to generated nodes.  Unescape identifiers.  Extended attributes
	for Definition's were ignored.
	(append_child): Set |parent_node| attribute.
	(parent_node): New attribute.
	(check): Support interface/exception members.  Support
	extended attributes.  Support definition identifier uniqueness
	constraint.
	(qualified_name): New attribute.
	(Interface/Exception idl_text): Extended attributes were
	not prepended to the returned text.

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

	* WebIDL.pm (parse_char_string): Set line/column numbers

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24