⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 expr.pm

📁 功能与特点: 1.可显示自己在巴巴变上的专辑列表 2.可显示自己在巴巴变上的公开的图片 3.可显示巴巴变上用户的评论 4.可显示自己在巴巴变上的关键字 5.可显示巴巴变上的精彩图片 6.支
💻 PM
📖 第 1 页 / 共 2 页
字号:
package HTML::Template::Expr;

use strict;
use vars qw($VERSION);

$VERSION = '0.07';

use HTML::Template 2.4;
use Carp qw(croak confess carp);
use Parse::RecDescent;

use base 'HTML::Template';

use vars qw($GRAMMAR);
$GRAMMAR = <<'END';
expression : paren /^$/  { $return = $item[1] } 

paren         : '(' binary_op ')'     { $item[2] }
              | '(' subexpression ')' { $item[2] }
              | subexpression         { $item[1] }
              | '(' paren ')'         { $item[2] }

subexpression : function_call
              | var
              | literal
              | <error>

binary_op     : paren (op paren { [ $item[2], $item[1] ] })(s)
              { $return = [ 'SUB_EXPR', $item[1], map { @$_ } @{$item[2]} ] }

op            : />=?|<=?|!=|==/      { [ 'BIN_OP',  $item[1] ] }
              | /le|ge|eq|ne|lt|gt/  { [ 'BIN_OP',  $item[1] ] }
              | /\|\||or|&&|and/     { [ 'BIN_OP',  $item[1] ] }
              | /[-+*\/%]/           { [ 'BIN_OP',  $item[1] ] }

function_call : function_name '(' args ')'  
                { [ 'FUNCTION_CALL', $item[1], $item[3] ] }
              | function_name ...'(' paren
                { [ 'FUNCTION_CALL', $item[1], [ $item[3] ] ] }
              | function_name '(' ')'
                { [ 'FUNCTION_CALL', $item[1] ] }

function_name : /[A-Za-z_][A-Za-z0-9_]*/

args          : <leftop: paren ',' paren>

var           : /[A-Za-z_][A-Za-z0-9_]*/ { [ 'VAR', $item[1] ] }

literal       : /-?\d*\.\d+/             { [ 'LITERAL', $item[1] ] }
              | /-?\d+/                  { [ 'LITERAL', $item[1] ] }
              | <perl_quotelike>         { [ 'LITERAL', $item[1][2] ] }

END


# create global parser
use vars qw($PARSER);
$PARSER = Parse::RecDescent->new($GRAMMAR);

# initialize preset function table
use vars qw(%FUNC);
%FUNC = 
  (
   'sprintf' => sub { sprintf(shift, @_); },
   'substr'  => sub { 
     return substr($_[0], $_[1]) if @_ == 2; 
     return substr($_[0], $_[1], $_[2]);
   },
   'lc'      => sub { lc($_[0]); },
   'lcfirst' => sub { lcfirst($_[0]); },
   'uc'      => sub { uc($_[0]); },
   'ucfirst' => sub { ucfirst($_[0]); },
   'length'  => sub { length($_[0]); },
   'defined' => sub { defined($_[0]); },
   'abs'     => sub { abs($_[0]); },
   'atan2'   => sub { atan2($_[0], $_[1]); },
   'cos'     => sub { cos($_[0]); },
   'exp'     => sub { exp($_[0]); },
   'hex'     => sub { hex($_[0]); },
   'int'     => sub { int($_[0]); },
   'log'     => sub { log($_[0]); },
   'oct'     => sub { oct($_[0]); },
   'rand'    => sub { rand($_[0]); },
   'sin'     => sub { sin($_[0]); },
   'sqrt'    => sub { sqrt($_[0]); },
   'srand'   => sub { srand($_[0]); },
  );

sub new { 
  my $pkg = shift;
  my $self;

  # check hashworthyness
  croak("HTML::Template::Expr->new() called with odd number of option parameters - should be of the form option => value")
    if (@_ % 2);
  my %options = @_;

  # check for unsupported options file_cache and shared_cache
  croak("HTML::Template::Expr->new() : sorry, this module won't work with file_cache or shared_cache modes.  This will hopefully be fixed in an upcoming version.")
    if ($options{file_cache} or $options{shared_cache});

  # push on our filter, one way or another.  Why did I allow so many
  # different ways to say the same thing?  Was I smoking crack?
  my @expr;
  if (exists $options{filter}) {
    # CODE => ARRAY
    $options{filter} = [ { 'sub'    => $options{filter},
                           'format' => 'scalar'         } ]
      if ref($options{filter}) eq 'CODE';

    # HASH => ARRAY
    $options{filter} = [ $options{filter} ]
      if ref($options{filter}) eq 'HASH';

    # push onto ARRAY
    if (ref($options{filter}) eq 'ARRAY') {
      push(@{$options{filter}}, { 'sub'    => sub { _expr_filter(\@expr, @_); },
                                  'format' => 'scalar' });
    } else {
      # unrecognized
      croak("HTML::Template::Expr->new() : bad format for filter argument.  Please check the HTML::Template docs for the allowed forms.");      
    }
  } else {
    # new filter
    $options{filter} = [ { 'sub'    => sub { _expr_filter(\@expr, @_) },
                           'format' => 'scalar'                    
                         } ];
  }  

  # force global_vars on
  $options{global_vars} = 1;

  # create an HTML::Template object, catch the results to keep error
  # message line-numbers helpful.
  eval {
    $self = $pkg->SUPER::new(%options, 
			     expr => \@expr, 
			     expr_func => $options{functions} || {});
  };
  croak("HTML::Template::Expr->new() : Error creating HTML::Template object : $@") if $@;

  return $self;
}

sub _expr_filter {
  my $expr = shift;
  my $text = shift;

  # find expressions and create parse trees
  my ($ref, $tree, $before_expr, $expr_text, $after_expr, $vars, $which, $out);
  $$text =~ s/
               <(?:!--\s*)?
               [Tt][Mm][Pp][Ll]_
               ([Ii][Ff]|[Uu][Nn][Ll][Ee][Ss][Ss]|[Vv][Aa][Rr]) # $1 => which tag
               (\s+[^<]+)?                                      # $2 => before expr
               \s+[Ee][Xx][Pp][Rr]=
               "([^"]*)"                                        # $3 => the actual expr
               (\s+[^>-]+)?                                     # $4 => after expr
               \s*(?:--)?>
             /
               $which       = $1;
               $before_expr = $2 || '';
               $expr_text   = $3;  
               $after_expr  = $4 || '';

               # add enclosing parens to keep grammar simple
               $expr_text = "($expr_text)";

               # parse the expression
               eval {
                 $tree = $PARSER->expression($expr_text);
               };
               croak("HTML::Template::Expr : Unable to parse expression: $expr_text")
                  if $@ or not $tree;

               # stub out variables needed by the expression
               $out = "<tmpl_if __expr_unused__>";
               foreach my $var (_expr_vars($tree)) {
                 next unless defined $var;
                 $out .= "<tmpl_var name=\"$var\">";
               }

               # save parse tree for later
               push(@$expr, $tree);
               
               # add the expression placeholder and replace
               $out . "<\/tmpl_if><tmpl_$which ${before_expr}__expr_" . $#{$expr} . "__$after_expr>";
             /xeg;
  # stupid emacs - /

  return;
}

# find all variables in a parse tree
sub _expr_vars {
    my $tree = shift;
    my %vars;

    # hunt for VAR nodes in the tree
    my @stack = @$tree;
    while (@stack) {
        my $node = shift @stack;
        if (ref $node and ref $node eq 'ARRAY') {
            if ($node->[0] eq 'VAR') {
                $vars{$node->[1]} = 1;
            } else {
                push @stack, @$node;
            }
        }
    }
    return keys %vars;
}

# allow loops to stay as HTML::Template objects, we don't need to
# override output for them
sub _new_from_loop {
    my ($pkg, @args) = @_;
    return HTML::Template->_new_from_loop(@args);
}

sub output {
  my $self = shift;
  my $parse_stack = $self->{parse_stack};
  my $options = $self->{options};
  my ($expr, $expr_func);

  # pull expr and expr_func out of the parse_stack for cache mode.
  if ($options->{cache}) {
    $expr      = pop @$parse_stack;
    $expr_func = pop @$parse_stack;
  } else {
    $expr      = $options->{expr};
    $expr_func = $options->{expr_func};
  }

  # setup expression evaluators
  my %param;
  for (my $x = 0; $x < @$expr; $x++) {
    my $node = $expr->[$x];
    $param{"__expr_" . $x . "__"} = sub { _expr_evaluate($node, @_) };
  }
  $self->param(\%param);

  # setup %FUNC 
  local %FUNC = (%FUNC, %$expr_func);

  my $result = $self->SUPER::output(@_);

  # restore cached values to their hideout in the parse_stack
  if ($options->{cache}) {
    push @$parse_stack, $expr_func;
    push @$parse_stack, $expr;
  }
  
  return $result;
}

sub _expr_evaluate {
  my ($tree, $template) = @_;
  my ($op, $lhs, $rhs, $node, $type, @stack);

  my @nodes = $tree;
  while (@nodes) {
      my $node = shift @nodes;
      my $type = $node->[0];

      if ($type eq 'LITERAL') {
          push @stack, $node->[1];
          next;
      }

      if ($type eq 'VAR') {
          push @stack, $template->param($node->[1]);
          next;
      } 

      if ($type eq 'SUB_EXPR') {
          unshift @nodes, @{$node}[1..$#{$node}];
          next;
      }

      if ($type eq 'BIN_OP') {
          $op  = $node->[1];
          $rhs = pop(@stack);
          $lhs = pop(@stack);

          # do the op
          if ($op eq '==') {push @stack, $lhs == $rhs; next; }
          if ($op eq 'eq') {push @stack, $lhs eq $rhs; next; }
          if ($op eq '>')  {push @stack, $lhs >  $rhs; next; }
          if ($op eq '<')  {push @stack, $lhs <  $rhs; next; }

          if ($op eq '!=') {push @stack, $lhs != $rhs; next; }
          if ($op eq 'ne') {push @stack, $lhs ne $rhs; next; }
          if ($op eq '>=') {push @stack, $lhs >= $rhs; next; }
          if ($op eq '<=') {push @stack, $lhs <= $rhs; next; }
          
          if ($op eq '+')  {push @stack, $lhs + $rhs;  next; }
          if ($op eq '-')  {push @stack, $lhs - $rhs;  next; }
          if ($op eq '/')  {push @stack, $lhs / $rhs;  next; }
          if ($op eq '*')  {push @stack, $lhs * $rhs;  next; }
          if ($op eq '%')  {push @stack, $lhs % $rhs;  next; }

          if ($op eq 'le') {push @stack, $lhs le $rhs; next; }
          if ($op eq 'ge') {push @stack, $lhs ge $rhs; next; }
          if ($op eq 'lt') {push @stack, $lhs lt $rhs; next; }
          if ($op eq 'gt') {push @stack, $lhs gt $rhs; next; }

          # short circuit or
          if ($op eq 'or' or $op eq '||') {
              if ($lhs) {
                  push @stack, 1;
                  next;
              }
              if ($rhs) {
                  push @stack, 1;
                  next;
              }
              push @stack, 0;
              next;
          } 

          # short circuit and
          if ($op eq '&&' or $op eq 'and') {
              unless ($lhs) {
                  push @stack, 0;
                  next;
              }
              unless ($rhs) {
                  push @stack, 0;
                  next;
              }
              push @stack, 1;
              next;
          }
    
          confess("HTML::Template::Expr : unknown op: $op");
      } 

      if ($type eq 'FUNCTION_CALL') {
          my $name = $node->[1];
          my $args = $node->[2];
          croak("HTML::Template::Expr : found unknown subroutine call ".
                ": $name.\n")
            unless exists($FUNC{$name});
          if (defined $args) {
              push @stack, 
                scalar 
                  $FUNC{$name}->(map { _expr_evaluate($_, $template) } @$args);
          } else {
              push @stack, scalar $FUNC{$name}->();
          }
          next;
      }

      confess("HTML::Template::Expr : unrecognized node in tree: $node");
  }

  unless (@stack == 1) {
      confess("HTML::Template::Expr : stack overflow!  ".
              "Please report this bug to the maintainer.");
  }

  return $stack[0];
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -