Reputation: 392
I have the following script and want turn some parts of it to Perl script. The parts that I'm interested in are very similar to perl and easy to convert (FYI: COND
and FORMULA
mean if
and return
in Perl, respectively). However, I'm struggling to extract these sections properly.
... #OTHER STUFFS
K K1 {
... #MORE OTHER STUFFS
LOL {
COND { d < 0.01 }
FORMULA { -0.2 + 3.3*sqrt(d) }
COND { d >= 0.01 }
FORMULA { -0.2 + 3.3*sqrt(d+0.4) }
}
... #MORE OTHER STUFFS
}
... #OTHER STUFFS
K K2 {
... #MORE OTHER STUFFS
LOL {
COND { d < 0.03 }
FORMULA { -2.2 + 1.3*sqrt(d) }
COND { d >= 0.03 }
FORMULA { -2.2 + 1.3*sqrt(d+0.8) }
}
... #MORE OTHER STUFFS
}
... #OTHER STUFFS
K K3 {
... #MORE OTHER STUFFS
LOL {
COND { d < 0.02 }
FORMULA { -4.3 + 0.3*sqrt(d) }
COND { d >= 0.02 }
FORMULA { -4.3 + 0.3*sqrt(d+0.3) }
}
... #MORE OTHER STUFFS
}
... #OTHER STUFF
I've tried the following perl-liner,
perl -ne 'print $1 if /K\sK2\s\{/ .. /\}/ and /LOL\s\{/ .. /\}/ and /COND*(.*)/' filename
to extract, for instance, { d < 0.03 }
from
K K2 {
... #MORE OTHER STUFFS
LOL {
COND { d < 0.03 }
FORMULA { -2.2 + 1.3*sqrt(d) }
COND { d >= 0.03 }
FORMULA { -2.2 + 1.3*sqrt(d+0.8) }
}
... #MORE OTHER STUFFS
}
But
COND
statement in the same section
(i.e. COND { w >= 0.03 }
). In other word, how I can skip the first, second,... occurrence of a string.PS If I can get this extraction part done, I know how to convert it to Perl-looking code
Upvotes: 1
Views: 200
Reputation: 35208
Parse the conditions, and translate them into anonymous subroutines that can be eval'd and then assigned to a hash.
You will want to test the below thoroughly before using, as I don't know your full data set.
use strict;
use warnings;
our %formula_per_k;
INIT {
# List all functions that you want to allow in formulas. All other words will be interpretted as variables.
my @FORMULA_FUNCS = qw(sqrt exp log);
# Load the data via a file.
my $data = do {local $/; <DATA>};
# Parse K blocks
while ($data =~ m{
^K \s+ (\w+) \s* \{
( (?: [^{}]+ | \{(?2)\} )* ) # Matched braces only.
\}
}mgx) {
my ($name, $params) = ($1, $2);
# Parse LOL block
next if $params !~ m{
LOL \s* \{
( (?: [^{}]+ | \{(?1)\} )*? ) # Matched braces only.
\}
}mx;
my $lol = $1;
# Start building anonymous subroutine
my $conditions = '';
# Parse Conditions and Formulas
while ($lol =~ m{
COND \s* \{ (.*?) \} \s*
FORMULA \s* \{ (.*?) \}
}gx) {
my ($cond, $formula) = ($1, $2);
# Remove Excess spacing and translate variable into perl scalar.
for ($cond, $formula) {
s/^\s+|\s+$//g;
s{([a-zA-Z]+)}{
my $var = $1;
$var = "\$hashref->{$var}" if ! grep {$var eq $_} @FORMULA_FUNCS;
$var
}eg;
}
$conditions .= "return $formula if $cond; ";
}
my $code = "sub {my \$hashref = shift; ${conditions} return; }";
my $sub = eval $code;
if ($@) {
die "Invalid formulas in $name: $@";
}
$formula_per_k{$name} = $sub;
}
}
sub formula_per_k {
my ($k, $vars) = @_;
die "Unrecognized K value '$k'" if ! exists $formula_per_k{$k};
return $formula_per_k{$k}($vars);
}
print "'K1', {d => .1} = " . formula_per_k('K1', {d => .1}) . "\n";
print "'K1', {d => .05} = " . formula_per_k('K1', {d => .05}) . "\n";
print "'K3', {d => .02} = " . formula_per_k('K3', {d => .02}) . "\n";
print "'K3', {d => .021} = " . formula_per_k('K3', {d => .021}) . "\n";
__DATA__
... #OTHER STUFFS
K K1 {
LOL {
COND { d < 0.01 }
FORMULA { -0.2 + 3.3*sqrt(d) }
COND { d >= 0.01 }
FORMULA { -0.2 + 3.3*sqrt(d+0.4) }
}
}
... #OTHER STUFFS
K K2 {
LOL {
COND { d < 0.03 }
FORMULA { -2.2 + 1.3*sqrt(d) }
COND { d >= 0.03 }
FORMULA { -2.2 + 1.3*sqrt(d+0.8) }
}
}
... #OTHER STUFFS
K K3 {
LOL {
COND { d < 0.02 }
FORMULA { -4.3 + 0.3*sqrt(d) }
COND { d >= 0.02 }
FORMULA { -4.3 + 0.3*sqrt(d+0.3) }
}
}
... #OTHER STUFF
Outputs:
'K1', {d => .1} = 2.13345237791561
'K1', {d => .05} = 2.01370729772479
'K3', {d => .02} = -4.13029437251523
'K3', {d => .021} = -4.13002941430942
Upvotes: 2
Reputation: 89584
First at all, sorry for the one-liner, but I use a readable way.
To extract the information you want (in general):
my $data = <<EOD;
... #OTHER STUFFS
K K1 {
LOL {
COND { d < 0.01 }
FORMULA { -0.2 + 3.3*sqrt(d) }
COND { d >= 0.01 }
FORMULA { -0.2 + 3.3*sqrt(d+0.4) }
}
}
... #OTHER STUFFS
K K2 {
LOL {
COND { d < 0.03 }
FORMULA { -2.2 + 1.3*sqrt(d) }
COND { d >= 0.03 }
FORMULA { -2.2 + 1.3*sqrt(d+0.8) }
}
}
... #OTHER STUFFS
K K3 {
LOL {
COND { d < 0.02 }
FORMULA { -4.3 + 0.3*sqrt(d) }
COND { d >= 0.02 }
FORMULA { -4.3 + 0.3*sqrt(d+0.3) }
}
}
EOD
while( $data =~ /COND \s* { \s* (?<cond> [^}]*? ) \s* } \s*
FORMULA \s* { \s* (?<formula> [^}]*? ) \s* }
/xg ) {
print "Condition: $+{cond}\nFormula: $+{formula}\n";
}
for a particular item, you can use:
if ($data =~ /K2 \s* { \s* LOL \s* { \s*
COND \s* { \s* (?<cond> [^}]*? ) \s* } \s*
FORMULA \s* { \s* (?<formula> [^}]*? ) \s* }
/x) {
print "Condition: $+{cond}\nFormula: $+{formula}\n";
}
Note: I have build the patterns to automatically trim spaces that wraps "condition" and "formula", but if you want to preserve these spaces you can change \s* (?<cond> [^}]*? ) \s*
to (?<cond> [^}]* )
(the same for "formula"). Note that this change makes your pattern more performant.
If the item you contains several "LOL" parts, you can use the \G
feature in a global research to obtain all the items:
my $data = <<EOD;
K K2 {
LOL {
COND { d < 0.02 }
FORMULA { -2.1 + 1.2*sqrt(d) }
COND { d >= 0.02 }
FORMULA { -2.1 + 1.2*sqrt(d+0.7) }
}
LOL2 {
COND { d < 0.03 }
FORMULA { -2.2 + 1.3*sqrt(d) }
COND { d >= 0.03 }
FORMULA { -2.2 + 1.3*sqrt(d+0.8) }
}
LOL3 {
COND { d < 0.04 }
FORMULA { -2.3 + 1.4*sqrt(d) }
COND { d >= 0.04 }
FORMULA { -2.3 + 1.4*sqrt(d+0.9) }
}
}
EOD
while($data =~ /(?:K2 \s* { | \G(?!\A) )\s* (?:LOL\d* \s* { \s* )?
COND \s* { \s* (?<cond> [^}]*? ) \s* } \s*
FORMULA \s* { \s* (?<formula> [^}]*? ) \s* } (?: \s* } )?
/x) {
print "Condition: $+{cond}\nFormula: $+{formula}\n";
}
Note: obviously, you must replace LOL\d*
with a subpattern that matches all possible names.
Upvotes: 1