pbot/Plugins/Plang.pm

139 lines
4.3 KiB
Perl
Raw Normal View History

2020-07-09 08:21:54 +02:00
# File: Plang.pm
# Author: pragma-
#
# Purpose: Simplified scripting language for creating advanced PBot factoids
# and interacting with various internal PBot APIs.
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
package Plugins::Plang;
use parent 'Plugins::Plugin';
use warnings; use strict;
use feature 'unicode_strings';
use Getopt::Long qw(GetOptionsFromArray);
sub initialize {
my ($self, %conf) = @_;
my $path = $self->{pbot}->{registry}->get_value('general', 'plang_dir') // 'Plang';
unshift @INC, $path if not grep { $_ eq $path } @INC;
require "$path/Interpreter.pm";
2020-07-12 06:15:54 +02:00
# regset plang.debug 0-10 -- Plugin must be reloaded for this value to take effect.
2020-07-09 08:21:54 +02:00
my $debug = $self->{pbot}->{registry}->get_value('plang', 'debug') // 0;
2020-07-12 06:15:54 +02:00
# create our Plang interpreter object
2020-07-09 08:21:54 +02:00
$self->{plang} = Plang::Interpreter->new(embedded => 1, debug => $debug);
2020-07-12 06:15:54 +02:00
# register some built-in functions
2020-07-13 02:36:32 +02:00
$self->{plang}->{interpreter}->add_function_builtin('factset',
# parameters are [['param1 name', default arg], ['param2 name', default arg], ...]
[['channel', undef], ['keyword', undef], ['text', undef]],
\&set_factoid);
$self->{plang}->{interpreter}->add_function_builtin('factget',
[['channel', undef], ['keyword', undef]],
\&get_factoid);
$self->{plang}->{interpreter}->add_function_builtin('factappend',
[['channel', undef], ['keyword', undef], ['text', undef]],
\&append_factoid);
2020-07-12 06:15:54 +02:00
# register the `plang` command
2020-07-09 08:21:54 +02:00
$self->{pbot}->{commands}->register(sub { $self->cmd_plang(@_) }, "plang", 0);
}
sub unload {
my $self = shift;
$self->{pbot}->{commands}->unregister("plang");
}
sub cmd_plang {
my ($self, $context) = @_;
2020-07-13 03:53:57 +02:00
my $usage = "Usage: plang <code>; see https://github.com/pragma-/Plang";
2020-07-09 08:21:54 +02:00
2020-07-12 06:15:54 +02:00
return $usage if not length $context->{arguments};
# run() returns result of the final statement
my $result = $self->run($context->{arguments});
# check to see if we need to append final result to output
2020-07-13 02:36:32 +02:00
$self->{output} .= $result->[1] if defined $result->[1];
2020-07-12 06:15:54 +02:00
2020-07-13 02:36:32 +02:00
# return the output
2020-07-12 06:15:54 +02:00
return length $self->{output} ? $self->{output} : "No output.";
}
# run an embedded plang program
# TODO this is just a proof-of-concept at this stage; 90% of this stuff will be moved into Plang::Interpreter
2020-07-12 06:15:54 +02:00
sub run {
my ($self, $code) = @_;
2020-07-09 08:21:54 +02:00
# reset output buffer
$self->{output} = ""; # collect output of the embedded Plang program
2020-07-12 06:15:54 +02:00
# parse the code into an ast
my $ast = $self->{plang}->parse_string($code);
# check for parse errors
my $errors = $self->{plang}->handle_parse_errors;
return ['ERROR', $errors] if defined $errors;
# return if no program
2020-07-12 06:15:54 +02:00
return if not defined $ast;
2020-07-09 08:21:54 +02:00
2020-07-12 06:15:54 +02:00
# create a new environment for a Plang program
my $context = $self->{plang}->{interpreter}->init_program;
2020-07-09 08:21:54 +02:00
2020-07-12 06:15:54 +02:00
# grab our program's statements
my $program = $ast->[0];
my $statements = $program->[1];
my $result; # result of the final statement
2020-07-13 02:36:32 +02:00
eval {
# interpret the statements
foreach my $node (@$statements) {
my $ins = $node->[0];
2020-07-12 06:15:54 +02:00
2020-07-13 02:36:32 +02:00
if ($ins eq 'STMT') {
$result = $self->{plang}->{interpreter}->statement($context, $node->[1]);
}
2020-07-12 06:15:54 +02:00
}
2020-07-13 02:36:32 +02:00
};
2020-07-12 06:15:54 +02:00
2020-07-13 02:36:32 +02:00
if ($@) {
$self->{output} .= $@;
2020-07-12 06:15:54 +02:00
return;
}
2020-07-13 02:36:32 +02:00
return $result; # return result of the final statement
2020-07-12 06:15:54 +02:00
}
# our custom PBot built-in functions for Plang
sub get_factoid {
my ($plang, $name, $arguments) = @_;
my ($channel, $keyword) = ($arguments->[0]->[1], $arguments->[1]->[1]);
return ['STRING', "get_factoid: channel: [$channel], keyword: [$keyword]"];
}
sub set_factoid {
my ($plang, $name, $arguments) = @_;
my ($channel, $keyword, $text) = ($arguments->[0]->[1], $arguments->[1]->[1], $arguments->[2]->[1]);
return ['STRING', "set_factoid: channel: [$channel], keyword: [$keyword], text: [$text]"];
}
2020-07-09 08:21:54 +02:00
2020-07-12 06:15:54 +02:00
sub append_factoid {
my ($plang, $name, $arguments) = @_;
my ($channel, $keyword, $text) = ($arguments->[0]->[1], $arguments->[1]->[1], $arguments->[2]->[1]);
return ['STRING', "append_factoid: channel: [$channel], keyword: [$keyword], text: [$text]"];
2020-07-09 08:21:54 +02:00
}
1;