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 )