File Coverage

blib/lib/Polyglot.pm
Criterion Covered Total %
statement 21 97 21.6
branch 0 24 0.0
condition 0 3 0.0
subroutine 7 23 30.4
pod 9 9 100.0
total 37 156 23.7


line stmt bran cond sub pod time code
1             package Polyglot;
2 1     1   480 use strict;
  1         1  
  1         27  
3 1     1   3 use vars qw($VERSION);
  1         0  
  1         34  
4              
5 1     1   7 use warnings;
  1         1  
  1         18  
6 1     1   3 no warnings;
  1         1  
  1         41  
7              
8             =encoding utf8
9              
10             =head1 NAME
11              
12             Polyglot - a little language interpreter
13              
14             =head1 SYNOPSIS
15              
16             # THIS IS ALPHA SOFTWARE
17              
18             use Polyglot;
19              
20             my $interpreter = Polyglot->new();
21              
22             $polyglot->add_action( ... );
23              
24             $interpreter->run();
25              
26             =head1 DESCRIPTION
27              
28             This module implements a simple, little language interpreter to
29             describe the language.
30              
31             For this interpreter, a program is a series of lines with one
32             directive perl line. The first group of non-whitespace characters in
33             the line is a the I and the remainder of the line becomes
34             its arguments. The interpreter reads one line, does what it says,
35             then moves to the next line until it reaches the end of the file. If
36             the interpreter does not read from a file, it prompts for standard
37             input.
38              
39             A small program to control an CD player may look like:
40              
41             VOLUME 5
42             PLAY
43             SLEEP 50
44             STOP
45             EJECT
46              
47             The interpreter does not support loops, conditionals, or other fancy
48             things, and I do not have plans to add those things.
49              
50             The interpret provides a few commands, but I expect other people to
51             create their own little languages specialized for their task. Most of
52             the methods deal with creating the language description at the
53             interpreter level. I plan on creating another layer above this to
54             make the language description even more simple.
55              
56             =cut
57              
58 1     1   407 use autouse 'Data::Dumper' => 'Dumper';
  1         595  
  1         5  
59              
60 1     1   43 use Carp qw(carp);
  1         1  
  1         39  
61 1     1   438 use Text::ParseWords qw( quotewords );
  1         874  
  1         810  
62              
63             $VERSION = '1.002';
64              
65             my $Debug = $ENV{DEBUG} || 0;
66              
67             =head2 Methods
68              
69             =over 4
70              
71             =item new
72              
73             Creates a new Polyglot object and returns it.
74              
75             =cut
76              
77             sub new
78             {
79 0     0 1   my( $class, @args ) = @_;
80              
81 0           my $self = bless {}, $class;
82              
83             $self->add_action( 'POLYGLOT',
84             sub {
85 0     0     my( $self, $package ) = @_;
86 0           eval{ eval "require $package" };
  0            
87 0           } );
88 0     0     $self->add_action( 'HELP', sub { my $self = shift; $self->help( @_ ) } );
  0            
  0            
89 0     0     $self->add_action( 'EXIT', sub { exit } );
  0            
90 0     0     $self->add_action( 'REFLECT', sub { print Dumper( $_[0] ) } );
  0            
91             $self->add_action( 'SHOW', sub {
92 0     0     my( $self, $name ) = ( shift, uc shift );
93 0           print "$name = ", $self->value($name), "\n";
94 0           $self; } );
  0            
95             }
96              
97             =item run
98              
99             Start the interpreter. It will read lines from the file names in
100             @ARGV or from standard input using the diamond operator. It splits
101             lines on whitespace and assumes the first element of that list is
102             the directive name. If the directive does not exist, it prints a
103             warning and continues.
104              
105             This method uses the diamond operator and assumes that nothing else
106             has mucked with it.
107              
108             =cut
109              
110             sub run
111             {
112 0     0 1   my $self = shift;
113              
114 0           my $prompt = "$0> ";
115              
116 0 0         print "H: Waiting for commands on standard input\n$prompt"
117             unless @ARGV;
118              
119 0           while( <> )
120             {
121 0           print "$ARGV\[$.]: $_";
122 0           chomp;
123 0 0         next if /^\s*#?$/;
124 0           my( $directive, $string ) = split /\s+/, $_, 2;
125              
126 0           $directive = uc $directive;
127 0 0         carp "DEBUG: directive is $directive\n" if $Debug;
128              
129 0           my @arguments = quotewords( '\s+', 0, $string );
130 0 0         carp "DEBUG: arguments are @arguments\n" if $Debug;
131              
132 0           eval {
133 0 0         die "Undefined subroutine" unless exists $self->{$directive};
134 0           $self->{$directive}[1]( $self, @arguments );
135             };
136              
137 0 0         warn "Not a valid directive: [$directive] at $ARGV line $.\n"
138             if $@ =~ m/Undefined subroutine/;
139              
140 0 0         print "$prompt" if $ARGV eq '-';
141             }
142              
143 0           print "\n";
144             }
145              
146             =item state
147              
148             Returns the string used to mark a directive that affects the program
149             state.
150              
151             =cut
152              
153             sub state () { 'state' }
154              
155             =item action
156              
157             Returns the string used to mark a directive that performs an action.
158              
159             =cut
160              
161             sub action () { 'action' }
162              
163              
164             =item add( DIRECTIVE, TYPE, CODEREF, INITIAL_VALUE, HELP )
165              
166             Adds DIRECTIVE to the little language with TYPE (state or action).
167             The value of the directive (for those that represent program state) is
168             INITIAL_VALUE or undef. The CODEREF is executed when the interpreter
169             encounters the directive. The built-in HELP directive returns the
170             HELP string for this DIRECTIVE.
171              
172             =cut
173              
174             sub add
175             {
176 0     0 1   my( $self, $name, $state, $sub, $value, $help ) = @_;
177              
178 0           $self->{$name} = [ $state, $sub, $help ];
179              
180 0           $self;
181             }
182              
183             =item value( DIRECTIVE [, VALUE ] )
184              
185             Returns the value for DIRECTIVE, or sets it if you specify VALUE.
186              
187             =cut
188              
189             sub value
190             {
191 0     0 1   my( $self, $name, $value ) = @_;
192 0 0         carp "Setting $name with $value\n" if $Debug;
193              
194 0 0         return unless exists $self->{ $name };
195              
196 0 0         return $self->{$name}[2] unless defined $value;
197              
198 0           $self->{$name}[2] = $value;
199              
200             }
201              
202             =item add_action( DIRECTIVE, CODEREF, INITIAL_VALUE, HELP )
203              
204             Like add(), but without TYPE which is automatically filled in. Use
205             this for a directive that does something other than setting a value.
206              
207             The CODEREF can be anything. The first argument is always the
208             interpreter object, and the rest of the arguments are from the current
209             line.
210              
211             =cut
212              
213             sub add_action
214             {
215 0     0 1   my $self = shift;
216 0           my $name = uc shift;
217 0           my( $sub, $value, $help ) = @_;
218              
219 0           $self->{$name} = [ $self->action, $sub, $value, $help ];
220              
221 0           $self;
222             }
223              
224             =item add_state( DIRECTIVE, INITIAL_VALUE, HELP )
225              
226             Like add(), but without TYPE and CODEREF which is automatically filled in.
227             Use this for a directive that can set a value.
228              
229             =cut
230              
231             sub add_state
232             {
233 0     0 1   my $self = shift;
234 0           my $name = uc shift;
235 0           my( $value, $help ) = @_;
236              
237             $self->{$name} = [ $self->state,
238 0     0     sub{ my $self = shift; $self->value( $name, @_ ) }, $value, $help ];
  0            
  0            
239              
240 0           $self;
241             }
242              
243             =item add_toggle( DIRECTIVE, INITIAL_VALUE, HELP )
244              
245             Like add(), but without TYPE and CODEREF which is automatically filled in.
246             Use this for a value that can be either "on" or "off".
247              
248             =cut
249              
250             sub add_toggle
251             {
252 0     0 1   my $self = shift;
253 0           my $name = uc shift;
254 0           my( $value, $help ) = @_;
255              
256             my $code = sub {
257 0     0     my $self = shift;
258              
259 0 0         return $self->{$name}[2] unless @_;
260 0           my $value = lc shift;
261 0           warn "saw $name with value [$value]\n";
262              
263 0 0 0       unless( $value eq 'on' or $value eq 'off' )
264             {
265 0           warn "$name can be only 'on' or 'off', line $.\n";
266             return
267 0           }
268              
269 0           $self->{$name}[2] = $value;
270              
271 0           print "$name is [$$self{$name}[2]]\n";
272 0           };
273              
274 0           $self->{$name} = [ $self->state, $code, $value, $help ];
275              
276 0           $self;
277             }
278              
279             =item help
280              
281             Returns a help message: you want to override this.
282              
283             =cut
284              
285             sub help
286             {
287 0     0 1   my $self = shift;
288 0           my $name = uc shift;
289              
290 0           print "This is a help message for [$name]\n";
291              
292 0           $self;
293             }
294              
295             =item directives
296              
297             Returns a list of directives.
298              
299             =cut
300              
301             sub directives
302             {
303 0     0 1   my $self = shift;
304              
305 0           return sort keys %$self;
306             }
307              
308             =back
309              
310             =head1 POLYGLOT LANGUAGES
311              
312             At the moment you are stuck with the examples and examining
313             the source.
314              
315             =head2 Built in directives
316              
317             The Polyglot module provides some basic directives.
318              
319             =over 4
320              
321             =item POLYGLOT PACKAGE
322              
323             Load a Perl package.
324              
325             =item SHOW DIRECTIVE
326              
327             Displays the value of DIRECTIVE
328              
329             =item DUMP
330              
331             Displays all of the "state" DIRECTIVES with their values
332              
333             =item REFLECT
334              
335             Displays the Polyglot object
336              
337             =item HELP DIRECTIVE
338              
339             Displays the help message for DIRECTIVE
340              
341             =back
342              
343             =head1 TO DO
344              
345             * i should really make all of these methods class methods that
346             access a Singleton object stored as class data.
347              
348             =head1 SOURCE AVAILABILITY
349              
350             The source is in GitHub:
351              
352             https://github.com/briandfoy/polyglot
353              
354             =head1 AUTHOR
355              
356             brian d foy, C<< >>.
357              
358             =head1 COPYRIGHT AND LICENSE
359              
360             Copyright © 2002-2017, brian d foy . All rights reserved.
361              
362             This program is free software; you can redistribute it and/or modify
363             it under the same terms as Perl itself.
364              
365             =cut
366              
367             "ein";