File Coverage

blib/lib/Inline/Spew.pm
Criterion Covered Total %
statement 64 69 92.7
branch 15 26 57.6
condition 3 4 75.0
subroutine 11 12 91.6
pod 6 6 100.0
total 99 117 84.6


line stmt bran cond sub pod time code
1             package Inline::Spew;
2              
3 2     2   67665 use 5.006;
  2         9  
  2         89  
4 2     2   14 use strict;
  2         4  
  2         75  
5 2     2   24 use warnings;
  2         4  
  2         1016  
6              
7             require Exporter;
8             require Inline;
9             require YAML;
10              
11             our @ISA = qw(Inline Exporter);
12              
13             our %EXPORT_TAGS = ( 'all' => [ qw(
14            
15             ) ] );
16              
17             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
18              
19             our @EXPORT = qw(
20            
21             );
22              
23             our $VERSION = '0.02';
24              
25             sub register {
26             return {
27 0     0 1 0 language => 'Spew',
28             type => 'interpreted',
29             suffix => 'spew',
30             };
31             }
32              
33 2     2 1 59 sub validate {
34             ## warn "validate called with:\n", YAML::Dump(@_), "\n\n";
35             }
36              
37             sub build {
38 1     1 1 17 my $o = shift;
39 1         3 my $code = $o->{API}{code};
40 1         4 my $location = "$o->{API}{location}";
41              
42 1         9 require File::Basename;
43 1         72 my $directory = File::Basename::dirname($location);
44 1 50       48 $o->mkpath($directory) unless -d $directory;
45              
46 1         8 my $spew = spew_compile($code);
47              
48 1         25 YAML::DumpFile($location, $spew);
49             }
50              
51             sub load {
52 2     2 1 29414 my $o = shift;
53              
54 2         5 my $sub = do {
55 2   100     34 my $s = $o->{CONFIG}{SUB} || "spew";
56 2 50       16 unless ($s =~ /::/) {
57 2         9 $s = $o->{API}{pkg}."::$s";
58             }
59 2         9 $s;
60             };
61 2         7 my $location = $o->{API}{location};
62 2         11 my @result = YAML::LoadFile($location);
63              
64             {
65 2     2   12 no strict 'refs';
  2         5  
  2         1872  
  2         248867  
66             *$sub = sub {
67 2   50 2   1735 my $start = shift || "START";
68 2         12 return spew_show($result[0], $start);
69 2         47 };
70             }
71             }
72              
73             sub spew_show {
74 54     54 1 99 my ($parsed, $defn) = @_;
75 54 50       150 die "missing defn for $defn" unless exists $parsed->{$defn};
76              
77 54         58 my @choices = @{$parsed->{$defn}{is}};
  54         238  
78 54         67 my $weight = 0;
79 54         74 my @keeper = ();
80 54         108 while (@choices) {
81 708         760 my ($thisweight, @thisitem) = @{pop @choices};
  708         1285  
82 708 50       1428 $thisweight = 0 if $thisweight < 0; # no funny stuff
83 708         746 $weight += $thisweight;
84 708 100       2419 @keeper = @thisitem if rand($weight) < $thisweight;
85             }
86 54         66 my $result;
87 54         79 for (@keeper) {
88             ## should be a list of ids or defns
89 125 50       230 die "huh $_ in $defn" if ref $defn;
90 125 100       493 if (/^ (.*)/s) {
    50          
91 73         175 $result .= $1;
92             } elsif (/^(\w+)$/) {
93 52         112 $result .= spew_show($parsed, $1);
94             } else {
95 0         0 die "Can't show $_ in $defn\n";
96             }
97             }
98 54         212 return $result;
99             }
100              
101             BEGIN {
102              
103 2     2   6 my $parser;
104 2         72 my $GRAMMAR = q{
105             ## return hashref
106             ## { ident => {
107             ## is => [
108             ## [weight => item, item, item, ...],
109             ## [weight => item, item, item, ...], ...
110             ## ],
111             ## defined => { line-number => times }
112             ## used => { line-number => times }
113             ## }, ...
114             ## }
115             ## item is " literal" or ident
116             ## ident is C-symbol or number (internal for nested rules)
117              
118             { my %grammar; my $internal = 0; }
119              
120             grammar: rule(s) /\Z/ { \%grammar; }
121              
122             ## rule returns identifier (not used)
123             rule: identifier ":" defn {
124             push @{$grammar{$item[1]}{is}}, @{$item[3]};
125             $grammar{$item[1]}{defined}{$itempos[1]{line}{to}}++;
126             $item[1];
127             }
128             |
129              
130             ## defn returns listref of choices
131             defn:
132              
133             ## choice returns a listref of [weight => @items]
134             choice: weight unweightedchoice { [ $item[1] => @{$item[2]} ] }
135              
136             ## weight returns weight if present, 1 if not
137             weight: /\d+(\.\d+)?/ /\@/ { $item[1] } | { 1 }
138              
139             ## unweightedchoice returns a listref of @items
140             unweightedchoice: item(s)
141              
142             ## item returns " literal text" or "identifier"
143             item:
144             { $_ = extract_quotelike($text) and " " . eval }
145             | identifier ...!/:/ { # must not be followed by colon!
146             $grammar{$item[1]}{used}{$itempos[1]{line}{to}}++;
147             $item[1]; # non-leading space flags an identifier
148             }
149             | "(" defn ")" { # parens for recursion, gensym an internal
150             ++$internal;
151             push @{$grammar{$internal}{is}}, @{$item[2]};
152             $internal;
153             }
154             |
155              
156             identifier: /[^\W\d]\w*/
157             };
158              
159             sub spew_compile {
160 1     1 1 2 my $source = shift;
161              
162 1 50       5 unless ($parser) {
163 1         2838 require Parse::RecDescent;
164 1 50       60419 $parser = Parse::RecDescent->new($GRAMMAR) or die "internal bad";
165             }
166              
167 1 50       72699 my $parsed = $parser->grammar($source) or die "bad spew grammar";
168              
169 1         16675 for my $id (sort keys %$parsed) {
170 1 50       18 next if $id =~ /^\d+$/; # skip internals
171 1         5 my $id_ref = $parsed->{$id};
172 1 50       14 unless (exists $id_ref->{defined}) {
173 0         0 die "$id used in @{[sort keys %{$id_ref->{used}}]} but not defined";
  0         0  
  0         0  
174             }
175             ## unless (exists $id_ref->{used} or $id eq $START) {
176             ## warn "$id defined in @{[sort keys %{$id_ref->{defined}}]} but not used";
177             ## }
178             }
179              
180 1         17 return $parsed;
181             }
182             }
183              
184             1;
185             __END__