Evaluation of an Expression¶
In contrast to the simplicity and regularity for representing the data
Expression, evaluation of this data or expresion is a bit more
involved than conventional programming languages. I suppose this is to
Part of the complexity revolves around the fact that the way function method lookup works is by pattern matching the expression. Also, there can be rule-based term-rewriting which goes on in conjunction with method lookup.
If you have programmed in WL, aside from the the Python-syntax and conventions used here, a lot of this should seem familiar,
If however you are not familiar with WL, but very familiar with Python
or similar languages, a lot of this can seem very mysterious at first:
functions don’t get called using a traditional way where you create an
Number() and then instantiate a method on that, like
__plus__(), or even
Of course, since the underlying interpreter language is Python, Python object creation and method lookup on that does happen. But it happens in a much more roundabout way.
For Python and Object-Oriented programmers, as an analogy for the
complexity and indirectness, an Object-Oriented “method dispatch” is
analogous. In Python or any Object-Oriented programming language, when
a.b(): there is a method lookup in the
a object, so
at runtime the type of
a has to be inspected. And after having
that, the method handle
b needs to be computed. And this comes
from a class heirarchy.
Mathics and WL are not Object Oriented, so there is no such class-hierarchy lookup. However, as mentioned above, pattern matching is used to decide which method of the object to call.
Function Name to Python method lookup¶
Expression has not been rewritten, the entire function
invocation in Mathics comes from the first leaf (or
should be a
Symbol. In Python this will be a class some sort, such
SympyFunction. These classes
are described in a later section.
The remaining leaves of the
Expression are the parameters to give
In the simplest case, the
evaluate() method is called. This is
used when a function has no parameters or arguments. In other words,
it looks like a constant or variable name, and usually is prefaced
$`. Examples here are ``$VersionNumber or
Functions which take no parameters are generally subclassed off of the
However when a function takes parameters it method’s Object class is
derived either directly indirectly from the
rather than the
Builtin. To figure out which
apply method in
the class object to call, each method’s document string (or docstring)
is consulted. The lookup process is kicked off using the
evaluate() method found in the
As we go along, we’ll describe other conventions that are used that
are crucial in getting the interpreter work properly. But for now,
just remember that unless there is an
evaluate() method, there is
a method name in a Mathics function class that begins with
and its docstring is used to figure out whether the leaves of the list
are applicable to that function.
Here is an example for the Environment primitive taken from the code
class Environment(Builtin): def apply(self, var, evaluation): """Environment[var_?StringQ]""" ...
apply() function above will get called when finding a
Head value is
Environment and it has one
leaf or parameter which which we will call
var. That leaf or
parameter should also much be a
Function Name Descriptions¶
Online and printed documentation for builtin
Environment comes from the docstring for
class Environment if that exists.
In the example above, it was omitted. Here is what it looks like in the actual code.
class Environment(Builtin): """ <dl> <dt>'Environment[$var$]' <dd>gives the value of an operating system environment variable. </dl> X> Environment["HOME"] = ... """ def apply(self, var, evaluation): <dl> <dt>'Environment[$var$]' <dd>gives the value of an operating system environment variable. </dl> X> Environment["HOME"] = ... """"
The XML/HTML markup is used to format help nicely. “Documentation markup” elsewhere describes this markup.
Python Code for Evaluating an Expression¶
Building on the code shown above for parsing an expression, here is code to evaluate an expression from a string:
# The below is a repeat of the parsing code... from mathics.core.parser import parse, SingleLineFeeder from mathics.core.definitions import Definitions definitions = Definitions(add_builtin=True) str_expression = "1 + 2 / 3" expr = parse(definitions, SingleLineFeeder(str_expression)) # This code is new... from mathics.core.evaluation import Evaluation evaluation = Evaluation(definitions=definitions, catch_interrupt=False) last_result = expr.evaluate(evaluation) print("type", type(last_result)) print("expr: ", last_result)
Running the above produces:
type <class 'mathics.core.expression.Rational'> expr: 5/3
All of the above is wrapped nicely in the module
performs the above. So here is an equivalent program:
from mathics.session import session str_expression = "1 + 2 / 3" result = session.evaluate(str_expression)
The fundamental classes that functions are built up from are described below. Most of these classes are defined in mathics.builtin.base.
Atom Class Attributes¶
Recall that an Expression to be evaluated is kind of S-expression
ExpressionList, where each list item is either itself
ExpressionList or an object in a class derived from
Atom class we encountered earlier when describing the nodes
that get created intially from a parse. However there are a few other
kinds of Atoms or fundamention objects that can appear in an
Evaluation list. These are
Builtin and Predefined¶
Most of the functions loaded when Mathics starts up and before any
packages are loaded are either
Predefined is a subclass of
A feature of the
Predefined class class is the convention that its
evaluation() method looks at the docstring of methods that start
applied in order to figure out which method to call
To be continued…