File Coverage

blib/lib/Polyglot.pm
Criterion Covered Total %
statement 23 99 23.2
branch 0 24 0.0
condition 0 3 0.0
subroutine 8 24 33.3
pod 9 9 100.0
total 40 159 25.1


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