App-Greple-jq

 view release on metacpan or  search on metacpan

lib/App/Greple/jq.pm  view on Meta::CPAN

use Carp;

our $VERSION = "0.06";

use Exporter 'import';
our @EXPORT = qw(&jq_filter);

use App::Greple::Common;
use App::Greple::Regions qw(match_regions merge_regions);
use Data::Dumper;

my %config;
sub set   { %config = @_ }
sub debug { set debug => 1 }
sub noif  { set noif  => 1 }

my $indent = '  ';
my $indent_re = qr/$indent/;

sub finalize {
    my($mod, $argv) = @_;
    if ($config{noif}) {
	my @default = $mod->default;
	$mod->setopt(default => grep { $_ ne '--jq-filter' } @default);
    }
}

sub re {
    my $pattern = shift;
    my $re = eval { qr/$pattern/ };
    if ($@) {
	die sprintf("$pattern: pattern error - %s\n",
		    $@ =~ /(.*?(?=;|$))/);
    }
    return $re;
}

sub prefix_regex {
    my $path = shift;
    my @prefix_re;
    my $level = '';
    while ($path =~ s/^([^.\n]*?)(\.+)//) {
	my($label, $dot) = ($1, $2);
	$label =~ s/%/.*/g;
	my $label_re = re($label);
	my $start_with = '';
	my $prefix_re = do {
	    if ($label eq '') {
		length($dot) > 1 ? '' : qr{ ^ (?= $indent_re \S) }xm;
	    } else {
		if (length($dot) == 1) {
		    ## using same capture group name is not a good idea
		    ## so make sure to put just for the last one
		    $level      = '?<level>' if $path eq '';
		    $start_with = qr/(?=\S)/;
		}
		qr{
		    ^ (${level} $indent_re*) "$label_re": .* \n
		    (?:
			## single line key-value pair
			\g{-1} $indent_re $start_with .++ \n
		    |
			## indented array/hash
			\g{-1} $indent_re \S .* [\[\{] \n
			(?: \g{-1} $indent_re \s .*+ \n) *+
			\g{-1} $indent_re [\]\}] ,? \n
		    ) *?
		}xm;
	    }
	};
	push @prefix_re, $prefix_re if $prefix_re;
    }
    if ($level eq '') {
	## refering named capture group causes error if it is not used
	## so put dummy expression just to fail
	push @prefix_re, qr/(?<level>(?!))?/;
    }
    @prefix_re
}

sub IN {
    my %opt = @_;
    my $target = delete $opt{&FILELABEL} or die;
    my($label, $pattern) = @opt{qw(label pattern)};
    my @prefix_re = $label =~ s/^((?:.*\.)?)// && prefix_regex($1);
    $label =~ s/%/.*/g;
    my($label_re, $pattern_re) = map re($_), $label, $pattern;
    my $re = qr{
	@prefix_re \K
	^
	(?(<level>) (?= \g{level} $indent_re \S ) )	# required level
	(?<in> [ ]*) "$label_re": [ ]*+			# find given label
	(?: . | \n\g{in} \s++ ) *			# and look for ...
	$pattern_re					# pattern
	(?: . | \n\g{in} (?: \s++ | [\]\}] ) ) *	# and take the rest
    }xm;
    warn "$re\n" if $config{debug};
    match_regions pattern => $re;
}

1;

__DATA__

define JSON-OBJECTS ^([ ]*)\{(?s:.*?)^\g{-1}\},?\n

option default --json-block --jq-filter

option --jq-filter --if='jq "if type == \"array\" then .[] else . end"'

option --json-block --block JSON-OBJECTS

define CALL_IN __PACKAGE__::IN(label=$<shift>,pattern=$<shift>)

option --AND  --IN
option --IN   --le  &CALL_IN --face +E
option --MUST --le +&CALL_IN --face +E
option --NOT  --le -&CALL_IN

#  LocalWords:  JSON jq json Zaphod greple CPANMINUS cpanm
#  LocalWords:  perl pid regex LESSANSIENDCHARS greplerc Kazumasa Mjq
#  LocalWords:  Utashiro Android committer mina ppid blockend



( run in 2.740 seconds using v1.01-cache-2.11-cpan-5c0b1e786e0 )