Charme supports two kinds of primitives: natural numbers and primitive procedures. If the expression is a number, it is a string of digits. The
is_number procedure evaluates to
True if and only if its input is a number:
return is_number(expr) or is_primitive_procedure(expr)
return isinstance(expr, str) and expr.isdigit()
Here, we use the built-in function
isinstance to check if
expr is of type
str. The and expression in Python evaluates similarly to the Scheme
and special form: the left operand is evaluated first; if it evaluates to a false value, the value of the and expression is that false value. If it evaluates to a true value, the right operand is evaluated, and the value of the and expression is the value of its right operand. This evaluation rule means it is safe to use
expr.isdigit() in the right operand, since it is only evaluated if the left operand evaluated to a true value, which means
expr is a string.
Primitive procedures are defined using Python procedures. We define the procedure
callable, a procedure that returns true only for callable objects such as procedures and methods:
The evaluation rule for a primitive is identical to the Scheme rule:
Charme Evaluation Rule 1: Primitives. A primitive expression evaluates to its pre-defined value.
We need to implement the pre-defined values in our Charme interpreter.
To evaluate a number primitive, we need to convert the string representation to a number of type
int(s) constructor takes a string as its input and outputs the corresponding integer:
if is_number(expr): return int(expr)
else: return expr
else clause means that all other primitives (in Charme, this is only primitive procedures and Boolean constants) self-evaluate: the value of evaluating a primitive is itself.
For the primitive procedures, we need to define Python procedures that implement the primitive procedure. For example, here is the
primitive_plus procedure that is associated with the
+ primitive procedure:
if (len(operands) == 0): return 0
else: return operands + primitive_plus (operands[1:])
The input is a list of operands. Since a procedure is applied only after all subexpressions are evaluated, there is no need to evaluate the operands: they are already the evaluated values. For numbers, the values are Python integers, so we can use the Python
+ operator to add them. To provide the same behavior as the Scheme primitive
+ procedure, we define our Charme primitive
+ procedure to evaluate to
0 when there are no operands, and otherwise to recursively add all of the operand values.
The other primitive procedures are defined similarly:
if (len(operands) == 0): return 1
else: return operands * primitive_times (operands[1:])
if (len(operands) == 1): return -1 * operands
elif len(operands) == 2: return operands - operands
eval_error('- expects 1 or 2 operands, given %s: %s'
% (len(operands), str(operands)))
check_operands (operands, 2, '=')
return operands == operands
check_operands (operands, 2, '<')
return operands < operands
check_operands procedure reports an error if a primitive procedure is applied to the wrong number of operands:
if (len(operands) != num):
eval_error('Primitive %s expected %s operands, given %s: %s'
% (prim, num, len(operands), str(operands)))