Permalink
Browse files

add merge_hash opcode

  • Loading branch information...
1 parent 58815c0 commit 56b1c719fac5ab4f9069e1a847aa8bdbe9a37310 @doy doy committed Oct 18, 2012
View
@@ -230,9 +230,7 @@ sub new {
sub _merge_hash {
my($self, $base, $add) = @_;
- foreach my $name(keys %{$add}) {
- $base->{$name} = $add->{$name};
- }
+ %$base = %{ Text::Xslate::Util::merge_hash($base, $add || {}) };
return;
}
View
@@ -348,6 +348,8 @@ sub _assemble {
sub is_array_ref { ref($_[0]) eq 'ARRAY' }
sub is_hash_ref { ref($_[0]) eq 'HASH' }
sub is_code_ref { ref($_[0]) eq 'CODE' }
+
+ sub merge_hash { +{ %{ $_[0] }, %{ $_[1] } } }
}
#
@@ -542,6 +542,11 @@ sub op_make_hash {
goto $_[0]->{ code }->[ ++$_[0]->{ pc } ]->{ exec_code };
}
+sub op_merge_hash {
+ $_[0]->{sa} = Text::Xslate::Util::merge_hash($_[0]->{sa}, $_[0]->{sb});
+ goto $_[0]->{ code }->[ ++$_[0]->{ pc } ]->{ exec_code };
+}
+
sub op_enter {
push @{$_[0]->{save_local_stack} ||= []}, delete $_[0]->{local_stack};
View
@@ -55,6 +55,7 @@ sub unmark_raw; # XS
sub html_escape; # XS
sub uri_escape; # XS
sub escaped_string; *escaped_string = \&mark_raw;
+sub merge_hash; # XS
sub html_builder (&){
my($code_ref) = @_;
View
@@ -137,6 +137,47 @@ tx_sv_is_code_ref(pTHX_ SV* const sv) {
return SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVCV && !SvOBJECT(SvRV(sv));
}
+SV*
+tx_merge_hash(pTHX_ tx_state_t* const st, SV* base, SV* value) {
+ SV* const retval = newSV(0);
+ HV* const hv = (HV*)SvRV(base);
+ HV* const result = newHVhv(hv);
+ SV* const resultref = newRV_noinc((SV*)result);
+ HE* he;
+ HV* m;
+
+ if(!tx_sv_is_hash_ref(aTHX_ value)) {
+ if (st) {
+ tx_error(aTHX_ st, "Merging value is not a HASH reference");
+ }
+ else {
+ die("Merging value is not a HASH reference");
+ }
+ SvREFCNT_dec(resultref);
+ return retval;
+ }
+
+ m = (HV*)SvRV(value);
+
+ ENTER;
+ SAVETMPS;
+ sv_2mortal(resultref);
+
+ hv_iterinit(m);
+ while((he = hv_iternext(m))) {
+ (void)hv_store_ent(result,
+ hv_iterkeysv(he),
+ newSVsv(hv_iterval(hv, he)),
+ 0U);
+ }
+
+ sv_setsv(retval, resultref);
+ FREETMPS;
+ LEAVE;
+
+ return retval;
+}
+
STATIC_INLINE bool
tx_str_is_raw(pTHX_ pMY_CXT_ SV* const sv); /* doesn't handle magics */
@@ -1766,6 +1807,13 @@ CODE:
ST(0) = boolSV( tx_sv_is_code_ref(aTHX_ sv));
}
+void
+merge_hash(SV* base, SV* value)
+CODE:
+{
+ ST(0) = sv_2mortal(tx_merge_hash(aTHX_ NULL, base, value));
+}
+
MODULE = Text::Xslate PACKAGE = Text::Xslate::Type::Raw
BOOT:
View
@@ -373,37 +373,7 @@ TXBM(hash, kv) {
}
TXBM(hash, merge) {
- HV* const hv = (HV*)SvRV(*MARK);
- SV* const value = *(++MARK);
- HV* const result = newHVhv(hv);
- SV* const resultref = newRV_noinc((SV*)result);
- HE* he;
- HV* m;
-
- if(!tx_sv_is_hash_ref(aTHX_ value)) {
- tx_error(aTHX_ st, "Merging value is not a HASH reference");
- sv_setsv(retval, &PL_sv_undef);
- SvREFCNT_dec(resultref);
- return;
- }
-
- m = (HV*)SvRV(value);
-
- ENTER;
- SAVETMPS;
- sv_2mortal(resultref);
-
- hv_iterinit(m);
- while((he = hv_iternext(m))) {
- (void)hv_store_ent(result,
- hv_iterkeysv(he),
- newSVsv(hv_iterval(hv, he)),
- 0U);
- }
-
- sv_setsv(retval, resultref);
- FREETMPS;
- LEAVE;
+ sv_setsv(retval, tx_merge_hash(st, *MARK, *(MARK + 1)));
}
static const tx_builtin_method_t tx_builtin_method[] = {
View
@@ -478,6 +478,15 @@ TXC(is_code_ref) {
TX_RETURN_NEXT();
}
+TXC(merge_hash) {
+ SV* const base = TX_st_sa;
+ SV* const value = TX_st_sb;
+ SvGETMAGIC(base);
+ SvGETMAGIC(value);
+ TX_st_sa = tx_merge_hash(TX_st, base, value);
+ TX_RETURN_NEXT();
+}
+
TXC(match) {
TX_st_sa = boolSV( tx_sv_match(aTHX_ TX_st_sb, TX_st_sa) );
TX_RETURN_NEXT();
@@ -0,0 +1,105 @@
+#!perl
+use strict;
+use warnings;
+use Test::More;
+
+use Text::Xslate;
+
+BEGIN {
+ package Text::Xslate::Syntax::Custom;
+ use Any::Moose;
+ extends 'Text::Xslate::Parser';
+
+ sub init_symbols {
+ my $self = shift;
+
+ $self->SUPER::init_symbols(@_);
+
+ $self->symbol('merge_hash')->set_nud($self->can('nud_merge_hash'));
+ }
+
+ sub nud_merge_hash {
+ my $self = shift;
+ my ($symbol) = @_;
+
+ $self->advance('(');
+ my $base = $self->expression(0);
+ $self->advance(',');
+ my $value = $self->expression(0);
+ $self->advance(')');
+
+ return $symbol->clone(
+ arity => 'merge_hash',
+ first => $base,
+ second => $value,
+ );
+ }
+
+ package Text::Xslate::Compiler::Custom;
+ use Any::Moose;
+ extends 'Text::Xslate::Compiler';
+
+ sub _generate_merge_hash {
+ my $self = shift;
+ my ($node) = @_;
+
+ my $lvar_id = $self->lvar_id;
+ local $self->{lvar_id} = $self->lvar_use(1);
+
+ return (
+ $self->compile_ast($node->first),
+ $self->opcode('save_to_lvar', $lvar_id),
+ $self->compile_ast($node->second),
+ $self->opcode('move_to_sb'),
+ $self->opcode('load_lvar', $lvar_id),
+ $self->opcode('merge_hash'),
+ );
+ }
+
+ package Text::Xslate::Custom;
+ use base 'Text::Xslate';
+
+ sub options {
+ my $class = shift;
+
+ my $options = $class->SUPER::options(@_);
+
+ $options->{compiler} = 'Text::Xslate::Compiler::Custom';
+ $options->{syntax} = 'Custom';
+
+ return $options;
+ }
+}
+
+my $tx = Text::Xslate::Custom->new;
+
+my @tests = (
+ [ ': [ merge_hash($a, $b).foo, $a.foo, $b.foo ].map(-> $elem { defined($elem) ? $elem : "undef" }).join(" ")', 'FOO FOO undef' ],
+ [ ': [ merge_hash($b, $a).foo, $a.foo, $b.foo ].map(-> $elem { defined($elem) ? $elem : "undef" }).join(" ")', 'FOO FOO undef' ],
+ [ ': [ merge_hash($a, $b).bar, $a.bar, $b.bar ].map(-> $elem { defined($elem) ? $elem : "undef" }).join(" ")', 'RAB BAR RAB' ],
+ [ ': [ merge_hash($b, $a).bar, $a.bar, $b.bar ].map(-> $elem { defined($elem) ? $elem : "undef" }).join(" ")', 'BAR BAR RAB' ],
+ [ ': [ merge_hash($a, $b).baz, $a.baz, $b.baz ].map(-> $elem { defined($elem) ? $elem : "undef" }).join(" ")', 'ZAB undef ZAB' ],
+ [ ': [ merge_hash($b, $a).baz, $a.baz, $b.baz ].map(-> $elem { defined($elem) ? $elem : "undef" }).join(" ")', 'ZAB undef ZAB' ],
+);
+
+for my $test (@tests) {
+ is(
+ $tx->render_string(
+ $test->[0],
+ {
+ a => {
+ foo => 'FOO',
+ bar => 'BAR',
+ },
+ b => {
+ bar => 'RAB',
+ baz => 'ZAB',
+ },
+ }
+ ),
+ $test->[1],
+ $test->[0]
+ );
+}
+
+done_testing;
View
@@ -210,5 +210,8 @@ tx_sv_is_code_ref(pTHX_ SV* const sv);
SV*
tx_methodcall(pTHX_ tx_state_t* const st, SV* const method);
+SV*
+tx_merge_hash(pTHX_ tx_state_t* const st, SV* base, SV* value);
+
void
tx_register_builtin_methods(pTHX_ HV* const hv);

0 comments on commit 56b1c71

Please sign in to comment.