From 5331e8ef6cdb9163e3ec6ee85491b7b286f4cbdc Mon Sep 17 00:00:00 2001 From: David Phillips Date: Sun, 16 Sep 2018 23:42:35 +1200 Subject: Overhaul config parsing * makes plugin config more private: The config file now uses sections denoted with [Plugin::Foo] where plugin- private config can be stored. Plugins are now passed the usual, as well as a hashref for their own config section. They are also passed the config section of the core, i.e. those config options not appearing in an explicit section. Generally, these are used for bot-global options, so should be accessible to all plugins, but plugin-specific config shall be hidden * tries to improve parsing of hash-like strings and arrays The previous mechanism of using regex to pull out possible tokens was only ever meant to be temporary, and caused problems with escaping or encapsulation inside strings. I have made steps on hash parsing to allow tokens inside strings. Both array and hash parsing still to provide an escape character to escape the item separator (,) --- ListParser.pm | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 ListParser.pm (limited to 'ListParser.pm') diff --git a/ListParser.pm b/ListParser.pm new file mode 100644 index 0000000..9e3e511 --- /dev/null +++ b/ListParser.pm @@ -0,0 +1,107 @@ +package ListParser; + +sub parse_mapping { + my ($input) = @_; + my $key, $value; + my $i = 0; + my $string_start, $string_end; + $string_start = $string_end = undef; + + # Are we currently lexing inside a string literal? + my $is_string = 0; + + # Currently parsing key or value? + my $is_key = 1; + + while ($i < length($input)) { + my $c = substr($input, $i, 1); + my $lookahead = substr($input, $i+1, 1); + + if ($is_string and $c eq "'") { + $is_string = 0; + $string_end = $i; + if (not $is_key) { + $value = substr($input, $string_start, $string_end - $string_start); + } + $i++; + next; + } + if (not $is_string) { + if ($c =~ /\s/) { + # allow whitespace + } elsif ($c eq "'") { + return ("Key/value must consist of single string", undef, undef) if defined $string_end; + $is_string = 1; + $string_start = $i + 1; + } elsif ($c eq "=") { + return ("Expected > after =, got $lookahead", undef, undef) unless $lookahead eq ">"; + return ("Unexpected '=>'.", undef, undef) unless $is_key; + $i++; + + $key = substr($input, $string_start, $string_end - $string_start); + $string_start = $string_end = undef; + $is_key = 0; + } else { + return ("Unexpected $c", undef, undef); + } + } + $i++; + } + + return (undef, $key, $value); +} + +sub parse_list { + my ($input, $is_hash) = @_; + my $c_start = $is_hash ? "{" : "["; + my $c_end = $is_hash ? "}" : "]"; + my %h_res; + my @a_res; + my $i = 0; + + # Index of the start of the current item + my $item_i = 0; + + # Level of nested lists, 1 being the minimum + my $nest = 1; + + return ("Error: expected $c_start", undef) unless substr($input, $i, 1) eq $c_start; + + $i++; + $item_i = $i; + + while ($nest != 0 && $i < length($input)) { + my $c = substr($input, $i, 1); + + if ($c eq $c_start) { + $nest++; + } elsif ($c eq $c_end) { + $nest--; + } + + if (($nest == 1 and $c eq ",") || ($nest == 0 and $c eq $c_end)) { + my $item = substr($input, $item_i, $i - $item_i); + $item =~ s/^\s+|\s+$//g; + if ($is_hash) { + # FIXME should we die on duplicate keys or no? + my ($error, $key, $value) = parse_mapping($item); + die $error if $error; + $h_res{$key} = $value; + } else { + push @a_res, $item; + } + $item_i = $i+1; + } + $i++; + } + + return ("Error: expected $c_end, got end of line", undef) unless $nest == 0; + + if ($i != length($input)) { + return ("Error: unexpected item in the bagging area (after '$c_end')", undef); + } + + return (undef, %h_res) if $is_hash; + return (undef, @a_res); +} +1; -- cgit v1.1