USING THE PARSER:

To use the new parser in your own problems, you need to load the
"Parser.pl" macro file:

    loadMacros("Parser.pl");

which defines the commands you need to interact with the parser.
Once you have done that, you can call the Parser functions to create
formulas for you.  The main call is Formula(), which takes a string and
returns a parsed version of the string.  For example:

      $f = Formula("x^2 + 3x + 1");

will set $f to a reference to the parsed version of the formula.


WORKING WITH FORMULAS:

A formula has a number of methods that you can call.  These include:

    $f->eval(x=>5)      Evaluate the formula when x is 5.
                        If $f has more variables than that, then
                        you must provide additional values, as in
                        $f->eval(x=>3,y=>1/2);

    $f->reduce          Tries to remove redundent items from your
                        formula.  For example, Formula("1x+0") returns "x".
                        Reduce tries to factor out negatives and do
                        some other adjustments as well.  (There still
                        needs to be more work done on this.  What it does
                        is correct, but not always smart, and there need
                        to be many more situations covered.)  All the
reduction rules can be individually enabled
                        or disabled using the Context()->reduction->set()
method, but the documentation for the various
                        rules is not yet ready.

    $f->substitute(x=>5)  Replace x by the value 5 throughout (you may want
                        to reduce the result afterword, as this is not
                        done automatically).  Note that you can replace a
                        variable by another formula, if you wish.  To make
                        this easier, substitute will apply Formula() to
                        any string values automatically.  E.g.,
                            Formula("x-1")->substitute(x=>"y")
                        returns "y-1" as a formula.

    $f->string          returns a string representation of the formula
                        (should be equivalent to the original, though not
                        necessarily equal to it).

    $f->TeX              returns a LaTeX representation of the formula.
                        You can use this in BEGIN_TEXT...END_TEXT blocks
                        as follows:

                            BEGIN_TEXT
                              Suppose \(f(x) = \{$f->TeX}\). ...
                            END_TEXT

    $f->perl            returns a representation of the formula that could
                        be evaluated by perl's eval() function.

    $f->perlFunction    returns a perl code block that can be called to
                        evaluate the function.  For example:

                            $f = Formula('x^2 + 3')->perlFunction;
                            $y = &$f(5);

                        will assign the value 28 to $y.
                        You can also pass a function name to perlFunction
                        to get a named function to call:

                            Formula('x^2 + 3')->perlFunction('f');
                            $y = f(5);

                        If the formula involves more than one variable,
                        then the paramaters should be given in
                        alphabetical order.

                            Formula('x^2 + y')->perlFunction('f');
                            $z = f(5,3);  # $z is 28.

                        Alternatively, you can tell the order for the
                        parameters:

                            Formula('x^2 + y')->perlFunction('f',['y','x']);
                            $z = f(5,3); $ now $z is 14.


COMBINING FORMULAS:

There is a second way to create formulas.  Once you have a formula, you can
create additional formulas simply by using perls' built-in operations and
functions, which have been overloaded to handle formulas.  For example,

    $x = Formula('x');
    $f = 3*x**2 + 2*$x - 1;

makes $f be a formula, and is equivalent to having done

    $f = Formula("3x^2 + 2x - 1");

This can be very convenient, but also has some pitfalls.  First, you
need to include '*' for multiplication, since perl doesn't do implied
multiplication, and you must remember to use '**' not '^'.  (If you use '^'
on a formula, the parser will remind you to use '**'.)  Second, the
precedences of the operators in perl are fixed, and so changes you make to
the precedence table for the parser are not reflected in formulas produced
in this way.  (The reason '^' is not overloaded to do exponentiation is
that the precedence of '^' is wrong for that in perl, and can't be
changed.)  As long as you leave the default precedences, however, things
should work as you expect.

Note that the standard functions, like sin, cos, etc, are overloaded to
generate appropriate formulas when their values are formulas.  For example,

    $x = Formula('x');
    $f = cos(3*$x + 1);

produces the same result as $f = Formula("cos(3x+1)"); and you can then go
on to output its TeX form, etc.


SPECIAL SYNTAX:

This parser has support for some things that are missing from the current
one, like absolute values.  You can say  |1+x|  rather than  abs(1+x)
(though both are allowed), and even |1 - |x|| works.

Also, you can use  sin^2(x)  (or even sin^2 x) to get  (sin(x))^2.

Finally, you can use  sin^-1(x)  to get  arcsin(x).

There is an experimental set of operator precedences that make it possible
to write  sin 2x + 3 and get  sin(2x) + 3.  See examples/7-precedence.pg
for some details.


THE FORMULA TYPES:

The parser understands a wide range of data types, including real and
complex numbers, points, vectors, matrices, arbitrary lists, intervals,
unions of intervals, and predefined words.  Each has a syntax for use
within formulas, as described below:

    numbers        the usual form:  153, 233.5, -2.456E-3, etc.

    complex        a + b i  where a and b are numbers:  1+i, -5i, 6-7i, etc.

    infinitites    the words 'infinity' or '-infinity' (or several
                  equivalents).

    point          (a,b,c)  where a, b and c are real or complex numbers.
                  any number of coordinates are allowed.  Eg, (1,2),
                  (1,0,0,0), (-1,2,-3).  Points are promoted to vectors
                  automatically, when necessary.

    vector        <a,b,c>  or  a i + b j + c k  (when used in vector context).
                  As with points, vectors can have any number of
                  coordinates.  For example, <1,0,0>, <-1,3>, <x,1-x>, etc.

    matrix        [[a11,...,a1n],...[am1,...amn]], i.e., use [..] around
                  each row, and around the matrix itself.  The elements
                  are separated by commas (not spaces).  e.g,
                        [[1,2],[3,4]]    (a 2x2 matrix)
                        [1,2]            (a 1x2 matrix, really a vector)
                        [[1],[2]]        (a 2x1 matrix, ie. column vector)
                  Points and vectors are promoted to matrices when
                  appropriate.  Vectors are converted to column vectors
                  when needed for matrix-vector multiplication.  Matrices
                  can be 3-dimensional or higher by repeated nesting of
                  matrices.  (In this way, a 2-dimensional matrix is really
                  thought of as a vector of vectors, and n-dimensional
                  ones as vectors of (n-1)-dimensional ones.)

  list            (a,b,c)  where a,b,c are arbitrary elements.
                  For example, (1+i, -3, <1,2,3>, Infinity).
                  The empty list () is allowed, and the parentheses are
                  optional if there is only one list.  (This makes it
                  possible to make list-based answer checkers that
                  really know where the separations occur.)

  interval        (a,b), (a,b], [a,b), [a,b], or [a,a]  where a and b are
                  numbers or appropriate forms of infinity.
                  For example, (-INF,3], [4,4], [2,INF), (-INF,INF).

  union          represented by 'U'.  For example  [-1,0) U (0,1].

  string          special predefined strings like NONE and DNE.

These forms are what are used in the strings passed to Formula().
If you want to create versions of these in perl, there are several
ways to do it.  One way is to use the Compute() command, which takes a
string parses it and then evaluates the result (it is equivalent to
Formula(...)->eval).  If the formula produces a vector, the result
will be a Vector constant that you can use in perl formulas by hand.

For example:

    $v = Compute("<1,1,0> >< <-1,4,-2>");

would compute the dot product of the two vectors and assign the
resulting vector object to $v.

Another way to generate constants of the various types is to use the
following routines.  If their inputs are constant, they produce a
constant of the appropriate type.  If an input is a formula, they
produce corresponding formula objects.

    Real(a)            create a real number with "fuzzy"
                      comparisons (so that 1.0000001 == Real(1) is true).

    Complex(a,b)      create a complex number a + b i

    Infinity          creates the +infinity object
    -(Infinity)        creates -infinity

    Point(x1,...xn) or Point([x1,...,xn])      produces (x1,...,xn)

    Vector(x1,...,xn) or Vector([x1,...,xn])  produces <x1,...,xn>

    Matrix([a11,...,a1m],...,[am1,...,amn]) or
    Matrix([[a11,...,a1m],...,[am1,...,amn]])  produces an n x m matrix

    List(a,...,b)  produces a list with the given elements

    Interval('(',a,b,']')  produces (a,b], (the other endpoints work as
                            expected.  Use 'INF' and '-INF' for infinities.)

    Union(I1,...,In)  takes the union of the n intervals. (where I1 to In
                      are intervals.)

    String(word)  Produces a string object for the given word (if it
                  is a known word).  This is mostly to be able to
  call the ->cmp and ->TeX methods.

For example,

    $a = random(-5,5,1)
    $V = Vector($a,1-$a,$a**2+1);

produces a vector with some random coordinates.

Objects of these types also have TeX, string and perl methods, so you can
use:

    Vector(1,2,3)->TeX

to produce a TeX version of the vector, just as you can with formulas.

There are several "constant" functions that generate common constant
values.  These include pi, i, j, k and Infininty.  you can use these
in perl expressions as though they were their actual values:

$z = $a + $b * i;
        $v = $a*i + $b*j + $c*k;
        $I = Infinity;

Note that because of a peculiarity of perl, you need to use -(pi)
or - pi (with a space) rather than -pi, and similary for the other
functions.  Without this, you will get an error message about an
ambiguity being resolved.  (This is not a problem if you process your
expressions through the parser itself, only if you are writing
expressions in perl directly.  Note that since student answers are
processed by the parser, not perl directly, they can write -pi without
problems.)

Another useful command is Compute(), which evaluates a formula and
returns its value.  This is one way to create point or vector-valued
constants, but there is an easier way discussed below.


SPECIFYING THE CONTEXT

You may have noticed that "i" was used in two different ways in the
examples above.  In the first example, it was treated as a complex
number and the second as a coordinate unit vector.  To control which
interpretation is used, you specify a parser "context".

The context controls what operations and functions are defined in the
parser, what variables and constants to allow, how to interpret
various paretheses, and so on.  Changing the context can completely
change the way a formula is interpreted.

There are several predefined contexts:  Numeric, Complex, Vector,
Interval and Full.  (You can also define your own contexts, but that
will be described elsewhere.)  To select a context, use the Context()
function, e.g.

  Context("Numeric");

selects the numeric context, where i, j and k have no special meaning,
points and vectors can't be used, and the only predefined variable is
'x'.

On the other hand, Context("Vector") makes i, j and k represent the
unit coordinate vectors, and defines variables 'x', 'y' and 'z'.

Context("Interval") is like numeric context, but it also defines the
parentheses so that they will form intervals (rather than points or
lists).

Once you have selected a context, you can modify it to suit the
particular needs of your problem.  The command

  $context = Context();

gets you a reference to the current context object (you can also use
something like

  $context = Context("Numeric");

to set the context and get its reference at the same time).  Once you
have this reference, you can call the Context methods to change values
in the context.  These are discussed in more detail in the
documentation of the Context object [not yet written], but some of the
more common actions are described here.

To add a variable, use, for example,

      $context->variables->add(y=>'Real');

To delete any existing variables and replace them with new ones, use

      $context->variables->are(t=>'Real');

To remove a variable, use

      $context->variables->remove('t');

To get the names of the defind variables, use

      @names = $context->variables->names;


Similarly, you can add a named constant via

      $context->constants->add(M=>1/log(10));

and can change, remove or list the constants via methods like those
used for variables above.  The command

      $M = $constant->constants->get('M');

will return the value of the consant M.  (See the
pg/lib/Value/Context/Data.pm file for more information on the methods
you can call for the various types of context data.)

To add new predefined words (like 'NONE' and 'DNE'), use something
like

$constant->strings->add(TRUE=>{},FALSE=>{});

Note that strings are case-sensitive, so you might want to add

        $constant->strings->add(
  true => {alias=>'TRUE'},
  false => {alias=>'FALSE'},
);

so that either "TRUE" or "true" will be interpreted as TRUE.

There are a number of values stored in the context that control things
like the tolerance used when comparing numbers, and so on.  You
control these via commands like:

$context->flags->set(tolerance=>.00001);

For example,

      $context->flags->set(ijk=>1);

will cause the output of all vectors to be written in ijk format
rather than <...> format.

Finally, you can add or modify the operators and functions that are
available in the parser via calls to $context->operators and
$context->functions.  See the files in webwork2/docs/parser/extensions
for examples of how to do this.
Last modified: Saturday, 22 February 2020, 3:11 PM