File Coverage

blib/lib/Tangence/Compiler/Parser.pm
Criterion Covered Total %
statement 149 150 99.3
branch 24 36 66.6
condition 3 3 100.0
subroutine 32 32 100.0
pod 8 15 53.3
total 216 236 91.5


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2011-2022 -- leonerd@leonerd.org.uk
5              
6 13     13   67654 use v5.26;
  13         56  
7 13     13   602 use Object::Pad 0.57;
  13         10374  
  13         65  
8              
9             package Tangence::Compiler::Parser 0.30;
10 13     13   9370 class Tangence::Compiler::Parser :isa(Parser::MGC);
  13         111221  
  13         645  
11              
12 13     13   8292 use Syntax::Keyword::Dynamically;
  13         9216  
  13         1648  
13 13     13   1256 use Syntax::Keyword::Match;
  13         768  
  13         89  
14              
15 13     13   846 use File::Basename qw( dirname );
  13         29  
  13         1065  
16              
17 13     13   506 use Tangence::Constants;
  13         24  
  13         3850  
18              
19             # Parsing is simpler if we treat Package.Name as a simple identifier
20 13     13   92 use constant pattern_ident => qr/[[:alnum:]_][\w.]*/;
  13         34  
  13         1108  
21              
22 13     13   89 use constant pattern_comment => qr/#.*\n/;
  13         26  
  13         54874  
23              
24             =head1 NAME
25              
26             C - parse C interface definition files
27              
28             =head1 DESCRIPTION
29              
30             This subclass of L parses a L interface definition and
31             returns a metadata tree.
32              
33             =cut
34              
35             =head1 GRAMMAR
36              
37             The top level of an interface definition file contains C directives
38             and C and C definitions.
39              
40             =head2 include
41              
42             An C directive imports the definitions from another file, named
43             relative to the current file.
44              
45             include "filename.tan"
46              
47             =head2 class
48              
49             A C definition defines the set of methods, events and properties
50             defined by a named class.
51              
52             class N {
53             ...
54             }
55              
56             The contents of the class block will be a list of C, C, C
57             and C declarations.
58              
59             =head2 struct
60              
61             A C definition defines the list of fields contained within a named
62             structure type.
63              
64             struct N {
65             ...
66             }
67              
68             The contents of the struct block will be a list of C declarations.
69              
70             =cut
71              
72             field $_package;
73              
74             # Parser::MGC version 0.20 adds this method. Before then, this workaround is
75             # known to be safe
76             if( $Parser::MGC::VERSION < 0.20 ) {
77             *filename = sub ( $self ) { $self->{filename} };
78             }
79              
80             method parse
81 15     15 0 2750 {
82 15         110 dynamically $_package = \my %package;
83              
84 15         103 while( !$self->at_eos ) {
85             match( $self->token_kw(qw( class struct include )) : eq ) {
86             case( 'class' ) {
87 15         2808 my $classname = $self->token_ident;
88              
89 15 50       962 exists $package{$classname} and
90             $self->fail( "Already have a class or struct called $classname" );
91              
92 15         75 my $class = $self->make_class( name => $classname );
93 15         56 $package{$classname} = $class;
94              
95 15     15   150 $self->scope_of( '{', sub { $self->parse_classblock( $class ) }, '}' ),
  15         1679  
96             }
97             case( 'struct' ) {
98 11         951 my $structname = $self->token_ident;
99              
100 11 50       673 exists $package{$structname} and
101             $self->fail( "Already have a class or struct called $structname" );
102              
103 11         66 my $struct = $self->make_struct( name => $structname );
104 11         46 $package{$structname} = $struct;
105              
106 11     11   77 $self->scope_of( '{', sub { $self->parse_structblock( $struct ) }, '}' ),
  11         1009  
107             }
108             case( 'include' ) {
109 2         490 my $filename = dirname($self->filename) . "/" . $self->token_string;
110              
111 2         500 my $subparser = (ref $self)->new;
112 2         13 my $included = $subparser->from_file( $filename );
113              
114 2         104 foreach my $classname ( keys %$included ) {
115 2 50       9 exists $package{$classname} and
116             $self->fail( "Cannot include '$filename' as class $classname collides" );
117              
118 2         25 $package{$classname} = $included->{$classname};
119             }
120             }
121 28 100       2032 default {
    100          
    50          
122 0         0 $self->fail( "Expected keyword, found $_" );
123             }
124             }
125             }
126              
127 15         1263 return \%package;
128             }
129              
130             =head2 method
131              
132             A C declaration defines one method in the class, giving its name (N)
133             and types of its arguments and and return (T).
134              
135             method N(T, T, ...) -> T;
136              
137             =head2 event
138              
139             An C declaration defines one event raised by the class, giving its name
140             (N) and types of its arguments (T).
141              
142             event N(T, T, ...);
143              
144             =head2 prop
145              
146             A C declaration defines one property supported by the class, giving its
147             name (N), dimension (D) and type (T). It may be declared as a C
148             property.
149              
150             [smashed] prop N = D of T;
151              
152             Scalar properties may omit the C, by supplying just the type
153              
154             [smashed] prop N = T;
155              
156             =head2 isa
157              
158             An C declaration declares a superclass of the class, by its name (C)
159              
160             isa C;
161              
162             =cut
163              
164 15         35 method parse_classblock ( $class )
  15         30  
  15         29  
165 15     15 0 66 {
166 15         79 my %methods;
167             my %events;
168 15         0 my %properties;
169 15         0 my @superclasses;
170              
171 15         61 while( !$self->at_eos ) {
172             match( $_ = $self->token_kw(qw( method event prop smashed isa )) : eq ) {
173             case( 'method' ) {
174 24         2066 my $methodname = $self->token_ident;
175              
176 24 50       1459 exists $methods{$methodname} and
177             $self->fail( "Already have a method called $methodname" );
178              
179 24         103 my $args = $self->parse_arglist;
180 24         1984 my $ret;
181              
182             $self->maybe( sub {
183 24     24   352 $self->expect( '->' );
184              
185 13         654 $ret = $self->parse_type;
186 24         171 } );
187              
188 24         1070 $methods{$methodname} = $self->make_method(
189             class => $class,
190             name => $methodname,
191             arguments => $args,
192             ret => $ret,
193             );
194             }
195              
196             case( 'event' ) {
197 13         1177 my $eventname = $self->token_ident;
198              
199 13 50       781 exists $events{$eventname} and
200             $self->fail( "Already have an event called $eventname" );
201              
202 13         49 my $args = $self->parse_arglist;
203              
204 13         1030 $events{$eventname} = $self->make_event(
205             class => $class,
206             name => $eventname,
207             arguments => $args,
208             );
209             }
210              
211             case( 'smashed' ), case( 'prop' ) {
212 92         7837 my $smashed = 0;
213              
214 92 100       264 if( $_ eq 'smashed' ) {
215 24         70 $smashed = 1;
216 24         77 $self->expect( 'prop' );
217             }
218              
219 92         1455 my $propname = $self->token_ident;
220              
221 92 50       5461 exists $properties{$propname} and
222             $self->fail( "Already have a property called $propname" );
223              
224 92         284 $self->expect( '=' );
225              
226 92         4416 my $dim = DIM_SCALAR;
227             $self->maybe( sub {
228 92     92   1404 $dim = $self->parse_dim;
229 55         152 $self->expect( 'of' );
230 92         537 } );
231              
232 92         7817 my $type = $self->parse_type;
233              
234 92         613 $properties{$propname} = $self->make_property(
235             class => $class,
236             name => $propname,
237             smashed => $smashed,
238             dimension => $dim,
239             type => $type,
240             );
241             }
242              
243 131 100 100     9778 case( 'isa' ) {
    100          
    100          
    50          
244 2         179 my $supername = $self->token_ident;
245              
246 2 50       123 my $super = $_package->{$supername} or
247             $self->fail( "Unrecognised superclass $supername" );
248              
249 2         5 push @superclasses, $super;
250             }
251             }
252              
253 131         378 $self->expect( ';' );
254             }
255              
256             $class->define(
257 15         1271 methods => \%methods,
258             events => \%events,
259             properties => \%properties,
260             superclasses => \@superclasses,
261             );
262             }
263              
264             method parse_arglist
265 37     37 0 113 {
266             return $self->scope_of(
267             "(",
268 37     37   2587 sub { $self->list_of( ",", \&parse_arg ) },
269 37         211 ")",
270             );
271             }
272              
273             method parse_arg
274 48     48 0 2959 {
275 48         82 my $name;
276 48         143 my $type = $self->parse_type;
277             $self->maybe( sub {
278 48     48   676 $name = $self->token_ident;
279 48         477 } );
280 48         3163 return $self->make_argument( name => $name, type => $type );
281             }
282              
283 11         24 method parse_structblock ( $struct )
  11         34  
  11         28  
284 11     11 0 47 {
285 11         28 my @fields;
286             my %fieldnames;
287              
288 11         47 while( !$self->at_eos ) {
289             match( $self->token_kw(qw( field )) : eq ) {
290 55 50       3698 case( 'field' ) {
291 55         4319 my $fieldname = $self->token_ident;
292              
293 55 50       3165 exists $fieldnames{$fieldname} and
294             $self->fail( "Already have a field called $fieldname" );
295              
296 55         163 $self->expect( '=' );
297              
298 55         2706 my $type = $self->parse_type;
299              
300 55         380 push @fields, $self->make_field(
301             name => $fieldname,
302             type => $type,
303             );
304 55         156 $fieldnames{$fieldname}++;
305             }
306             }
307 55         170 $self->expect( ';' );
308             }
309              
310             $struct->define(
311 11         899 fields => \@fields,
312             );
313             }
314              
315             =head2 Types
316              
317             The following basic type names are recognised
318              
319             bool int str obj any
320             s8 s16 s32 s64 u8 u16 u32 u64
321              
322             Aggregate types may be formed of any type (T) by
323              
324             list(T) dict(T)
325              
326             =cut
327              
328             my @basic_types = qw(
329             bool
330             int
331             s8 s16 s32 s64 u8 u16 u32 u64
332             float
333             float16 float32 float64
334             str
335             obj
336             any
337             );
338              
339             method parse_type
340 219     219 0 1301 {
341             $self->any_of(
342             sub {
343 219     219   3217 my $aggregate = $self->token_kw(qw( list dict ));
344              
345 11         951 $self->commit;
346              
347 11         132 my $membertype = $self->scope_of( "(", \&parse_type, ")" );
348              
349 11         575 return $self->make_type( $aggregate => $membertype );
350             },
351             sub {
352 208     208   26478 my $typename = $self->token_ident;
353              
354 208 50       12398 grep { $_ eq $typename } @basic_types or
  3536         5827  
355             $self->fail( "'$typename' is not a typename" );
356              
357 208         630 return $self->make_type( $typename );
358             },
359 219         1322 );
360             }
361              
362             my %dimensions = (
363             scalar => DIM_SCALAR,
364             hash => DIM_HASH,
365             queue => DIM_QUEUE,
366             array => DIM_ARRAY,
367             objset => DIM_OBJSET,
368             );
369              
370             method parse_dim
371 92     92 0 265 {
372 92         352 my $dimname = $self->token_kw( keys %dimensions );
373              
374 55         4731 return $dimensions{$dimname};
375             }
376              
377             =head1 SUBCLASS METHODS
378              
379             If this class is subclassed, the following methods may be overridden to
380             customise the behaviour. They allow the subclass to return different objects
381             in the syntax tree.
382              
383             =cut
384              
385             =head2 make_class
386              
387             $class = $parser->make_class( name => $name )
388              
389             Return a new instance of L to go in a package. The
390             parser will call C on it.
391              
392             =cut
393              
394             method make_class
395 3     3 1 8 {
396 3         529 require Tangence::Meta::Class;
397 3         31 return Tangence::Meta::Class->new( @_ );
398             }
399              
400             =head2 make_struct
401              
402             $struct = $parser->make_struct( name => $name )
403              
404             Return a new instance of L to go in a package. The
405             parser will call C on it.
406              
407             =cut
408              
409             method make_struct
410 1     1 1 12 {
411 1         571 require Tangence::Meta::Struct;
412 1         12 return Tangence::Meta::Struct->new( @_ );
413             }
414              
415             =head2 make_method
416              
417             $method = $parser->make_method( %args )
418              
419             =head2 make_event
420              
421             $event = $parser->make_event( %args )
422              
423             =head2 make_property
424              
425             $property = $parser->make_property( %args )
426              
427             Return a new instance of L, L
428             or L to go in a class.
429              
430             =cut
431              
432             method make_method
433 24     24 1 78 {
434 24         567 require Tangence::Meta::Method;
435 24         248 return Tangence::Meta::Method->new( @_ );
436             }
437              
438             method make_event
439 13     13 1 56 {
440 13         501 require Tangence::Meta::Event;
441 13         160 return Tangence::Meta::Event->new( @_ );
442             }
443              
444             method make_property
445 10     10 1 24 {
446 10         501 require Tangence::Meta::Property;
447 10         60 return Tangence::Meta::Property->new( @_ );
448             }
449              
450             =head2 make_argument
451              
452             $argument = $parser->make_argument( %args )
453              
454             Return a new instance of L to use for a method
455             or event argument.
456              
457             =cut
458              
459             method make_argument
460 48     48 1 138 {
461 48         680 require Tangence::Meta::Argument;
462 48         313 return Tangence::Meta::Argument->new( @_ );
463             }
464              
465             =head2 make_field
466              
467             $field = $parser->make_field( %args )
468              
469             Return a new instance of L to use for a structure type.
470              
471             =cut
472              
473             method make_field
474 55     55 1 143 {
475 55         830 require Tangence::Meta::Field;
476 55         312 return Tangence::Meta::Field->new( @_ );
477             }
478              
479             =head2 make_type
480              
481             $type = $parser->make_type( $primitive_name )
482              
483             $type = $parser->make_type( $aggregate_name => $member_type )
484              
485             Return an instance of L representing the given
486             primitive or aggregate type name. An implementation is allowed to use
487             singleton objects and return identical objects for the same primitive name or
488             aggregate and member type.
489              
490             =cut
491              
492             method make_type
493 24     24 1 59 {
494 24         592 require Tangence::Meta::Type;
495 24         105 return Tangence::Meta::Type->make( @_ );
496             }
497              
498             =head1 AUTHOR
499              
500             Paul Evans
501              
502             =cut
503              
504             0x55AA;