use strict;
use warnings;

our %formula_per_k;
INIT {
    my $braces_re;
    $braces_re = qr{
        (?:
            (?> [^{}]+ )
        |
            \{ (??{ $braces_re }) \}
        )*
    }sx;

    # 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* \{ ( $braces_re ) \} }mgx ) {
        my ( $name, $params ) = ( $1, $2 );

        # Parse LOL block
        next if $params !~ m{ LOL \s* \{ ( $braces_re ) \} }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
