File Coverage

blib/lib/Tangence/Compiler/Parser.pm
Criterion Covered Total %
statement 95 95 100.0
branch 16 24 66.6
condition 3 3 100.0
subroutine 20 20 100.0
pod 0 2 0.0
total 134 144 93.0


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