BarefootJS

 view release on metacpan or  search on metacpan

lib/BarefootJS/Evaluator.pm  view on Meta::CPAN

package BarefootJS::Evaluator;
our $VERSION = "0.14.0";
use strict;
use warnings;
use utf8;
use feature 'signatures';
no warnings 'experimental::signatures';

use B ();
use POSIX ();
use JSON::PP ();
use Scalar::Util qw(looks_like_number);

# Lightweight evaluator for the pure `ParsedExpr` subset, scoped to
# higher-order callback bodies (reduce / sort / map / filter / find
# `(…) => expr`) — issue #2018. Templates cannot carry a lambda in
# expression position, which is why the adapters historically special-cased
# these callbacks into fixed shapes (bf_sort's comparator catalogue,
# bf_reduce's +/* fold). Instead, the callback BODY rides as a pure
# `ParsedExpr` subtree (the structured IR the compiler already produces) and
# is evaluated here against an environment (`{acc, item, …captured free
# vars}`).
#
# ONE shared implementation for both Perl backends (Mojo + Xslate), living
# alongside SearchParams.pm in the engine-agnostic core (no CPAN deps beyond
# core B / POSIX / Scalar::Util). The accepted subset and its semantics are
# documented in spec/compiler.md ("ParsedExpr Evaluator Semantics") and pinned
# isomorphically by the cross-language golden vectors
# (packages/adapter-tests/helper-vectors/eval-vectors.json), shared with the Go
# evaluator (bf.go) — same input → same output.
#
# The coercion below is JS-faithful (ToNumber / ToString / ToBoolean, strict
# equality) and deliberately distinct from the divergent bf->string / number
# helpers in BarefootJS.pm, so the contract is unambiguous and the two
# template adapters stay byte-equal with each other and with Go.

# evaluate($node, $env)
#
# Evaluate a decoded ParsedExpr node (a hashref keyed by `kind`) against the
# environment hashref ($env), returning a Perl value (number, string,
# JSON::PP::Boolean, undef for null, arrayref, hashref). The matching JSON
# entry point is eval_json() below.
sub evaluate ($node, $env) {
    return undef unless ref $node eq 'HASH';
    my $kind = $node->{kind} // '';

    if ($kind eq 'literal') {
        return $node->{value};
    }
    if ($kind eq 'identifier') {
        return $env->{ $node->{name} };
    }
    if ($kind eq 'binary') {
        return _binary($node->{op},
            evaluate($node->{left}, $env), evaluate($node->{right}, $env));
    }
    if ($kind eq 'unary') {
        return _unary($node->{op}, evaluate($node->{argument}, $env));
    }
    if ($kind eq 'logical') {
        my $op   = $node->{op};
        my $left = evaluate($node->{left}, $env);
        if ($op eq '&&') {
            return _truthy($left) ? evaluate($node->{right}, $env) : $left;
        }
        if ($op eq '||') {
            return _truthy($left) ? $left : evaluate($node->{right}, $env);
        }
        # `??`
        return defined $left ? $left : evaluate($node->{right}, $env);
    }
    if ($kind eq 'conditional') {
        return _truthy(evaluate($node->{test}, $env))
            ? evaluate($node->{consequent}, $env)
            : evaluate($node->{alternate}, $env);
    }
    if ($kind eq 'member') {
        return _read_property(evaluate($node->{object}, $env), $node->{property});



( run in 0.587 second using v1.01-cache-2.11-cpan-600a1bdf6e4 )