[system] / trunk / pg / macros / parserFunction.pl Repository:
ViewVC logotype

View of /trunk/pg/macros/parserFunction.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 5551 - (download) (as text) (annotate)
Tue Oct 2 20:48:05 2007 UTC (12 years, 2 months ago) by sh002i
File size: 4621 byte(s)
improved formatting for docs -- these were in pod sections but were all
formatted as verbatim sections, and i moved them into normal paragraphs,
lists, etc. should make things more readable from the web.

    1 =head1 NAME
    2 
    3 parserFunction.pl - An easy way of adding new functions to the current context.
    4 
    5 =head1 DESCRIPTION
    6 
    7 This file implements an easy way of creating new functions that
    8 are added to the current Parser context.  (This avoids having to
    9 do the complicated procedure outlined in the docs/parser/extensions
   10 samples.)
   11 
   12 To create a function that can be used in Formula() calls (and by
   13 students in their answers), use the parserFunction() routine, as
   14 in the following examples:
   15 
   16    parserFunction(f => "sqrt(x+1)-2");
   17 
   18    $x = Formula('x');
   19    parserFunction(f => sqrt($x+1)-2);
   20 
   21    parserFunction("f(x)" => "sqrt(x+1)-2");
   22 
   23    parserFunction("f(x,y)" => "sqrt(x*y)");
   24 
   25 The first parameter to parserFunction is the name of the function
   26 or the name with its argument list.  In the first case, the
   27 names of the variables are taken from the formula for the
   28 function, and are listed in alphabetical order.
   29 
   30 The second argument is the formula used to compute the value
   31 of the function.  It can be either a string or a Parser Formula
   32 object.
   33 
   34 =cut
   35 
   36 loadMacros('MathObjects.pl');
   37 
   38 sub _parserFunction_init {parserFunction::Init()}; # don't reload this file
   39 #
   40 #  The package that will manage user-defined functions
   41 #
   42 package parserFunction;
   43 our @ISA = qw(Parser::Function);
   44 
   45 sub Init {
   46   main::PG_restricted_eval('sub parserFunction {parserFunction->Create(@_)}');
   47 }
   48 
   49 sub Create {
   50   my $self = shift; my $name = shift; my $formula = shift;
   51   my $context = (Value::isContext($_[0]) ? shift : Value->context);
   52   my @argNames; my @argTypes; my @newVars;
   53   #
   54   #  Look for argument names for the function
   55   #   (check that the arguments are ok, and temporarily
   56   #    add in any variables that are not already there)
   57   #
   58   if ($name =~ m/^([a-z0-9]+)\(\s*(.*?)\s*\)$/i) {
   59     $name = $1; my $args = $2;
   60     @argNames = split(/\s*,\s*/,$args);
   61     foreach my $x (@argNames) {
   62       Value::Error("Illegal variable name '%s'",$x) if $x =~ m/[^a-z]/i;
   63       unless ($context->{variables}{$x}) {
   64   $context->variables->add($x=>'Real');
   65   push(@newVars,$x);
   66       }
   67     }
   68   } else {
   69     Value::Error("Illegal function name '%s'",$name)
   70       if $name =~ m/[^a-z0-9]/i;
   71   }
   72   #
   73   #  Create the formula and get its arguments and types
   74   #
   75   $formula = $context->Package("Formula")->new($context,$formula) unless Value::isFormula($formula);
   76   @argNames = main::lex_sort(keys(%{$formula->{variables}})) unless scalar(@argNames);
   77   foreach my $x (@argNames) {push(@argTypes,$context->{variables}{$x}{type})}
   78   #
   79   #  Add the function to the context and create the perl function
   80   #
   81   $context->functions->add(
   82     $name => {
   83       (length($name) == 1? (TeX=>$name): ()),
   84       @_, class => 'parserFunction', argCount => scalar(@argNames),
   85       argNames => [@argNames], argTypes => [@argTypes],
   86       function => $formula->perlFunction(undef,[@argNames]),
   87       formula => $formula, type => $formula->typeRef,
   88     }
   89   );
   90   main::PG_restricted_eval("sub main::$name {Parser::Function->call('$name',\@_)}");
   91   $context->variables->remove(@newVars) if scalar(@newVars);
   92 }
   93 
   94 #
   95 #  Check that there are the right number of arguments
   96 #  and they are of the right type.
   97 #
   98 sub _check {
   99   my $self = shift; my $name = $self->{name};
  100   return if $self->checkArgCount($self->{def}{argCount});
  101   my @argTypes = @{$self->{def}{argTypes}}; my $n = 0;
  102   foreach my $x (@{$self->{params}}) {
  103     my $atype = shift(@argTypes); $n++;
  104     $self->Error("The %s argument for '%s' should be of type %s",
  105      NameForNumber($n),$name,$atype->{name})
  106       unless (Parser::Item::typeMatch($atype,$x->{type}));
  107   }
  108   $self->{type} = $self->{def}{type};
  109 }
  110 
  111 #
  112 #  Call the function stored in the definition
  113 #
  114 sub _eval {
  115   my $self = shift; my $name = $self->{name};
  116   &{$self->{def}{function}}(@_);
  117 }
  118 
  119 #
  120 #  Check the arguments and compute the result.
  121 #
  122 sub _call {
  123   my $self = shift; my $name = shift;
  124   my $def = Value->context->{functions}{$name};
  125   &{$def->{function}}(@_);
  126 }
  127 
  128 =head3 ($Function)->D
  129 
  130  #
  131  #  Compute the derivative of (single-variable) functions
  132  #    using the chain rule.
  133  #
  134 
  135 =cut
  136 
  137 sub D {
  138   my $self = shift; my $def = $self->{def};
  139   $self->Error("Can't differentiate function '%s'",$self->{name})
  140     unless $def->{argCount} == 1;
  141   my $x = $def->{argNames}[0];
  142   my $Df = $def->{formula}->D($x);
  143   my $g = $self->{params}[0];
  144   return (($Df->substitute($x=>$g))*($g->D(@_)))->{tree}->reduce;
  145 }
  146 
  147 #
  148 #  Get the name for a number
  149 #
  150 sub NameForNumber {
  151   my $n = shift;
  152   my $name =  ('zeroth','first','second','third','fourth','fifth',
  153                'sixth','seventh','eighth','ninth','tenth')[$n];
  154   $name = "$n-th" if ($n > 10);
  155   return $name;
  156 }
  157 
  158 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9