File Coverage

blib/lib/Data/DEC.pm
Criterion Covered Total %
statement 18 110 16.3
branch 0 24 0.0
condition n/a
subroutine 6 16 37.5
pod 0 5 0.0
total 24 155 15.4


line stmt bran cond sub pod time code
1             package Data::DEC;
2              
3 1     1   83575 use 5.014002;
  1         3  
  1         36  
4 1     1   6 use strict;
  1         1  
  1         35  
5 1     1   5 use warnings;
  1         8  
  1         46  
6              
7 1     1   928 use Parse::Highlife;
  1         111610  
  1         37  
8 1     1   14 use Parse::Highlife::Utils qw(dump_ast);
  1         2  
  1         62  
9 1     1   824 use Data::DEC::Declaration;
  1         4  
  1         1363  
10             #use Data::Dump qw(dump);
11              
12             # require Exporter;
13             #
14             # our @ISA = qw(Exporter);
15             #
16             # # Items to export into callers namespace by default. Note: do not export
17             # # names by default without a very good reason. Use EXPORT_OK instead.
18             # # Do not simply export all your public functions/methods/constants.
19             #
20             # # This allows declaration use Data::DEC ':all';
21             # # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
22             # # will save memory.
23             # our %EXPORT_TAGS = ( 'all' => [ qw(
24             #
25             # ) ] );
26             #
27             # our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
28             #
29             # our @EXPORT = qw(
30             #
31             # );
32              
33             our $VERSION = '0.03';
34              
35             our $Grammar = q{
36              
37             space ignored: /\s\n\r\t+/;
38              
39             multiline-comment ignored: "/*" .. "*/";
40              
41             singleline-comment ignored: /\#[^\n\r]*/;
42              
43             file: { declaration 0..* };
44            
45             declaration: [ "@" identifier ] literal;
46            
47             literal: < map string real number identifier >;
48            
49             map: [ symbol ] "[" { pair 0..* } "]";
50            
51             pair: [ symbol ":" ] declaration;
52            
53             string: < double-quoted-string single-quoted-string >;
54            
55             double-quoted-string: '"' .. '"';
56            
57             single-quoted-string: "'" .. "'";
58            
59             real: /\d+\.\d+/;
60            
61             number: /\d+/;
62            
63             identifier: symbol { "." symbol 0..* };
64              
65             symbol: /[\w\d]+(\-[\w\d]+)*/;
66            
67             };
68              
69             sub new
70             {
71 0     0 0   my( $class, @args ) = @_;
72 0           my $self = bless {}, $class;
73 0           return $self->_init( @args );
74             }
75              
76             sub _init
77             {
78 0     0     my( $self, @filenames ) = @_;
79              
80             # raw DEC content
81 0           $self->{'filenames'} = [ @filenames ];
82            
83             # the parsed declarations
84 0           $self->{'declarations'} = [];
85            
86             # setup compiler
87 0           my $compiler = Parse::Highlife -> Compiler;
88 0           $compiler->grammar( $Grammar );
89 0           $compiler->toprule( -name => 'file' );
90             $compiler->transformer(
91             -rule => 'declaration',
92 0     0     -fn => sub { _declaration_from_ast( $self->{'declarations'}, @_ ) },
93 0           );
94 0           $self->{'dec-compiler'} = $compiler;
95              
96             # compile document
97 0           $compiler -> compile( @{$self->{'filenames'}} );
  0            
98 0           $self->_delete_temporary_references();
99            
100 0           return $self;
101             }
102              
103             sub validate
104             {
105 0     0 0   my( $self, $decs ) = @_;
106 0           warn "Data::DEC::validate() is not implemented, yet.\n";
107             # ...
108 0           return 1;
109             }
110              
111             sub dump
112             {
113 0     0 0   my( $self ) = @_;
114 0           map{ $_->dump() } @{$self->{'declarations'}};
  0            
  0            
115             }
116              
117             sub declarations
118             {
119 0     0 0   my( $self ) = @_;
120 0           return @{$self->{'declarations'}};
  0            
121             }
122              
123             # identifier declarations can only resolved after all declarations
124             # are parsed, but then all indirect references to declarations
125             # can be turned into direct ones (eliminating unesessary declarations)
126             sub _delete_temporary_references
127             {
128 0     0     my( $self ) = @_;
129 0           while( $self->_has_temporary_references() )
130             {
131 0           my @ids_to_delete;
132             map {
133 0 0         if( $_->{'type'} eq 'map' ) {
  0            
134             # check all keys
135 0           foreach my $e (0..scalar(@{$_->{'value'}})-1) {
  0            
136 0           my $entry = $_->{'value'}->[$e];
137 0           my( $name, $value ) = @{$entry};
  0            
138 0 0         if( $value->{'type'} eq 'identifier' ) {
139 0           $_->{'value'}->[$e]->[1] = $self->find_declaration_by_name( $value->{'value'} );
140 0           push @ids_to_delete, $value->{name};
141             }
142             }
143             }
144             }
145 0           @{$self->{'declarations'}};
146            
147             # filter out the ones that are unecessary
148 0           $self->{'declarations'} = [
149             grep {
150 0           my $d = $_;
151 0           scalar(grep { $_ eq $d->{'name'} } @ids_to_delete) == 0;
  0            
152             }
153 0           @{$self->{'declarations'}}
154             ];
155             }
156             }
157              
158             # returns 1 if a declaration exists that is an identifier
159             sub _has_temporary_references
160             {
161 0     0     my( $self ) = @_;
162 0 0         map {
163 0           return 1 if $_->{'type'} eq 'identifier';
164             }
165 0           @{$self->{'declarations'}};
166 0           return 0;
167             }
168              
169             sub find_declaration_by_name
170             {
171 0     0 0   my( $self, $name ) = @_;
172             map {
173 0 0         if( $_->{'name'} eq $name ) {
  0            
174 0           return $_;
175             }
176             }
177 0           @{$self->{'declarations'}};
178 0           return undef;
179             }
180              
181             sub _declaration_from_ast
182             {
183 0     0     my( $declarations_result, $transformer, $ast ) = @_;
184              
185 0           my $name = '';
186 0 0         if( $ast->first_child()->has_ancestor('symbol') ) {
187 0           $name = join '.', map { $_->value() } $ast->first_child()->ancestors('symbol');
  0            
188             }
189              
190 0           my $type = '';
191 0           my $valuetype = '';
192 0           my $value = '';
193 0 0         if( $ast->ancestor('literal')->has_ancestor('map') ) {
    0          
    0          
    0          
    0          
194 0           $type = 'map';
195 0           $valuetype = $ast->ancestor('literal')->first_child('symbol')->value();
196 0 0         $valuetype = '' if $valuetype eq '['; # unnamed map declaration!
197 0           $value = [];
198 0           my $unnamed_counter = 0;
199 0           foreach my $pair (@{$ast->ancestor('literal')->ancestor('map')->third_child()->{'children'}}) {
  0            
200 0           my $entry_name;
201 0 0         if( $pair->first_child()->has_ancestor('symbol') ) {
202 0           $entry_name = $pair->first_child()->ancestor('symbol')->value();
203             }
204             else {
205 0           $entry_name = $unnamed_counter;
206 0           $unnamed_counter ++;
207             }
208 0           my $entry_value = _declaration_from_ast( $declarations_result, $transformer, $pair->second_child() );
209 0           push @{$value}, [ $entry_name, $entry_value ];
  0            
210             }
211             }
212             elsif( $ast->ancestor('literal')->has_ancestor('identifier') ) {
213 0           $type = 'identifier';
214 0           $value = $ast->ancestor('literal')->ancestor('identifier')->value();
215             }
216             elsif( $ast->ancestor('literal')->has_ancestor('number') ) {
217 0           $type = 'number';
218 0           $value = $ast->ancestor('literal')->ancestor('number')->value();
219             }
220             elsif( $ast->ancestor('literal')->has_ancestor('real') ) {
221 0           $type = 'real';
222 0           $value = $ast->ancestor('literal')->ancestor('real')->value();
223             }
224             elsif( $ast->ancestor('literal')->has_ancestor('string') ) {
225 0           $type = 'string';
226 0           $value = $ast->ancestor('literal')->ancestor('string')->value();
227             }
228            
229 0           my $decl = Data::DEC::Declaration->new( $name, $type, $value, $valuetype );
230              
231 0           push @{$declarations_result}, $decl;
  0            
232 0           return $decl;
233             }
234              
235             1;
236             __END__