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   653 use strict;
  1         2  
  1         27  
3 1     1   4 use vars qw($VERSION);
  1         1  
  1         38  
4              
5 1     1   5 use warnings;
  1         1  
  1         22  
6 1     1   4 no warnings;
  1         2  
  1         50  
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   389 use autouse 'Data::Dumper' => 'Dumper';
  1         627  
  1         5  
59              
60 1     1   54 use Carp qw(carp);
  1         3  
  1         41  
61 1     1   413 use Text::ParseWords qw( quotewords );
  1         1080  
  1         960  
62              
63             our $VERSION = '1.003';
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 0     0 1   my( $class, @args ) = @_;
79              
80 0           my $self = bless {}, $class;
81              
82             $self->add_action( 'POLYGLOT',
83             sub {
84 0     0     my( $self, $package ) = @_;
85 0           eval{ eval "require $package" };
  0            
86 0           } );
87 0     0     $self->add_action( 'HELP', sub { my $self = shift; $self->help( @_ ) } );
  0            
  0            
88 0     0     $self->add_action( 'EXIT', sub { exit } );
  0            
89 0     0     $self->add_action( 'REFLECT', sub { print Dumper( $_[0] ) } );
  0            
90             $self->add_action( 'SHOW', sub {
91 0     0     my( $self, $name ) = ( shift, uc shift );
92 0           print "$name = ", $self->value($name), "\n";
93 0           $self; } );
  0            
94             }
95              
96             =item run
97              
98             Start the interpreter. It will read lines from the file names in
99             @ARGV or from standard input using the diamond operator. It splits
100             lines on whitespace and assumes the first element of that list is
101             the directive name. If the directive does not exist, it prints a
102             warning and continues.
103              
104             This method uses the diamond operator and assumes that nothing else
105             has mucked with it.
106              
107             =cut
108              
109             sub run {
110 0     0 1   my $self = shift;
111              
112 0           my $prompt = "$0> ";
113              
114 0 0         print "H: Waiting for commands on standard input\n$prompt"
115             unless @ARGV;
116              
117 0           while( <> ) {
118 0           print "$ARGV\[$.]: $_";
119 0           chomp;
120 0 0         next if /^\s*#?$/;
121 0           my( $directive, $string ) = split /\s+/, $_, 2;
122              
123 0           $directive = uc $directive;
124 0 0         carp "DEBUG: directive is $directive\n" if $Debug;
125              
126 0           my @arguments = quotewords( '\s+', 0, $string );
127 0 0         carp "DEBUG: arguments are @arguments\n" if $Debug;
128              
129 0           eval {
130 0 0         die "Undefined subroutine" unless exists $self->{$directive};
131 0           $self->{$directive}[1]( $self, @arguments );
132             };
133              
134 0 0         warn "Not a valid directive: [$directive] at $ARGV line $.\n"
135             if $@ =~ m/Undefined subroutine/;
136              
137 0 0         print "$prompt" if $ARGV eq '-';
138             }
139              
140 0           print "\n";
141             }
142              
143             =item state
144              
145             Returns the string used to mark a directive that affects the program
146             state.
147              
148             =cut
149              
150             sub state () { 'state' }
151              
152             =item action
153              
154             Returns the string used to mark a directive that performs an action.
155              
156             =cut
157              
158             sub action () { 'action' }
159              
160              
161             =item add( DIRECTIVE, TYPE, CODEREF, INITIAL_VALUE, HELP )
162              
163             Adds DIRECTIVE to the little language with TYPE (state or action).
164             The value of the directive (for those that represent program state) is
165             INITIAL_VALUE or undef. The CODEREF is executed when the interpreter
166             encounters the directive. The built-in HELP directive returns the
167             HELP string for this DIRECTIVE.
168              
169             =cut
170              
171             sub add {
172 0     0 1   my( $self, $name, $state, $sub, $value, $help ) = @_;
173              
174 0           $self->{$name} = [ $state, $sub, $help ];
175              
176 0           $self;
177             }
178              
179             =item value( DIRECTIVE [, VALUE ] )
180              
181             Returns the value for DIRECTIVE, or sets it if you specify VALUE.
182              
183             =cut
184              
185             sub value {
186 0     0 1   my( $self, $name, $value ) = @_;
187 0 0         carp "Setting $name with $value\n" if $Debug;
188              
189 0 0         return unless exists $self->{ $name };
190              
191 0 0         return $self->{$name}[2] unless defined $value;
192              
193 0           $self->{$name}[2] = $value;
194              
195             }
196              
197             =item add_action( DIRECTIVE, CODEREF, INITIAL_VALUE, HELP )
198              
199             Like add(), but without TYPE which is automatically filled in. Use
200             this for a directive that does something other than setting a value.
201              
202             The CODEREF can be anything. The first argument is always the
203             interpreter object, and the rest of the arguments are from the current
204             line.
205              
206             =cut
207              
208             sub add_action {
209 0     0 1   my $self = shift;
210 0           my $name = uc shift;
211 0           my( $sub, $value, $help ) = @_;
212              
213 0           $self->{$name} = [ $self->action, $sub, $value, $help ];
214              
215 0           $self;
216             }
217              
218             =item add_state( DIRECTIVE, INITIAL_VALUE, HELP )
219              
220             Like add(), but without TYPE and CODEREF which is automatically filled in.
221             Use this for a directive that can set a value.
222              
223             =cut
224              
225             sub add_state {
226 0     0 1   my $self = shift;
227 0           my $name = uc shift;
228 0           my( $value, $help ) = @_;
229              
230             $self->{$name} = [ $self->state,
231 0     0     sub{ my $self = shift; $self->value( $name, @_ ) }, $value, $help ];
  0            
  0            
232              
233 0           $self;
234             }
235              
236             =item add_toggle( DIRECTIVE, INITIAL_VALUE, HELP )
237              
238             Like add(), but without TYPE and CODEREF which is automatically filled in.
239             Use this for a value that can be either "on" or "off".
240              
241             =cut
242              
243             sub add_toggle {
244 0     0 1   my $self = shift;
245 0           my $name = uc shift;
246 0           my( $value, $help ) = @_;
247              
248             my $code = sub {
249 0     0     my $self = shift;
250              
251 0 0         return $self->{$name}[2] unless @_;
252 0           my $value = lc shift;
253 0           warn "saw $name with value [$value]\n";
254              
255 0 0 0       unless( $value eq 'on' or $value eq 'off' ) {
256 0           warn "$name can be only 'on' or 'off', line $.\n";
257             return
258 0           }
259              
260 0           $self->{$name}[2] = $value;
261              
262 0           print "$name is [$$self{$name}[2]]\n";
263 0           };
264              
265 0           $self->{$name} = [ $self->state, $code, $value, $help ];
266              
267 0           $self;
268             }
269              
270             =item help
271              
272             Returns a help message: you want to override this.
273              
274             =cut
275              
276             sub help {
277 0     0 1   my $self = shift;
278 0           my $name = uc shift;
279              
280 0           print "This is a help message for [$name]\n";
281              
282 0           $self;
283             }
284              
285             =item directives
286              
287             Returns a list of directives.
288              
289             =cut
290              
291             sub directives {
292 0     0 1   my $self = shift;
293              
294 0           return sort keys %$self;
295             }
296              
297             =back
298              
299             =head1 POLYGLOT LANGUAGES
300              
301             At the moment you are stuck with the examples and examining
302             the source.
303              
304             =head2 Built in directives
305              
306             The Polyglot module provides some basic directives.
307              
308             =over 4
309              
310             =item POLYGLOT PACKAGE
311              
312             Load a Perl package.
313              
314             =item SHOW DIRECTIVE
315              
316             Displays the value of DIRECTIVE
317              
318             =item DUMP
319              
320             Displays all of the "state" DIRECTIVES with their values
321              
322             =item REFLECT
323              
324             Displays the Polyglot object
325              
326             =item HELP DIRECTIVE
327              
328             Displays the help message for DIRECTIVE
329              
330             =back
331              
332             =head1 TO DO
333              
334             * i should really make all of these methods class methods that
335             access a Singleton object stored as class data.
336              
337             =head1 SOURCE AVAILABILITY
338              
339             The source is in GitHub:
340              
341             https://github.com/briandfoy/polyglot
342              
343             =head1 AUTHOR
344              
345             brian d foy, C<< >>.
346              
347             =head1 COPYRIGHT AND LICENSE
348              
349             Copyright © 2002-2018, brian d foy . All rights reserved.
350              
351             This program is free software; you can redistribute it and/or modify
352             it under the same terms as Perl itself.
353              
354             =cut
355              
356             "ein";