/[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.3 - (hide annotations) (download)
Sat Jul 19 07:41:22 2008 UTC (16 years, 3 months ago) by wakaba
Branch: MAIN
Changes since 1.2: +77 -6 lines
++ whatpm/Whatpm/ChangeLog	19 Jul 2008 07:41:15 -0000
	* WebIDL.pm: Support for |exception| syntax.
	(Interface->idl_text): Tentative support for inheritances.

2008-07-19  Wakaba  <wakaba@suika.fam.cx>

1 wakaba 1.1 package Whatpm::WebIDL;
2     use strict;
3    
4     package Whatpm::WebIDL::Parser;
5    
6     my $integer = qr/-?0([0-7]*|[Xx][0-9A-Fa-f]+)|[1-9][0-9]*/;
7     my $float = qr/-?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+/;
8     my $identifier = qr/[A-Z_a-z][0-9A-Z_a-z]*/;
9     my $whitespace = qr<[\t\n\r ]+|[\t\n\r ]*((//.*|/\*.*?\*/)[\t\n\r ]*)+>;
10    
11     sub new ($) {
12     my $self = bless {
13     must_level => 'm',
14     should_level => 's',
15     warn_level => 'w',
16     info_level => 'i',
17     }, $_[0];
18     return $self;
19     } # new
20    
21     sub parse_char_string ($$;$) {
22     my $self = shift;
23     my $s = ref $_[0] ? $_[0] : \($_[0]);
24    
25     my $defs = Whatpm::WebIDL::Definitions->new;
26    
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 wakaba 1.3 # reconsume
627     $state = 'before exception member type';
628 wakaba 1.1 } elsif ($token->{type} eq '}') {
629     $token = $get_next_token->();
630     $state = 'before semicolon';
631     $next_state = 'before definitions';
632     } elsif ($token->{type} eq 'eof') {
633     $onerror->(type => 'before exception member:eof',
634     level => $self->{must_level});
635     $current[-2]->append_child ($current[-1]);
636     last;
637     } else {
638     $onerror->(type => 'before exception member',
639     level => $self->{must_level});
640     # reconsume
641     pop @current; # interface
642     $state = 'ignore';
643     $nest_level = 0;
644     $next_state = 'before exception member';
645     }
646     } elsif ($state eq 'before typedef type') {
647     if ({
648     void => 1, any => 1, boolean => 1, octet => 1, float => 1,
649     DOMString => 1, Object => 1, short => 1, long => 1, unsigned => 1,
650     sequence => 1, '::' => 1, identifier => 1,
651     }->{$token->{type}}) {
652     $current_type = $get_type->();
653     if (defined $current_type) {
654     # next token
655     $state = 'before typedef rest';
656     } else {
657     # reconsume
658     $state = 'ignore';
659     $nest_level = 0;
660     $next_state = 'before definitions';
661     }
662     } else {
663     $onerror->(type => 'before type', level => $self->{must_level});
664     # reconsume
665     $state = 'ignore';
666     $nest_level = 0;
667     $next_state = 'before definitions';
668     }
669     } elsif ($state eq 'before boxed type') {
670     if ({
671     boolean => 1, octet => 1, float => 1,
672     short => 1, long => 1, unsigned => 1,
673     sequence => 1, '::' => 1, identifier => 1,
674     }->{$token->{type}}) {
675     $current_type = $get_type->();
676     if (defined $current_type) {
677     $current[-1]->type ($current_type);
678     # next token
679     $state = 'before semicolon';
680     $next_state = 'before definitions';
681     } else {
682     shift @current; # valuetype
683     # reconsume
684     $state = 'ignore';
685     $nest_level = 0;
686     $next_state = 'before definitions';
687     }
688     } else {
689     $onerror->(type => 'before boxed type', level => $self->{must_level});
690     shift @current; # valuetype
691     # reconsume
692     $state = 'ignore';
693     $nest_level = 0;
694     $next_state = 'before definitions';
695     }
696     } elsif ($state eq 'before const type') {
697     if ({
698     any => 1, boolean => 1, octet => 1, float => 1,
699     DOMString => 1, Object => 1, short => 1, long => 1, unsigned => 1,
700     '::' => 1, identifier => 1,
701     }->{$token->{type}}) {
702     $current_type = $get_type->();
703     if (defined $current_type) {
704     # next token
705     $state = 'before const identifier';
706     } else {
707     # reconsume
708     $state = 'ignore';
709     $nest_level = 0;
710     #$next_state = $next_state;
711     }
712     } else {
713     $onerror->(type => 'before type', level => $self->{must_level});
714     # reconsume
715     $state = 'ignore';
716     $nest_level = 0;
717     #$next_state = $next_state;
718     }
719     } elsif ($state eq 'before typedef rest') {
720     if ($token->{type} eq 'identifier') {
721     ## TODO: unescape
722     push @current, Whatpm::WebIDL::Typedef->new ($token->{value});
723     $current[-1]->type ($current_type);
724     $token = $get_next_token->();
725     $state = 'before semicolon';
726     $next_state = 'before definitions';
727     } elsif ($token->{type} eq 'DOMString') {
728     push @current, Whatpm::WebIDL::Typedef->new ('::DOMString::');
729     $current[-1]->type ($current_type);
730     $token = $get_next_token->();
731     $state = 'before semicolon';
732     $next_state = 'before defnitions';
733     } else {
734     $onerror->(type => 'before typedef rest',
735     level => $self->{must_level});
736     # reconsume
737     $state = 'ignore';
738     $nest_level = 0;
739     $next_state = 'before definitions';
740     }
741     } elsif ($state eq 'before const identifier') {
742     if ($token->{type} eq 'identifier') {
743     ## TODO: unescape
744     push @current, Whatpm::WebIDL::Const->new ($token->{value});
745     $current[-1]->type ($current_type);
746     $token = $get_next_token->();
747     if ($token->{type} eq '=') {
748     $token = $get_next_token->();
749     $state = 'before const expr';
750     next;
751     } else {
752     $onerror->(type => 'const eq', level => $self->{must_level});
753     #
754     }
755     } else {
756     $onerror->(type => 'const identifier', level => $self->{must_level});
757     #
758     }
759     # reconsume
760     $state = 'ignore';
761     $nest_level = 0;
762     #$next_state = $next_state;
763     } elsif ($state eq 'before const expr') {
764     if ($token->{type} eq 'TRUE' or $token->{type} eq 'FALSE') {
765     $current[-1]->value ([$token->{type}]);
766     #
767     } elsif ($token->{type} eq 'integer' or $token->{type} eq 'float') {
768     $current[-1]->value ([$token->{type}, $token->{value}]);
769     #
770     } else {
771     # reconsume
772     $state = 'ignore';
773     $nest_level = 0;
774     #$next_state = $next_state;
775     next;
776     }
777    
778     $token = $get_next_token->();
779     $state = 'before semicolon';
780     #$next_state = $next_state;
781     } elsif ($state eq 'after attribute') {
782     if ({
783     any => 1, boolean => 1, octet => 1, float => 1,
784     DOMString => 1, Object => 1, short => 1, long => 1, unsigned => 1,
785     '::' => 1, identifier => 1,
786     }->{$token->{type}}) {
787     $current_type = $get_type->();
788     if (defined $current_type) {
789     # next token
790     $state = 'before attribute identifier';
791     } else {
792     # reconsume
793     $state = 'ignore';
794     $nest_level = 0;
795     $next_state = 'before interface member';
796     }
797     } else {
798     $onerror->(type => 'before type', level => $self->{must_level});
799     # reconsume
800     $state = 'ignore';
801     $nest_level = 0;
802     $next_state = 'before interface member';
803     }
804 wakaba 1.3 } elsif ($state eq 'before exception member type') {
805     if ({
806     any => 1, boolean => 1, octet => 1, float => 1,
807     DOMString => 1, Object => 1, short => 1, long => 1, unsigned => 1,
808     '::' => 1, identifier => 1,
809     }->{$token->{type}}) {
810     $current_type = $get_type->();
811     if (defined $current_type) {
812     # next token
813     $state = 'before exception member identifier';
814     } else {
815     # reconsume
816     $state = 'ignore';
817     $nest_level = 0;
818     $next_state = 'before exception 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 exception member';
826     }
827 wakaba 1.1 } elsif ($state eq 'before operation 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     void => 1,
833     }->{$token->{type}}) {
834     $current_type = $get_type->();
835     if (defined $current_type) {
836     # next token
837     $state = 'before operation identifier';
838     } else {
839     # reconsume
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     # reconsume
847     $state = 'ignore';
848     $nest_level = 0;
849     $next_state = 'before interface member';
850     }
851     } elsif ($state eq 'before argument type') {
852     if ({
853     any => 1, boolean => 1, octet => 1, float => 1,
854     DOMString => 1, Object => 1, short => 1, long => 1, unsigned => 1,
855     '::' => 1, identifier => 1,
856     }->{$token->{type}}) {
857     $current_type = $get_type->();
858     if (defined $current_type) {
859     # next token
860     $state = 'before argument identifier';
861     } else {
862     # reconsume
863     pop @current; # operation
864     $state = 'ignore';
865     $nest_level = 0;
866     $next_state = 'before interface member';
867     }
868     } else {
869     $onerror->(type => 'before type', level => $self->{must_level});
870     pop @current; # operation
871     # reconsume
872     $state = 'ignore';
873     $nest_level = 0;
874     $next_state = 'before interface member';
875     }
876     } elsif ($state eq 'before attribute identifier') {
877     if ($token->{type} eq 'identifier') {
878     ## TODO: unescape
879     push @current, Whatpm::WebIDL::Attribute->new ($token->{value});
880     $current[-1]->readonly ($read_only);
881     $current[-1]->type ($current_type);
882     $token = $get_next_token->();
883     if ($token->{type} eq 'getraises') {
884     $token = $get_next_token->();
885 wakaba 1.2 $state = 'after raises';
886     $next_state = '*getraises';
887 wakaba 1.1 next;
888     } elsif ($token->{type} eq 'setraises') {
889     $token = $get_next_token->();
890 wakaba 1.2 $state = 'after raises';
891     $next_state = '*setraises';
892 wakaba 1.1 next;
893     } else {
894     # reconsume
895     $state = 'before semicolon';
896     $next_state = 'before interface member';
897     next;
898     }
899     } else {
900     $onerror->(type => 'attribute identifier',
901     level => $self->{must_level});
902     #
903     }
904     # reconsume
905     $state = 'ignore';
906     $nest_level = 0;
907 wakaba 1.3 $next_state = 'before interface member';
908     } elsif ($state eq 'before exception member identifier') {
909     if ($token->{type} eq 'identifier') {
910     ## TODO: unescape
911     push @current, Whatpm::WebIDL::ExceptionMember->new ($token->{value});
912     $current[-1]->type ($current_type);
913     $token = $get_next_token->();
914     $state = 'before semicolon';
915     $next_state = 'before exception member';
916     } else {
917     $onerror->(type => 'exception member identifier',
918     level => $self->{must_level});
919     # reconsume
920     $state = 'ignore';
921     $nest_level = 0;
922     $next_state = 'before exception member';
923     }
924 wakaba 1.1 } elsif ($state eq 'before operation identifier') {
925     if ($token->{type} eq 'identifier') {
926     ## TODO: unescape
927     push @current, Whatpm::WebIDL::Operation->new ($token->{value});
928     $current[-1]->type ($current_type);
929     $token = $get_next_token->();
930     if ($token->{type} eq '(') {
931     $token = $get_next_token->();
932     if ($token->{type} eq ')') {
933     $token = $get_next_token->();
934 wakaba 1.2 $state = 'before raises';
935     $next_state = '*raises';
936 wakaba 1.1 next;
937     } else {
938     # reconsume
939     $state = 'before argument';
940     next;
941     }
942     } else {
943     pop @current;
944     $onerror->(type => 'arguments lparen',
945     level => $self->{must_level});
946     #
947     }
948     } else {
949     $onerror->(type => 'operation identifier',
950     level => $self->{must_level});
951     #
952     }
953     # reconsume
954     $state = 'ignore';
955     $nest_level = 0;
956 wakaba 1.3 $next_state = 'before interface member';
957 wakaba 1.1 } elsif ($state eq 'before argument identifier') {
958     if ($token->{type} eq 'identifier') {
959     ## TODO: unescape
960     my $arg = Whatpm::WebIDL::Argument->new ($token->{value});
961     $arg->type ($current_type);
962     $current[-1]->append_child ($arg);
963     $token = $get_next_token->();
964     if ($token->{type} eq ')') {
965     $token = $get_next_token->();
966     $state = 'before raises';
967     next;
968     } elsif ($token->{type} eq ',') {
969     $token = $get_next_token->();
970     $state = 'before argument';
971     next;
972     } else {
973     $onerror->(type => 'after argument',
974     level => $self->{must_level});
975     #
976     }
977     } else {
978     $onerror->(type => 'argument identifier',
979     level => $self->{must_level});
980     #
981     }
982     pop @current; # operation
983     # reconsume
984     $state = 'ignore';
985     $nest_level = 0;
986 wakaba 1.3 $next_state = 'before interface member';
987 wakaba 1.1 } elsif ($state eq 'before argument') {
988     if ($token->{type} eq '[') {
989     $token = $get_next_token->();
990     $state = 'before xattr';
991     $next_state = 'before argument in';
992     } else {
993     # reconsume
994     $state = 'before argument in';
995     }
996     } elsif ($state eq 'before argument in') {
997     if ($token->{type} eq 'in') {
998     $token = $get_next_token->();
999     $state = 'before argument type';
1000     } else {
1001     $onerror->(type => 'argument in',
1002     level => $self->{must_level});
1003     pop @current; # operation
1004     $state = 'ignore';
1005     $nest_level = 0;
1006     $next_state = 'before interface member';
1007     }
1008     } elsif ($state eq 'before raises') {
1009     if ($token->{type} eq 'raises') {
1010     $token = $get_next_token->();
1011     $state = 'after raises';
1012 wakaba 1.2 $next_state = '*raises';
1013 wakaba 1.1 } else {
1014     # reconsume
1015     $state = 'before semicolon';
1016     $next_state = 'before interface member';
1017     }
1018     } elsif ($state eq 'after raises') {
1019 wakaba 1.2 if ($token->{type} eq '(') {
1020     $token = $get_next_token->();
1021     $state = 'before exception name';
1022     } else {
1023     $onerror->(type => 'raises lparen',
1024     level => $self->{must_level});
1025     pop @current; # operation
1026     $state = 'ignore';
1027     $nest_level = 0;
1028     $next_state = 'before interface member';
1029     }
1030 wakaba 1.1 } elsif ($state eq 'before semicolon') {
1031     if ($token->{type} eq ';') {
1032     $current[-2]->append_child ($current[-1]);
1033     pop @current;
1034     $token = $get_next_token->();
1035     $state = {
1036     #'before definitions',
1037     'before interface member' => 'before members', # keep $next_state
1038     'before exception member' => 'before members', # as is
1039     }->{$next_state} || $next_state;
1040     } else {
1041     pop @current;
1042     $onerror->(type => 'before semicolon', level => 'm',
1043     token => $token);
1044     # reconsume
1045     $state = 'ignore';
1046     $nest_level = 0;
1047     $next_state = 'before definitions';
1048     }
1049     } elsif ($state eq 'ignore') {
1050     if (($nest_level == 0 and $token->{type} eq ';') or
1051     (@current > 1 and not $nest_level and $token->{type} eq '}')) {
1052     $token = $get_next_token->();
1053     $state = {
1054     #'before definitions',
1055     'before interface member' => 'before members', # keep $next_state
1056     'before exception member' => 'before members', # as is
1057     }->{$next_state} || $next_state;
1058     } elsif ($token->{type} eq '{') {
1059     $nest_level++;
1060     $token = $get_next_token->();
1061     # stay in the state
1062     } elsif ($nest_level and $token->{type} eq '}') {
1063     $nest_level--;
1064     $token = $get_next_token->();
1065     # stay in the state
1066     } elsif ($token->{type} eq 'eof') {
1067     last;
1068     } else {
1069     # ignore the token
1070     $token = $get_next_token->();
1071     # stay in the state
1072     }
1073     } else {
1074     die "parse_char_string: unkown state: $state";
1075     }
1076     }
1077    
1078     if (@current > 1) {
1079     $onerror->(type => 'block not closed', level => $self->{must_level});
1080     }
1081    
1082     $get_type = undef; # unlink loop
1083    
1084     return $defs;
1085     } # parse_char_string
1086    
1087     package Whatpm::WebIDL::Definitions;
1088    
1089     sub new ($) {
1090     return bless {child_nodes => []}, $_[0];
1091     } # new
1092    
1093     *append_child = \&Whatpm::WebIDL::Definition::append_child;
1094    
1095     sub idl_text ($) {
1096     return join "\x0A", map {$_->idl_text} @{$_[0]->{child_nodes}};
1097     } # idl_text
1098    
1099     package Whatpm::WebIDL::Definition;
1100    
1101     sub new ($$) {
1102     return bless {child_nodes => [], node_name => ''.$_[1]}, $_[0];
1103     } # new
1104    
1105     sub append_child ($$) {
1106     my $self = shift;
1107     my $child = shift;
1108    
1109     ## TODO: child type
1110     ## TODO: parent check
1111    
1112     push @{$self->{child_nodes}}, $child;
1113    
1114     return $child;
1115     } # append_child
1116    
1117     sub node_name ($) {
1118     return $_[0]->{node_name};
1119     } # node_name
1120    
1121     sub idl_text ($) {
1122     return '[[ERROR: ' . (ref $_[0]) . '->idl_text]]';
1123     } # idl_text
1124    
1125     sub type ($;$) {
1126     if (@_ > 1) {
1127     if (defined $_[1]) {
1128     $_[0]->{type} = $_[1];
1129     } else {
1130     $_[0]->{type} = ['::any::'];
1131     }
1132     }
1133     return $_[0]->{type};
1134     } # type
1135    
1136     sub type_text ($) {
1137     my $type = $_[0]->{type};
1138     return undef unless defined $type;
1139    
1140     if ($type->[0] eq '::sequence::') {
1141     return 'sequence<' . (join '::', @{$type->[1]}) . '>'; ## TODO: escape, nested
1142     } else {
1143     return join '::', @$type; ## TODO: escape
1144     }
1145     } # type_text
1146    
1147     package Whatpm::WebIDL::Module;
1148     push our @ISA, 'Whatpm::WebIDL::Definition';
1149    
1150     sub idl_text ($) {
1151     my $r = 'module ' . $_[0]->node_name . "{\x0A\x0A"; ## TODO: escape
1152     for (@{$_[0]->{child_nodes}}) {
1153     $r .= $_->idl_text;
1154     }
1155     $r .= "\x0A};\x0A";
1156     return $r;
1157     } # idl_text
1158    
1159     package Whatpm::WebIDL::Interface;
1160     push our @ISA, 'Whatpm::WebIDL::Definition';
1161    
1162     sub new ($$) {
1163     my $self = shift->SUPER::new (@_);
1164     $self->{inheritances} = [];
1165     return $self;
1166     } # new
1167    
1168     sub append_inheritance ($$) {
1169     my $self = shift;
1170     my $scoped_name = shift;
1171     push @{$self->{inheritances}}, $scoped_name;
1172     } # append_inheritance
1173    
1174     sub idl_text ($) {
1175 wakaba 1.3 my $self = shift;
1176     my $r = 'interface ' . $self->node_name;
1177     if (@{$self->{inheritances}}) {
1178     $r .= ' : '; ## TODO: ...
1179     $r .= join ', ', map {join '::', @{$_}} @{$self->{inheritances}};
1180     }
1181     $r .= " {\x0A"; ## TODO: escape
1182     for (@{$self->{child_nodes}}) {
1183 wakaba 1.1 $r .= ' ' . $_->idl_text;
1184     }
1185     $r .= "};\x0A";
1186     return $r;
1187     } # idl_text
1188    
1189     package Whatpm::WebIDL::Exception;
1190     push our @ISA, 'Whatpm::WebIDL::Definition';
1191    
1192 wakaba 1.3 sub idl_text ($) {
1193     my $r = 'exception ' . $_[0]->node_name . "{\x0A"; ## TODO: escape
1194     for (@{$_[0]->{child_nodes}}) {
1195     $r .= ' ' . $_->idl_text;
1196     }
1197     $r .= "};\x0A";
1198     return $r;
1199     } # idl_text
1200    
1201 wakaba 1.1 package Whatpm::WebIDL::Typedef;
1202     push our @ISA, 'Whatpm::WebIDL::Definition';
1203    
1204     sub new ($$) {
1205     my $self = shift->SUPER::new (@_);
1206     $self->{type} = ['::any::'];
1207     return $self;
1208     } # new
1209    
1210     sub idl_text ($) {
1211     ## TODO: escape
1212     return 'typedef ' . $_[0]->type_text . ' ' . $_[0]->node_name . ";\x0A";
1213     } # idl_text
1214    
1215     package Whatpm::WebIDL::Valuetype;
1216     push our @ISA, 'Whatpm::WebIDL::Definition';
1217    
1218     sub new ($$) {
1219     my $self = shift->SUPER::new (@_);
1220     $self->{type} = ['::boolean::'];
1221     return $self;
1222     } # new
1223    
1224     sub idl_text ($) {
1225     ## TODO: escape
1226     return 'valuetype ' . $_[0]->node_name . ' ' . $_[0]->type_text . ";\x0A";
1227     } # idl_text
1228    
1229     package Whatpm::WebIDL::InterfaceMember;
1230    
1231     sub new ($$) {
1232     return bless {node_name => ''.$_[1]}, $_[0];
1233     } # new
1234    
1235     *idl_text = \&Whatpm::WebIDL::Definition::idl_text;
1236    
1237     *node_name = \&Whatpm::WebIDL::Definition::node_name;
1238    
1239     *type = \&Whatpm::WebIDL::Definition::type;
1240    
1241     *type_text = \&Whatpm::WebIDL::Definition::type_text;
1242    
1243     package Whatpm::WebIDL::Const;
1244     push our @ISA, 'Whatpm::WebIDL::Definition', 'Whatpm::WebIDL::InterfaceMember';
1245    
1246     sub new ($$) {
1247     my $self = shift->SUPER::new (@_); # Definition->new should be called
1248     $self->{type} = ['::boolean::'];
1249     $self->{value} = ['FALSE'];
1250     return $self;
1251     } # new
1252    
1253     sub value ($;$) {
1254     if (@_ > 1) {
1255     $_[0]->{value} = $_[1];
1256     }
1257    
1258     return $_[0]->{value};
1259     } # value
1260    
1261     sub value_text ($) {
1262     my $value = $_[0]->{value};
1263    
1264     if ($value->[0] eq 'TRUE' or $value->[0] eq 'FALSE') {
1265     return $value->[0];
1266     } elsif ($value->[0] eq 'integer' or $value->[0] eq 'float') {
1267     ## TODO: format
1268     return $value->[1];
1269     } else {
1270     return undef;
1271     }
1272     } # value_text
1273    
1274     sub idl_text ($) {
1275     return 'const ' . $_[0]->type_text . ' ' . $_[0]->node_name . ' = ' . $_[0]->value_text . ";\x0A"; ## TODO: escape
1276     } # idl_text
1277    
1278     package Whatpm::WebIDL::Attribute;
1279     push our @ISA, 'Whatpm::WebIDL::InterfaceMember';
1280    
1281     sub new ($$) {
1282     my $self = shift->SUPER::new (@_);
1283     $self->{type} = ['::any::'];
1284 wakaba 1.2 $self->{getraises} = [];
1285     $self->{setraises} = [];
1286 wakaba 1.1 return $self;
1287     } # new
1288    
1289 wakaba 1.2 sub append_getraises ($$) {
1290     ## TODO: error check, etc.
1291     push @{$_[0]->{getraises}}, $_[1];
1292     } # append_getraises
1293    
1294     sub append_setraises ($$) {
1295     ## TODO: error check, etc.
1296     push @{$_[0]->{setraises}}, $_[1];
1297     } # append_setraises
1298    
1299 wakaba 1.1 sub readonly ($;$) {
1300     if (@_ > 1) {
1301     $_[0]->{readonly} = $_[1];
1302     }
1303    
1304     return $_[0]->{readonly};
1305     } # readonly
1306    
1307     sub idl_text ($) {
1308 wakaba 1.2 my $self = shift;
1309     my $r = ($self->readonly ? 'readonly ' : '') . 'attribute ' . $self->type_text . ' ' . $self->node_name;
1310 wakaba 1.1 ## TODO: escape
1311 wakaba 1.2 if (@{$self->{getraises}}) {
1312     $r .= ' getraises (';
1313     ## todo: ...
1314     $r .= join ', ', map {join '::', @{$_}} @{$self->{getraises}};
1315     $r .= ')';
1316     }
1317     if (@{$self->{setraises}}) {
1318     $r .= ' setraises (';
1319     ## todo: ...
1320     $r .= join ', ', map {join '::', @{$_}} @{$self->{setraises}};
1321     $r .= ')';
1322     }
1323     $r .= ";\x0A";
1324     return $r;
1325 wakaba 1.1 } # idl_text
1326    
1327     package Whatpm::WebIDL::Operation;
1328     push our @ISA, 'Whatpm::WebIDL::InterfaceMember';
1329    
1330     sub new ($$) {
1331     my $self = shift->SUPER::new (@_);
1332     $self->{type} = ['::any::'];
1333     $self->{child_nodes} = [];
1334 wakaba 1.2 $self->{raises} = [];
1335 wakaba 1.1 return $self;
1336     } # new
1337    
1338     *append_child = \&Whatpm::WebIDL::Definition::append_child;
1339    
1340 wakaba 1.2 sub append_raises ($$) {
1341     ## TODO: error check, etc.
1342     push @{$_[0]->{raises}}, $_[1];
1343     } # append_raises
1344    
1345 wakaba 1.1 sub idl_text ($) {
1346 wakaba 1.2 my $self = shift;
1347     my $r = $self->type_text . ' ' . $self->node_name . ' ('; ## TODO: escape
1348     $r .= join ', ', map {$_->idl_text} @{$self->{child_nodes}};
1349     $r .= ')';
1350     if (@{$self->{raises}}) {
1351     $r .= ' raises (';
1352     ## todo: ...
1353     $r .= join ', ', map {join '::', @{$_}} @{$self->{raises}};
1354     $r .= ')';
1355     }
1356 wakaba 1.1 $r .= ";\x0A";
1357     return $r;
1358     } # idl_text
1359    
1360     package Whatpm::WebIDL::Argument;
1361    
1362     sub new ($$) {
1363     return bless {node_name => ''.$_[1], type => ['::any::']}, $_[0];
1364     } # new
1365    
1366     sub idl_text ($) {
1367     return 'in ' . $_[0]->type_text . ' ' . $_[0]->node_name; ## TODO: escape
1368 wakaba 1.3 } # idl_text
1369    
1370     *node_name = \&Whatpm::WebIDL::Definition::node_name;
1371    
1372     *type = \&Whatpm::WebIDL::Definition::type;
1373    
1374     *type_text = \&Whatpm::WebIDL::Definition::type_text;
1375    
1376     package Whatpm::WebIDL::ExceptionMember;
1377    
1378     sub new ($$) {
1379     return bless {node_name => ''.$_[1], type => ['::any::']}, $_[0];
1380     } # new
1381    
1382     sub idl_text ($) {
1383     return $_[0]->type_text . ' ' . $_[0]->node_name . ";\x0A"; ## TODO: escape
1384 wakaba 1.1 } # idl_text
1385    
1386     *node_name = \&Whatpm::WebIDL::Definition::node_name;
1387    
1388     *type = \&Whatpm::WebIDL::Definition::type;
1389    
1390     *type_text = \&Whatpm::WebIDL::Definition::type_text;
1391    
1392     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24