3
0
mirror of https://github.com/pragma-/pbot.git synced 2024-12-25 04:02:37 +01:00

Factoids: refactor and improve $variable parsing and replacement

This commit is contained in:
Pragmatic Software 2020-06-11 22:51:40 -07:00
parent 9ec2622183
commit 78de682320

View File

@ -725,50 +725,76 @@ sub expand_factoid_vars {
if ($action =~ m/^\/call --keyword-override=([^ ]+)/i) { $root_keyword = $1; } if ($action =~ m/^\/call --keyword-override=([^ ]+)/i) { $root_keyword = $1; }
while (1) { my $result = '';
last if ++$depth >= 1000; my $rest = $action;
while (1) {
last if ++$depth >= 100;
$rest =~ s/(?<!\\)\$0/$root_keyword/g;
my $offset = 0;
my $matches = 0; my $matches = 0;
my $expansions = 0; my $expansions = 0;
$action =~ s/(?<!\\)\$0/$root_keyword/g;
my $const_action = $action;
while ($const_action =~ /(\ba\s*|\ban\s*)?(?<!\\)\$(?:(\{[a-zA-Z0-9_#]+(?::[a-zA-Z0-9_(),.#:'"+-]+)?\}|[a-zA-Z0-9_#]+(?::[a-zA-Z0-9_(),.#:'"+-]+)?))/gi) {
my ($indefinite_article, $v) = ($1, $2);
$indefinite_article = '' if not defined $indefinite_article;
next if not defined $v;
my $original_v = $v; while ($rest =~ s/(.*?)(?<!\\)\$([\w|{])/$2/) {
my $test_v = $v; $result .= $1;
$test_v =~ s/(.):$/$1/; # remove trailing : only if at least one character precedes it
next if $test_v =~ m/^_/; # special character prefix skipped for shell/code-factoids/etc my $var;
next if $test_v =~ m/^(?:nick|channel|randomnick|arglen|args|arg\[.+\]|[_0])(?:\:json)*$/i; # don't override special variables my $extract_method;
last if ++$depth >= 1000;
if ($action =~ /^\{.*?\}/) {
($var, $rest) = $self->{pbot}->{interpreter}->extract_bracketed($rest, '{', '}');
if ($var =~ /:/) {
my @stuff = split /:/, $var, 2;
$var = $stuff[0];
$rest = ':' . $stuff[1] . $rest;
}
$extract_method = 'bracket';
} else {
$rest =~ s/^(\w+)//;
$var = $1;
$extract_method = 'regex';
}
if ($var =~ /^(?:_.*|nick|channel|randomnick|arglen|args|arg\[.+\])$/i) {
# skip identifiers with leading underscore and special variables
$result .= $extract_method eq 'bracket' ? '${' . $var . '}' : '$' . $var;
next;
}
$matches++; $matches++;
$test_v =~ s/\{(.+)\}/$1/; # extract channel expansion modifier
if ($rest =~ s/^\s*:\s*(#[^:]+|global)//i) {
my $modifier = '';
if ($test_v =~ s/(:.*)$//) { $modifier = $1; }
if ($modifier =~ m/^:(#[^:]+|global)/i) {
$from = $1; $from = $1;
$from = '.*' if lc $from eq 'global'; $from = '.*' if lc $from eq 'global';
} }
my $recurse = 0; my $recurse = 0;
ALIAS: ALIAS:
my @factoids = $self->find_factoid($from, $test_v, exact_channel => 2, exact_trigger => 2); my @factoids = $self->find_factoid($from, $var, exact_channel => 2, exact_trigger => 2);
next if not @factoids or not $factoids[0];
my ($var_chan, $var) = ($factoids[0]->[0], $factoids[0]->[1]); if (not @factoids or not $factoids[0]) {
$result .= $extract_method eq 'bracket' ? '${' . $var . '}' : '$' . $var;
next;
}
my $var_chan;
($var_chan, $var) = ($factoids[0]->[0], $factoids[0]->[1]);
if ($self->{factoids}->get_data($var_chan, $var, 'action') =~ m{^/call (.*)}ms) { if ($self->{factoids}->get_data($var_chan, $var, 'action') =~ m{^/call (.*)}ms) {
$test_v = $1; $var = $1;
next if ++$recurse > 100;
if (++$recurse > 100) {
$self->{pbot}->{logger}->log("Factoids: variable expansion recursion limit reached\n");
$result .= $extract_method eq 'bracket' ? '${' . $var . '}' : '$' . $var;
next;
}
goto ALIAS; goto ALIAS;
} }
@ -779,47 +805,12 @@ sub expand_factoid_vars {
my @replacements; my @replacements;
if (wantarray) { if (wantarray) {
@replacements = $self->select_item($context, join ('|', @list), \$modifier, %opts); @replacements = $self->select_item($context, join ('|', @list), \$rest, %opts);
} else { } else {
push @replacements, scalar $self->select_item($context, join ('|', @list), \$modifier, %opts); push @replacements, scalar $self->select_item($context, join ('|', @list), \$rest, %opts);
} }
foreach my $replacement (@replacements) { foreach my $replacement (@replacements) {
foreach my $mod (split /:/, $modifier) {
$mod =~ s/,$//;
next if not length $mod;
if ($replacement =~ /^\$\{\$([a-zA-Z0-9_:#]+)\}(.*)$/) {
$replacement = "\${\$$1:$mod}$2";
next;
} elsif ($replacement =~ /^\$\{([a-zA-Z0-9_:#]+)\}(.*)$/) {
$replacement = "\${$1:$mod}$2";
next;
} elsif ($replacement =~ /^\$\$([a-zA-Z0-9_:#]+)(.*)$/) {
$replacement = "\${\$$1:$mod}$2";
next;
} elsif ($replacement =~ /^\$([a-zA-Z0-9_:#]+)(.*)$/) {
$replacement = "\${$1:$mod}$2";
next;
}
given ($mod) {
when ('uc') { $replacement = uc $replacement; }
when ('lc') { $replacement = lc $replacement; }
when ('ucfirst') { $replacement = ucfirst $replacement; }
when ('title') {
$replacement = ucfirst lc $replacement;
$replacement =~ s/ (\w)/' ' . uc $1/ge;
}
when ('json') { $replacement = $self->escape_json($replacement); }
}
}
if ($indefinite_article) {
my $fixed_article = select_indefinite_article $replacement;
$fixed_article = ucfirst $fixed_article if $indefinite_article =~ m/^A/;
$replacement = "$fixed_article $replacement";
}
} }
if (wantarray) { if (wantarray) {
@ -828,29 +819,32 @@ sub expand_factoid_vars {
my $replacement = $opts{nested} ? join('|', @replacements) : "@replacements"; my $replacement = $opts{nested} ? join('|', @replacements) : "@replacements";
$original_v = quotemeta $original_v;
$original_v =~ s/\\:/:/g;
if (not length $replacement) { if (not length $replacement) {
substr($action, $offset) =~ s/$indefinite_article\$$original_v ?/$replacement/; $result =~ s/\s+$//;
$offset += $-[0] + length $replacement;
} else {
substr($action, $offset) =~ s/$indefinite_article\$$original_v/$replacement/;
$offset += $-[0] + length $replacement;
} }
$result .= $replacement;
$expansions++; $expansions++;
} else {
$result .= $extract_method eq 'bracket' ? '${' . $var . '}' : '$' . $var;
} }
} }
last if $matches == 0 or $expansions == 0; last if $matches == 0 or $expansions == 0;
if (not length $rest) {
$rest = $result;
$result = '';
}
} }
$action = $self->expand_special_vars($from, $nick, $root_keyword, $action); $result .= $rest;
$action =~ s/\\\$/\$/g; $result = $self->expand_special_vars($from, $nick, $root_keyword, $result);
return validate_string($action, $self->{pbot}->{registry}->get_value('factoids', 'max_content_length')); $result =~ s/\\\$/\$/g;
return validate_string($result, $self->{pbot}->{registry}->get_value('factoids', 'max_content_length'));
} }
sub expand_action_arguments { sub expand_action_arguments {