/[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.2 - (hide annotations) (download)
Sat Jul 19 07:25:32 2008 UTC (16 years, 3 months ago) by wakaba
Branch: MAIN
Changes since 1.1: +103 -14 lines
++ whatpm/Whatpm/ChangeLog	19 Jul 2008 07:25:16 -0000
2008-07-19  Wakaba  <wakaba@suika.fam.cx>

	* WebIDL.pm: Hierarchical scoped name support was broken.
	Support for raises, setraises, and getraises syntaxes.

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    
27     pos ($$s) = 0;
28     my $line = 1;
29     my $column = 0;
30    
31     my $get_next_token = sub {
32     if (length $$s <= pos $$s) {
33     return {type => 'eof'};
34     }
35    
36     while ($$s =~ /\G($whitespace)/gc) {
37     my $v = $1;
38     while ($v =~ s/^[^\x0D\x0A]*(?>\x0D\x0A?|\x0A)//) {
39     $line++;
40     $column = 0;
41     }
42     $column += length $v;
43     }
44    
45     if (length $$s <= pos $$s) {
46     return {type => 'eof'};
47     }
48    
49     ## ISSUE: "must"s in "A. IDL grammer" are not "MUST"s.
50    
51     if ($$s =~ /\G($identifier)/gc) {
52     my $v = $1;
53     $column += length $v;
54     if ({
55     module => 1, interface => 1, exception => 1, typedef => 1,
56     valuetype => 1, DOMString => 1, sequence => 1, unsigned => 1,
57     short => 1, const => 1, TRUE => 1, FALSE => 1, readonly => 1,
58     attribute => 1, getraises => 1, setraises => 1, raises => 1, in => 1,
59     any => 1, boolean => 1, octet => 1, float => 1, Object => 1,
60     short => 1, long => 1, void => 1,
61     }->{$v}) {
62     return {type => $v};
63     } else {
64     return {type => 'identifier', value => $v};
65     }
66     } elsif ($$s =~ /\G($float)/gc) { ## ISSUE: negative number
67     $column += length $1;
68     return {type => 'float', value => $1};
69     } elsif ($$s =~ /\G($integer)/gc) { ## ISSUE: negative nmber
70     $column += length $1;
71     return {type => 'integer', value => $1};
72     } elsif ($$s =~ /\G::/gcs) {
73     $column += 2;
74     return {type => '::'};
75     } elsif ($$s =~ /\G(.)/gcs) { ## NOTE: Should never be a newline char.
76     $column++;
77     return {type => $1};
78     } else {
79     die "get_next_token: non-possible case: " . substr ($$s, pos $$s, 20);
80     }
81     }; # $get_next_token
82    
83     my $state = 'before definitions';
84     my $token = $get_next_token->();
85     my $nest_level = 0;
86     my $next_state;
87     my $xattrs;
88     my $last_xattr;
89     my $read_only;
90     my $current_type;
91     my @current = ($defs);
92    
93     my $_onerror = $_[1] || sub {
94     my %opt = @_;
95     my $r = 'Line ' . $opt{line} . ' column ' . $opt{column} . ': ';
96    
97     if ($opt{token}) {
98     $r .= 'Token ' . (defined $opt{token}->{value} ? $opt{token}->{value} : $opt{token}->{type}) . ': ';
99     }
100    
101     $r .= $opt{type} . ';' . $opt{level};
102    
103     warn $r . "\n";
104     }; # $_onerror
105     my $onerror = sub {
106     $_onerror->(line => $line, column => $column, token => $token, @_);
107     }; # $onerror
108    
109     my $get_scoped_name = sub {
110     my $name = [];
111    
112     ## NOTE: "DOMString" is not a scoped name, while "::DOMString"
113     ## and "x::DOMString" are.
114    
115     if ($token->{type} eq 'identifier') {
116     ## TODO: unescape
117     push @$name, $token->{value};
118     $token = $get_next_token->();
119     while ($token->{type} eq '::') {
120 wakaba 1.2 $token = $get_next_token->();
121 wakaba 1.1 if ($token->{type} eq 'identifier') {
122     ## TODO: unescape
123     push @$name, $token->{value};
124     $token = $get_next_token->();
125     } elsif ($token->{type} eq 'DOMString') {
126     push @$name, '::DOMString::';
127     $token = $get_next_token->();
128     last;
129     }
130     }
131     } elsif ($token->{type} eq '::') {
132     push @$name, '';
133     while ($token->{type} eq '::') {
134 wakaba 1.2 $token = $get_next_token->();
135 wakaba 1.1 if ($token->{type} eq 'identifier') {
136     ## TODO: unescape
137     push @$name, $token->{value};
138     $token = $get_next_token->();
139     } elsif ($token->{type} eq 'DOMString') {
140     push @$name, '::DOMString::';
141     $token = $get_next_token->();
142     last;
143     } else {
144     last;
145     }
146     }
147    
148     if (@$name == 1) {
149     return undef;
150     }
151     } else {
152     # reconsume
153     return undef;
154     }
155     return $name;
156     }; # $get_scoped_name
157    
158     my $get_type;
159     $get_type = sub {
160     my $r;
161     if ({
162     void => 1, any => 1, boolean => 1, octet => 1, float => 1,
163     DOMString => 1, Object => 1, short => 1,
164     }->{$token->{type}}) {
165     $r = ['::'.$token->{type}.'::'];
166     $token = $get_next_token->();
167     } elsif ($token->{type} eq 'unsigned') {
168     $token = $get_next_token->();
169     if ($token->{type} eq 'short') {
170     $r = ['::unsigned '.$token->{type}.'::'];
171     $token = $get_next_token->();
172     } elsif ($token->{type} eq 'long') {
173     $token = $get_next_token->();
174     if ($token->{type} eq 'long') {
175     $r = ['::unsigned long long::'];
176     $token = $get_next_token->();
177     } else {
178     $r = ['::unsigned long::'];
179     # reconsume
180     }
181     } else {
182     $onerror->(type => 'unsigned', level => $self->{must_level});
183     return undef;
184     }
185     } elsif ($token->{type} eq 'long') {
186     $token = $get_next_token->();
187     if ($token->{type} eq 'long') {
188     $r = ['::long long::'];
189     $token = $get_next_token->();
190     } else {
191     $r = ['::long::'];
192     # reconsume
193     }
194     } elsif ($token->{type} eq '::' or $token->{type} eq 'identifier') {
195     $r = $get_scoped_name->();
196     if (defined $r) {
197     # next token
198     } else { # "::" not followed by identifier or "DOMString"
199     $onerror->(type => 'scoped name', level => $self->{must_level});
200     return undef;
201     }
202     } elsif ($token->{type} eq 'sequence') {
203     $token = $get_next_token->();
204     if ($token->{type} eq '<') {
205     $token = $get_next_token->();
206     if ({
207     void => 1, any => 1, boolean => 1, octet => 1, float => 1,
208     DOMString => 1, Object => 1, short => 1, long => 1, unsigned => 1,
209     sequence => 1, '::' => 1, identifier => 1,
210     }->{$token->{type}}) {
211     my $type = $get_type->();
212     if (defined $type) {
213     if ($token->{type} eq '>') {
214     $r = ['::sequence::', $type];
215     $token = $get_next_token->();
216     } else {
217     $onerror->(type => 'sequence gt', level => $self->{must_level});
218     return undef;
219     }
220     } else {
221     # error reported
222     return undef;
223     }
224     } else {
225     $onerror->(type => 'sequence type', level => $self->{must_level});
226     return undef;
227     }
228     } else {
229     $onerror->(type => 'sequence lt', level => $self->{must_level});
230     return undef;
231     }
232     } else {
233     die "get_type: bad token: $token->{type}";
234     }
235    
236     return $r;
237     }; # $get_type
238    
239     while (1) {
240     if ($state eq 'before definitions') {
241     if ($token->{type} eq '[') {
242     $xattrs = {};
243     $token = $get_next_token->();
244     $state = 'before xattr';
245     $next_state = 'before def';
246     } elsif ({module => 1, interface => 1, exception => 1,
247     typedef => 1, valuetype => 1, const => 1}->{$token->{type}}) {
248     $xattrs = {};
249     # reconsume
250     $state = 'before def';
251     } elsif ($token->{type} eq '}' and @current > 1) {
252     $token = $get_next_token->();
253     $state = 'before semicolon';
254     $next_state = 'before definitions';
255     } elsif ($token->{type} eq 'eof') {
256     last;
257     } else {
258     $onerror->(type => 'before definitions', level => 'm',
259     token => $token);
260     # reconsume
261     $state = 'ignore';
262     $nest_level = 0;
263     $next_state = 'before definitions';
264     }
265     } elsif ($state eq 'before xattr') {
266    
267     ## TODO: methodType methodName ([broken xattr] ...); is not well
268     ## handled on forward-compatible parsing (i.e. pop @current).
269    
270     if ($token->{type} eq 'identifier') {
271     ## TODO: _escape
272     ## ISSUE: Duplicate attributes
273     ## ISSUE: Unkown attributes
274     $xattrs->{$token->{value}} = '';
275     $last_xattr = $token->{value};
276     $token = $get_next_token->();
277     $state = 'after xattr';
278     } elsif ($token->{type} eq 'eof') {
279     $onerror->(type => 'before xattr:eof', level => 'm', token => $token);
280     $token = $get_next_token->();
281     $state = 'after xattrlist';
282     } else {
283     $onerror->(type => 'before xattr', level => 'm', token => $token);
284     # reconsume
285     $state = 'ignore';
286     $nest_level = 0;
287     $next_state = 'before definitions';
288     }
289     } elsif ($state eq 'after xattr') {
290     if ($token->{type} eq '=') {
291     $token = $get_next_token->();
292     $state = 'before xattrarg';
293     } else {
294     # reconsume
295     $state = 'after xattrarg';
296     }
297     } elsif ($state eq 'before xattrarg') {
298     if ($token->{type} eq 'identifier') {
299     ## TODO: escape
300     $xattrs->{$last_xattr} = $token->{value};
301     $token = $get_next_token->();
302     $state = 'after xattrarg';
303     } elsif ($token->{type} eq 'eof') {
304     # any extended attributes are ignored
305     $onerror->(type => 'after xattrarg:eof', level => 'm',
306     token => $token);
307     last;
308     } else {
309     $onerror->(type => 'after xattrarg', level => 'm', token => $token);
310     # reconsume
311     $state = 'ignore';
312     $nest_level = 0;
313     $next_state = 'before definitions';
314     }
315     } elsif ($state eq 'after xattrarg') {
316     if ($token->{type} eq ',') {
317     $token = $get_next_token->();
318     $state = 'before xattr';
319     } elsif ($token->{type} eq ']') {
320     $token = $get_next_token->();
321     $state = $next_state; # 'before def' or 'before interface member'
322     } elsif ($token->{type} eq 'eof') {
323     # any extended attributes are ignored
324     $onerror->(type => 'after xattr:eof', level => 'm', token => $token);
325     last;
326     } else {
327     $onerror->(type => 'after xattr', level => 'm', token => $token);
328     # reconsume
329     $state = 'ignore';
330     $nest_level = 0;
331     $next_state = 'before definitions';
332     }
333     } elsif ($state eq 'before def') {
334     if ($token->{type} eq 'module') {
335     $token = $get_next_token->();
336     if ($token->{type} eq 'identifier') {
337     ## TODO: escape
338     push @current, Whatpm::WebIDL::Module->new ($token->{value});
339     $token = $get_next_token->();
340     $state = 'before module block';
341     next;
342     } else {
343     $onerror->(type => 'module identifier',
344     level => $self->{must_level});
345     #
346     }
347     } elsif ($token->{type} eq 'interface') {
348     $token = $get_next_token->();
349     if ($token->{type} eq 'identifier') {
350     ## TODO: escape
351     push @current, Whatpm::WebIDL::Interface->new ($token->{value});
352     $token = $get_next_token->();
353     $state = 'before interface inheritance';
354     next;
355     } else {
356     $onerror->(type => 'interface identifier',
357     level => $self->{must_level});
358     #
359     }
360     } elsif ($token->{type} eq 'exception') {
361     $token = $get_next_token->();
362     if ($token->{type} eq 'identifier') {
363     ## TODO: escape
364     push @current, Whatpm::WebIDL::Exception->new ($token->{value});
365     $token = $get_next_token->();
366     $state = 'before exception block';
367     next;
368     } else {
369     $onerror->(type => 'exception identifier',
370     level => $self->{must_level});
371     #
372     }
373     } elsif ($token->{type} eq 'typedef') {
374     $token = $get_next_token->();
375     $state = 'before typedef type';
376     next;
377     } elsif ($token->{type} eq 'valuetype') {
378     $token = $get_next_token->();
379     if ($token->{type} eq 'identifier') {
380     ## TODO: escape
381     push @current, Whatpm::WebIDL::Valuetype->new ($token->{value});
382     $token = $get_next_token->();
383     $state = 'before boxed type';
384     next;
385     } elsif ($token->{type} eq 'DOMString') {
386     push @current, Whatpm::WebIDL::Valuetype->new ('::DOMString::');
387     $token = $get_next_token->();
388     if ($token->{type} eq 'sequence') {
389     $token = $get_next_token->();
390     if ($token->{type} eq '<') {
391     $token = $get_next_token->();
392     if ($token->{type} eq 'unsigned') {
393     $token = $get_next_token->();
394     if ($token->{type} eq 'short') {
395     $token = $get_next_token->();
396     if ($token->{type} eq '>') {
397     $current[-1]->type
398     (['::sequence::', ['::unsigned short::']]);
399     $token = $get_next_token->();
400     $state = 'before semicolon';
401     $next_state = 'before definitions';
402     next;
403     } else {
404     $onerror->(type => 'valuetype DOMString sequence lt unsigned short gt',
405     level => $self->{must_level});
406     #
407     }
408     } else {
409     $onerror->(type => 'valuetype DOMString sequence lt unsigned short',
410     level => $self->{must_level});
411     #
412     }
413     } else {
414     $onerror->(type => 'valuetype DOMString sequence lt unsigned',
415     level => $self->{must_level});
416     #
417     }
418     } else {
419     $onerror->(type => 'valuetype DOMString sequence lt',
420     level => $self->{must_level});
421     #
422     }
423     } else {
424     $onerror->(type => 'valuetype DOMString sequence',
425     level => $self->{must_level});
426     #
427     }
428     shift @current; # valuetype
429     #
430     } else {
431     $onerror->(type => 'valuetype identifier',
432     level => $self->{must_level});
433     #
434     }
435     } elsif ($token->{type} eq 'const') {
436     $token = $get_next_token->();
437     $state = 'before const type';
438     $next_state = 'before definitions';
439     next;
440     } elsif ($token->{type} eq 'eof') {
441     ## NOTE: Any extended attributes are ignored.
442     $onerror->(type => 'before def:eof', level => 'm', token => $token);
443     last;
444     } else {
445     $onerror->(type => 'before definition', level => 'm',
446     token => $token);
447     # reconsume
448     #
449     }
450     $state = 'ignore';
451     $nest_level = 0;
452     $next_state = 'before definitions';
453     } elsif ($state eq 'before module block') {
454     if ($token->{type} eq '{') {
455     $token = $get_next_token->();
456     $state = 'before definitions';
457     } else {
458     $onerror->(type => 'before module block', level => 'm',
459     token => $token);
460     pop @current; # module
461     # reconsume
462     $state = 'ignore';
463     $nest_level = 0;
464     $next_state = 'before definitions';
465     }
466     } elsif ($state eq 'before interface inheritance') {
467     if ($token->{type} eq ':') {
468     $token = $get_next_token->();
469     $state = 'before parent interface name';
470     } else {
471     # reconsume
472     $state = 'before interface block';
473     }
474     } elsif ($state eq 'before parent interface name') {
475     my $name = $get_scoped_name->();
476     if (defined $name) {
477     $current[-1]->append_inheritance ($name);
478    
479     if ($token->{type} eq ',') {
480     $token = $get_next_token->();
481     # stay in the state
482     } else {
483     # reconsume
484     $state = 'before interface block';
485     }
486     } else {
487     $onerror->(type => 'scoped name', level => $self->{must_level});
488     pop @current; # interface
489     # reconsume
490     $state = 'ignore';
491     $nest_level = 0;
492     $next_state = 'before definitions';
493     }
494 wakaba 1.2 } elsif ($state eq 'before exception name') {
495     my $name = $get_scoped_name->();
496     if (defined $name) {
497     my $method = $next_state eq '*raises' ? 'append_raises' :
498     $next_state eq '*getraises' ? 'append_getraises' :
499     'append_setraises';
500     $current[-1]->$method ($name);
501    
502     if ($token->{type} eq ',') {
503     $token = $get_next_token->();
504     # stay in the state
505     } elsif ($token->{type} eq ')') {
506     $token = $get_next_token->();
507     if ($next_state eq '*getraises' and $token->{type} eq 'setraises') {
508     $token = $get_next_token->();
509     $state = 'after raises';
510     $next_state = '*setraises';
511     } else {
512     # reprocess
513     $state = 'before semicolon';
514     $next_state = 'before interface member';
515     }
516     } else {
517     $onerror->(type => 'after exception name',
518     level => $self->{must_level});
519     pop @current; # operation/attribute
520     # reconsume
521     $state = 'ignore';
522     $nest_level = 0;
523     $next_state = 'before interface member';
524     }
525     } else {
526     $onerror->(type => 'scoped name', level => $self->{must_level});
527     pop @current; # operation/attribute
528     # reconsume
529     $state = 'ignore';
530     $nest_level = 0;
531     $next_state = 'before interface member';
532     }
533 wakaba 1.1 } elsif ($state eq 'before interface block') {
534     if ($token->{type} eq '{') {
535     $token = $get_next_token->();
536     $state = 'before members';
537     $next_state = 'before interface member';
538     } else {
539     $onerror->(type => 'before interface block',
540     level => $self->{must_level});
541     # reconsume
542     pop @current; # interface
543     $state = 'ignore';
544     $nest_level = 0;
545     $next_state = 'before definitions';
546     }
547     } elsif ($state eq 'before exception block') {
548     if ($token->{type} eq '{') {
549     $token = $get_next_token->();
550     $state = 'before members';
551     $next_state = 'before exception member';
552     } else {
553     $onerror->(type => 'before exception block',
554     level => $self->{must_level});
555     # reconsume
556     pop @current; # exception
557     $state = 'ignore';
558     $nest_level = 0;
559     $next_state = 'before definitions';
560     }
561     } elsif ($state eq 'before members') {
562     if ($token->{type} eq '[') {
563     $token = $get_next_token->();
564     $state = 'before xattr';
565     #$next_state = $next_state; # 'before interface member' or ...
566     } elsif ($token->{type} eq '}') {
567     $token = $get_next_token->();
568     $state = 'before semicolon';
569     $next_state = 'before definitions';
570     } else {
571     # reconsume
572     $state = $next_state; # ... 'before exception member'
573     }
574     } elsif ($state eq 'before interface member') {
575     if ($token->{type} eq 'const') {
576     $token = $get_next_token->();
577     $state = 'before const type';
578     $next_state = 'before definitions';
579     } elsif ($token->{type} eq 'readonly') {
580     $read_only = 1;
581     $token = $get_next_token->();
582     if ($token->{type} eq 'attribute') {
583     $token = $get_next_token->();
584     $state = 'after attribute';
585     } else {
586     # reconsume
587     $state = 'ignore';
588     $nest_level = 0;
589     $next_state = 'before interface member';
590     }
591     } elsif ($token->{type} eq 'attribute') {
592     $read_only = 0;
593     $token = $get_next_token->();
594     $state = 'after attribute';
595     } elsif ({
596     void => 1, any => 1, boolean => 1, octet => 1, float => 1,
597     DOMString => 1, Object => 1, unsigned => 1, short => 1, long => 1,
598     '::' => 1, identifier => 1,
599     }->{$token->{type}}) {
600     # reconsume
601     $state = 'before operation type';
602     } elsif ($token->{type} eq '}') {
603     $token = $get_next_token->();
604     $state = 'before semicolon';
605     $next_state = 'before definitions';
606     } elsif ($token->{type} eq 'eof') {
607     $onerror->(type => 'before interface member:eof',
608     level => $self->{must_level});
609     $current[-2]->append_child ($current[-1]);
610     last;
611     } else {
612     $onerror->(type => 'before interface member',
613     level => $self->{must_level});
614     # reconsume
615     pop @current; # interface
616     $state = 'ignore';
617     $nest_level = 0;
618     $next_state = 'before interface member';
619     }
620     } elsif ($state eq 'before exception member') {
621     if ({
622     void => 1, any => 1, boolean => 1, octet => 1, float => 1,
623     DOMString => 1, Object => 1, unsigned => 1, short => 1, long => 1,
624     '::' => 1, identifier => 1,
625     }->{$token->{type}}) {
626    
627     } elsif ($token->{type} eq '}') {
628     $token = $get_next_token->();
629     $state = 'before semicolon';
630     $next_state = 'before definitions';
631     } elsif ($token->{type} eq 'eof') {
632     $onerror->(type => 'before exception member:eof',
633     level => $self->{must_level});
634     $current[-2]->append_child ($current[-1]);
635     last;
636     } else {
637     $onerror->(type => 'before exception member',
638     level => $self->{must_level});
639     # reconsume
640     pop @current; # interface
641     $state = 'ignore';
642     $nest_level = 0;
643     $next_state = 'before exception member';
644     }
645     } elsif ($state eq 'before typedef type') {
646     if ({
647     void => 1, any => 1, boolean => 1, octet => 1, float => 1,
648     DOMString => 1, Object => 1, short => 1, long => 1, unsigned => 1,
649     sequence => 1, '::' => 1, identifier => 1,
650     }->{$token->{type}}) {
651     $current_type = $get_type->();
652     if (defined $current_type) {
653     # next token
654     $state = 'before typedef rest';
655     } else {
656     # reconsume
657     $state = 'ignore';
658     $nest_level = 0;
659     $next_state = 'before definitions';
660     }
661     } else {
662     $onerror->(type => 'before type', level => $self->{must_level});
663     # reconsume
664     $state = 'ignore';
665     $nest_level = 0;
666     $next_state = 'before definitions';
667     }
668     } elsif ($state eq 'before boxed type') {
669     if ({
670     boolean => 1, octet => 1, float => 1,
671     short => 1, long => 1, unsigned => 1,
672     sequence => 1, '::' => 1, identifier => 1,
673     }->{$token->{type}}) {
674     $current_type = $get_type->();
675     if (defined $current_type) {
676     $current[-1]->type ($current_type);
677     # next token
678     $state = 'before semicolon';
679     $next_state = 'before definitions';
680     } else {
681     shift @current; # valuetype
682     # reconsume
683     $state = 'ignore';
684     $nest_level = 0;
685     $next_state = 'before definitions';
686     }
687     } else {
688     $onerror->(type => 'before boxed type', level => $self->{must_level});
689     shift @current; # valuetype
690     # reconsume
691     $state = 'ignore';
692     $nest_level = 0;
693     $next_state = 'before definitions';
694     }
695     } elsif ($state eq 'before const type') {
696     if ({
697     any => 1, boolean => 1, octet => 1, float => 1,
698     DOMString => 1, Object => 1, short => 1, long => 1, unsigned => 1,
699     '::' => 1, identifier => 1,
700     }->{$token->{type}}) {
701     $current_type = $get_type->();
702     if (defined $current_type) {
703     # next token
704     $state = 'before const identifier';
705     } else {
706     # reconsume
707     $state = 'ignore';
708     $nest_level = 0;
709     #$next_state = $next_state;
710     }
711     } else {
712     $onerror->(type => 'before type', level => $self->{must_level});
713     # reconsume
714     $state = 'ignore';
715     $nest_level = 0;
716     #$next_state = $next_state;
717     }
718     } elsif ($state eq 'before typedef rest') {
719     if ($token->{type} eq 'identifier') {
720     ## TODO: unescape
721     push @current, Whatpm::WebIDL::Typedef->new ($token->{value});
722     $current[-1]->type ($current_type);
723     $token = $get_next_token->();
724     $state = 'before semicolon';
725     $next_state = 'before definitions';
726     } elsif ($token->{type} eq 'DOMString') {
727     push @current, Whatpm::WebIDL::Typedef->new ('::DOMString::');
728     $current[-1]->type ($current_type);
729     $token = $get_next_token->();
730     $state = 'before semicolon';
731     $next_state = 'before defnitions';
732     } else {
733     $onerror->(type => 'before typedef rest',
734     level => $self->{must_level});
735     # reconsume
736     $state = 'ignore';
737     $nest_level = 0;
738     $next_state = 'before definitions';
739     }
740     } elsif ($state eq 'before const identifier') {
741     if ($token->{type} eq 'identifier') {
742     ## TODO: unescape
743     push @current, Whatpm::WebIDL::Const->new ($token->{value});
744     $current[-1]->type ($current_type);
745     $token = $get_next_token->();
746     if ($token->{type} eq '=') {
747     $token = $get_next_token->();
748     $state = 'before const expr';
749     next;
750     } else {
751     $onerror->(type => 'const eq', level => $self->{must_level});
752     #
753     }
754     } else {
755     $onerror->(type => 'const identifier', level => $self->{must_level});
756     #
757     }
758     # reconsume
759     $state = 'ignore';
760     $nest_level = 0;
761     #$next_state = $next_state;
762     } elsif ($state eq 'before const expr') {
763     if ($token->{type} eq 'TRUE' or $token->{type} eq 'FALSE') {
764     $current[-1]->value ([$token->{type}]);
765     #
766     } elsif ($token->{type} eq 'integer' or $token->{type} eq 'float') {
767     $current[-1]->value ([$token->{type}, $token->{value}]);
768     #
769     } else {
770     # reconsume
771     $state = 'ignore';
772     $nest_level = 0;
773     #$next_state = $next_state;
774     next;
775     }
776    
777     $token = $get_next_token->();
778     $state = 'before semicolon';
779     #$next_state = $next_state;
780     } elsif ($state eq 'after attribute') {
781     if ({
782     any => 1, boolean => 1, octet => 1, float => 1,
783     DOMString => 1, Object => 1, short => 1, long => 1, unsigned => 1,
784     '::' => 1, identifier => 1,
785     }->{$token->{type}}) {
786     $current_type = $get_type->();
787     if (defined $current_type) {
788     # next token
789     $state = 'before attribute identifier';
790     } else {
791     # reconsume
792     $state = 'ignore';
793     $nest_level = 0;
794     $next_state = 'before interface member';
795     }
796     } else {
797     $onerror->(type => 'before type', level => $self->{must_level});
798     # reconsume
799     $state = 'ignore';
800     $nest_level = 0;
801     $next_state = 'before interface member';
802     }
803     } elsif ($state eq 'before operation type') {
804     if ({
805     any => 1, boolean => 1, octet => 1, float => 1,
806     DOMString => 1, Object => 1, short => 1, long => 1, unsigned => 1,
807     '::' => 1, identifier => 1,
808     void => 1,
809     }->{$token->{type}}) {
810     $current_type = $get_type->();
811     if (defined $current_type) {
812     # next token
813     $state = 'before operation identifier';
814     } else {
815     # reconsume
816     $state = 'ignore';
817     $nest_level = 0;
818     $next_state = 'before interface member';
819     }
820     } else {
821     $onerror->(type => 'before type', level => $self->{must_level});
822     # reconsume
823     $state = 'ignore';
824     $nest_level = 0;
825     $next_state = 'before interface member';
826     }
827     } elsif ($state eq 'before argument type') {
828     if ({
829     any => 1, boolean => 1, octet => 1, float => 1,
830     DOMString => 1, Object => 1, short => 1, long => 1, unsigned => 1,
831     '::' => 1, identifier => 1,
832     }->{$token->{type}}) {
833     $current_type = $get_type->();
834     if (defined $current_type) {
835     # next token
836     $state = 'before argument identifier';
837     } else {
838     # reconsume
839     pop @current; # operation
840     $state = 'ignore';
841     $nest_level = 0;
842     $next_state = 'before interface member';
843     }
844     } else {
845     $onerror->(type => 'before type', level => $self->{must_level});
846     pop @current; # operation
847     # reconsume
848     $state = 'ignore';
849     $nest_level = 0;
850     $next_state = 'before interface member';
851     }
852     } elsif ($state eq 'before attribute identifier') {
853     if ($token->{type} eq 'identifier') {
854     ## TODO: unescape
855     push @current, Whatpm::WebIDL::Attribute->new ($token->{value});
856     $current[-1]->readonly ($read_only);
857     $current[-1]->type ($current_type);
858     $token = $get_next_token->();
859     if ($token->{type} eq 'getraises') {
860     $token = $get_next_token->();
861 wakaba 1.2 $state = 'after raises';
862     $next_state = '*getraises';
863 wakaba 1.1 next;
864     } elsif ($token->{type} eq 'setraises') {
865     $token = $get_next_token->();
866 wakaba 1.2 $state = 'after raises';
867     $next_state = '*setraises';
868 wakaba 1.1 next;
869     } else {
870     # reconsume
871     $state = 'before semicolon';
872     $next_state = 'before interface member';
873     next;
874     }
875     } else {
876     $onerror->(type => 'attribute identifier',
877     level => $self->{must_level});
878     #
879     }
880     # reconsume
881     $state = 'ignore';
882     $nest_level = 0;
883     #$next_state = $next_state;
884     } elsif ($state eq 'before operation identifier') {
885     if ($token->{type} eq 'identifier') {
886     ## TODO: unescape
887     push @current, Whatpm::WebIDL::Operation->new ($token->{value});
888     $current[-1]->type ($current_type);
889     $token = $get_next_token->();
890     if ($token->{type} eq '(') {
891     $token = $get_next_token->();
892     if ($token->{type} eq ')') {
893     $token = $get_next_token->();
894 wakaba 1.2 $state = 'before raises';
895     $next_state = '*raises';
896 wakaba 1.1 next;
897     } else {
898     # reconsume
899     $state = 'before argument';
900     next;
901     }
902     } else {
903     pop @current;
904     $onerror->(type => 'arguments lparen',
905     level => $self->{must_level});
906     #
907     }
908     } else {
909     $onerror->(type => 'operation identifier',
910     level => $self->{must_level});
911     #
912     }
913     # reconsume
914     $state = 'ignore';
915     $nest_level = 0;
916     #$next_state = $next_state;
917     } elsif ($state eq 'before argument identifier') {
918     if ($token->{type} eq 'identifier') {
919     ## TODO: unescape
920     my $arg = Whatpm::WebIDL::Argument->new ($token->{value});
921     $arg->type ($current_type);
922     $current[-1]->append_child ($arg);
923     $token = $get_next_token->();
924     if ($token->{type} eq ')') {
925     $token = $get_next_token->();
926     $state = 'before raises';
927     next;
928     } elsif ($token->{type} eq ',') {
929     $token = $get_next_token->();
930     $state = 'before argument';
931     next;
932     } else {
933     $onerror->(type => 'after argument',
934     level => $self->{must_level});
935     #
936     }
937     } else {
938     $onerror->(type => 'argument identifier',
939     level => $self->{must_level});
940     #
941     }
942     pop @current; # operation
943     # reconsume
944     $state = 'ignore';
945     $nest_level = 0;
946     #$next_state = $next_state;
947     } elsif ($state eq 'before argument') {
948     if ($token->{type} eq '[') {
949     $token = $get_next_token->();
950     $state = 'before xattr';
951     $next_state = 'before argument in';
952     } else {
953     # reconsume
954     $state = 'before argument in';
955     }
956     } elsif ($state eq 'before argument in') {
957     if ($token->{type} eq 'in') {
958     $token = $get_next_token->();
959     $state = 'before argument type';
960     } else {
961     $onerror->(type => 'argument in',
962     level => $self->{must_level});
963     pop @current; # operation
964     $state = 'ignore';
965     $nest_level = 0;
966     $next_state = 'before interface member';
967     }
968     } elsif ($state eq 'before raises') {
969     if ($token->{type} eq 'raises') {
970     $token = $get_next_token->();
971     $state = 'after raises';
972 wakaba 1.2 $next_state = '*raises';
973 wakaba 1.1 } else {
974     # reconsume
975     $state = 'before semicolon';
976     $next_state = 'before interface member';
977     }
978     } elsif ($state eq 'after raises') {
979 wakaba 1.2 if ($token->{type} eq '(') {
980     $token = $get_next_token->();
981     $state = 'before exception name';
982     } else {
983     $onerror->(type => 'raises lparen',
984     level => $self->{must_level});
985     pop @current; # operation
986     $state = 'ignore';
987     $nest_level = 0;
988     $next_state = 'before interface member';
989     }
990 wakaba 1.1 } elsif ($state eq 'before semicolon') {
991     if ($token->{type} eq ';') {
992     $current[-2]->append_child ($current[-1]);
993     pop @current;
994     $token = $get_next_token->();
995     $state = {
996     #'before definitions',
997     'before interface member' => 'before members', # keep $next_state
998     'before exception member' => 'before members', # as is
999     }->{$next_state} || $next_state;
1000     } else {
1001     pop @current;
1002     $onerror->(type => 'before semicolon', level => 'm',
1003     token => $token);
1004     # reconsume
1005     $state = 'ignore';
1006     $nest_level = 0;
1007     $next_state = 'before definitions';
1008     }
1009     } elsif ($state eq 'ignore') {
1010     if (($nest_level == 0 and $token->{type} eq ';') or
1011     (@current > 1 and not $nest_level and $token->{type} eq '}')) {
1012     $token = $get_next_token->();
1013     $state = {
1014     #'before definitions',
1015     'before interface member' => 'before members', # keep $next_state
1016     'before exception member' => 'before members', # as is
1017     }->{$next_state} || $next_state;
1018     } elsif ($token->{type} eq '{') {
1019     $nest_level++;
1020     $token = $get_next_token->();
1021     # stay in the state
1022     } elsif ($nest_level and $token->{type} eq '}') {
1023     $nest_level--;
1024     $token = $get_next_token->();
1025     # stay in the state
1026     } elsif ($token->{type} eq 'eof') {
1027     last;
1028     } else {
1029     # ignore the token
1030     $token = $get_next_token->();
1031     # stay in the state
1032     }
1033     } else {
1034     die "parse_char_string: unkown state: $state";
1035     }
1036     }
1037    
1038     if (@current > 1) {
1039     $onerror->(type => 'block not closed', level => $self->{must_level});
1040     }
1041    
1042     $get_type = undef; # unlink loop
1043    
1044     return $defs;
1045     } # parse_char_string
1046    
1047     package Whatpm::WebIDL::Definitions;
1048    
1049     sub new ($) {
1050     return bless {child_nodes => []}, $_[0];
1051     } # new
1052    
1053     *append_child = \&Whatpm::WebIDL::Definition::append_child;
1054    
1055     sub idl_text ($) {
1056     return join "\x0A", map {$_->idl_text} @{$_[0]->{child_nodes}};
1057     } # idl_text
1058    
1059     package Whatpm::WebIDL::Definition;
1060    
1061     sub new ($$) {
1062     return bless {child_nodes => [], node_name => ''.$_[1]}, $_[0];
1063     } # new
1064    
1065     sub append_child ($$) {
1066     my $self = shift;
1067     my $child = shift;
1068    
1069     ## TODO: child type
1070     ## TODO: parent check
1071    
1072     push @{$self->{child_nodes}}, $child;
1073    
1074     return $child;
1075     } # append_child
1076    
1077     sub node_name ($) {
1078     return $_[0]->{node_name};
1079     } # node_name
1080    
1081     sub idl_text ($) {
1082     return '[[ERROR: ' . (ref $_[0]) . '->idl_text]]';
1083     } # idl_text
1084    
1085     sub type ($;$) {
1086     if (@_ > 1) {
1087     if (defined $_[1]) {
1088     $_[0]->{type} = $_[1];
1089     } else {
1090     $_[0]->{type} = ['::any::'];
1091     }
1092     }
1093     return $_[0]->{type};
1094     } # type
1095    
1096     sub type_text ($) {
1097     my $type = $_[0]->{type};
1098     return undef unless defined $type;
1099    
1100     if ($type->[0] eq '::sequence::') {
1101     return 'sequence<' . (join '::', @{$type->[1]}) . '>'; ## TODO: escape, nested
1102     } else {
1103     return join '::', @$type; ## TODO: escape
1104     }
1105     } # type_text
1106    
1107     package Whatpm::WebIDL::Module;
1108     push our @ISA, 'Whatpm::WebIDL::Definition';
1109    
1110     sub idl_text ($) {
1111     my $r = 'module ' . $_[0]->node_name . "{\x0A\x0A"; ## TODO: escape
1112     for (@{$_[0]->{child_nodes}}) {
1113     $r .= $_->idl_text;
1114     }
1115     $r .= "\x0A};\x0A";
1116     return $r;
1117     } # idl_text
1118    
1119     package Whatpm::WebIDL::Interface;
1120     push our @ISA, 'Whatpm::WebIDL::Definition';
1121    
1122     sub new ($$) {
1123     my $self = shift->SUPER::new (@_);
1124     $self->{inheritances} = [];
1125     return $self;
1126     } # new
1127    
1128     sub append_inheritance ($$) {
1129     my $self = shift;
1130     my $scoped_name = shift;
1131     push @{$self->{inheritances}}, $scoped_name;
1132     } # append_inheritance
1133    
1134     sub idl_text ($) {
1135     my $r = 'interface ' . $_[0]->node_name . "{\x0A"; ## TODO: escape
1136     for (@{$_[0]->{child_nodes}}) { ## TODO: inheritances
1137     $r .= ' ' . $_->idl_text;
1138     }
1139     $r .= "};\x0A";
1140     return $r;
1141     } # idl_text
1142    
1143     package Whatpm::WebIDL::Exception;
1144     push our @ISA, 'Whatpm::WebIDL::Definition';
1145    
1146     package Whatpm::WebIDL::Typedef;
1147     push our @ISA, 'Whatpm::WebIDL::Definition';
1148    
1149     sub new ($$) {
1150     my $self = shift->SUPER::new (@_);
1151     $self->{type} = ['::any::'];
1152     return $self;
1153     } # new
1154    
1155     sub idl_text ($) {
1156     ## TODO: escape
1157     return 'typedef ' . $_[0]->type_text . ' ' . $_[0]->node_name . ";\x0A";
1158     } # idl_text
1159    
1160     package Whatpm::WebIDL::Valuetype;
1161     push our @ISA, 'Whatpm::WebIDL::Definition';
1162    
1163     sub new ($$) {
1164     my $self = shift->SUPER::new (@_);
1165     $self->{type} = ['::boolean::'];
1166     return $self;
1167     } # new
1168    
1169     sub idl_text ($) {
1170     ## TODO: escape
1171     return 'valuetype ' . $_[0]->node_name . ' ' . $_[0]->type_text . ";\x0A";
1172     } # idl_text
1173    
1174     package Whatpm::WebIDL::InterfaceMember;
1175    
1176     sub new ($$) {
1177     return bless {node_name => ''.$_[1]}, $_[0];
1178     } # new
1179    
1180     *idl_text = \&Whatpm::WebIDL::Definition::idl_text;
1181    
1182     *node_name = \&Whatpm::WebIDL::Definition::node_name;
1183    
1184     *type = \&Whatpm::WebIDL::Definition::type;
1185    
1186     *type_text = \&Whatpm::WebIDL::Definition::type_text;
1187    
1188     package Whatpm::WebIDL::Const;
1189     push our @ISA, 'Whatpm::WebIDL::Definition', 'Whatpm::WebIDL::InterfaceMember';
1190    
1191     sub new ($$) {
1192     my $self = shift->SUPER::new (@_); # Definition->new should be called
1193     $self->{type} = ['::boolean::'];
1194     $self->{value} = ['FALSE'];
1195     return $self;
1196     } # new
1197    
1198     sub value ($;$) {
1199     if (@_ > 1) {
1200     $_[0]->{value} = $_[1];
1201     }
1202    
1203     return $_[0]->{value};
1204     } # value
1205    
1206     sub value_text ($) {
1207     my $value = $_[0]->{value};
1208    
1209     if ($value->[0] eq 'TRUE' or $value->[0] eq 'FALSE') {
1210     return $value->[0];
1211     } elsif ($value->[0] eq 'integer' or $value->[0] eq 'float') {
1212     ## TODO: format
1213     return $value->[1];
1214     } else {
1215     return undef;
1216     }
1217     } # value_text
1218    
1219     sub idl_text ($) {
1220     return 'const ' . $_[0]->type_text . ' ' . $_[0]->node_name . ' = ' . $_[0]->value_text . ";\x0A"; ## TODO: escape
1221     } # idl_text
1222    
1223     package Whatpm::WebIDL::Attribute;
1224     push our @ISA, 'Whatpm::WebIDL::InterfaceMember';
1225    
1226     sub new ($$) {
1227     my $self = shift->SUPER::new (@_);
1228     $self->{type} = ['::any::'];
1229 wakaba 1.2 $self->{getraises} = [];
1230     $self->{setraises} = [];
1231 wakaba 1.1 return $self;
1232     } # new
1233    
1234 wakaba 1.2 sub append_getraises ($$) {
1235     ## TODO: error check, etc.
1236     push @{$_[0]->{getraises}}, $_[1];
1237     } # append_getraises
1238    
1239     sub append_setraises ($$) {
1240     ## TODO: error check, etc.
1241     push @{$_[0]->{setraises}}, $_[1];
1242     } # append_setraises
1243    
1244 wakaba 1.1 sub readonly ($;$) {
1245     if (@_ > 1) {
1246     $_[0]->{readonly} = $_[1];
1247     }
1248    
1249     return $_[0]->{readonly};
1250     } # readonly
1251    
1252     sub idl_text ($) {
1253 wakaba 1.2 my $self = shift;
1254     my $r = ($self->readonly ? 'readonly ' : '') . 'attribute ' . $self->type_text . ' ' . $self->node_name;
1255 wakaba 1.1 ## TODO: escape
1256 wakaba 1.2 if (@{$self->{getraises}}) {
1257     $r .= ' getraises (';
1258     ## todo: ...
1259     $r .= join ', ', map {join '::', @{$_}} @{$self->{getraises}};
1260     $r .= ')';
1261     }
1262     if (@{$self->{setraises}}) {
1263     $r .= ' setraises (';
1264     ## todo: ...
1265     $r .= join ', ', map {join '::', @{$_}} @{$self->{setraises}};
1266     $r .= ')';
1267     }
1268     $r .= ";\x0A";
1269     return $r;
1270 wakaba 1.1 } # idl_text
1271    
1272     package Whatpm::WebIDL::Operation;
1273     push our @ISA, 'Whatpm::WebIDL::InterfaceMember';
1274    
1275     sub new ($$) {
1276     my $self = shift->SUPER::new (@_);
1277     $self->{type} = ['::any::'];
1278     $self->{child_nodes} = [];
1279 wakaba 1.2 $self->{raises} = [];
1280 wakaba 1.1 return $self;
1281     } # new
1282    
1283     *append_child = \&Whatpm::WebIDL::Definition::append_child;
1284    
1285 wakaba 1.2 sub append_raises ($$) {
1286     ## TODO: error check, etc.
1287     push @{$_[0]->{raises}}, $_[1];
1288     } # append_raises
1289    
1290 wakaba 1.1 sub idl_text ($) {
1291 wakaba 1.2 my $self = shift;
1292     my $r = $self->type_text . ' ' . $self->node_name . ' ('; ## TODO: escape
1293     $r .= join ', ', map {$_->idl_text} @{$self->{child_nodes}};
1294     $r .= ')';
1295     if (@{$self->{raises}}) {
1296     $r .= ' raises (';
1297     ## todo: ...
1298     $r .= join ', ', map {join '::', @{$_}} @{$self->{raises}};
1299     $r .= ')';
1300     }
1301 wakaba 1.1 $r .= ";\x0A";
1302     return $r;
1303     } # idl_text
1304    
1305     package Whatpm::WebIDL::Argument;
1306    
1307     sub new ($$) {
1308     return bless {node_name => ''.$_[1], type => ['::any::']}, $_[0];
1309     } # new
1310    
1311     sub idl_text ($) {
1312     return 'in ' . $_[0]->type_text . ' ' . $_[0]->node_name; ## TODO: escape
1313     } # idl_text
1314    
1315     *node_name = \&Whatpm::WebIDL::Definition::node_name;
1316    
1317     *type = \&Whatpm::WebIDL::Definition::type;
1318    
1319     *type_text = \&Whatpm::WebIDL::Definition::type_text;
1320    
1321     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24