-
Notifications
You must be signed in to change notification settings - Fork 0
Calc API
CalcAPI (or the calc package) defines classes to hold expression data and simplify parts of expressions. It is based on two main classes - Token and Resolver.
A token is the most fundamental part of an expression. It can be an operand (a number), operator (like +), or a function (like avg()). An expression is represented by a list of token. For example, 2,+,2 contains two operand tokens and one operator token.
Tokens are semantic parts of CalcAPI. Operator and function tokens do not actually solve the expression. That is accomplished by another set of classes (subclasses of Resolver). Also, Token objects are mutable and can be converted from one type to another.
File: calc/token.py
Properties: type, val
Methods: un_op, bin_op
type
Defines the type of the token; it can hold resolver.TYPE_RESOLVER (for functions), resolver.TYPE_OPERATOR (for operators), or resolver.TYPE_NUMBER (for operands).
val
Holds the value of the token, which can be a string literal (if type == resolver.TYPE_RESOLVER) holding a function name, a character (if type == resolver.TYPE_OPERATOR) holding the written operator, or a numeric value (if type == resolver.TYPE_NUMBER).
Token.un_op()
If this token is an unary operator, this method will return the UnOp object corresponding to it. A unary operator is placed between an operator and a number, e.g. (,-,2,).
Token.bin_op()
If this token is an binary operator, this method will return the 'BinOp' object corresponding to it. A binary operator is placed between to number tokens, e.g. '2', '+', '2'
A resolver object solves a specific operator token (note that resolver objects don't exist, only static and class methods are used). To do so, it requires the expression and the position of the operator token to resolve.
NOTE: Resolver is an abstract class. Its methods are required to be implemented by subclasses for each operator that this program can solve. See BinOp and AddOp for an example implementation.
Resolution of an operator (or function) is done in three steps - collection of arguments, evaluation, and cleanup. These steps are describes as follows:
-
Collection of arguments: This step involves gathering raw data from the expression (like numbers) and combining them into an (ordered) list. These arguments are passed into the evaluation step. For example, in
2,+,3, the resolver for the plus token will gather the numbers before and after it. It will be returned in an list[2, 3]. -
Evaluation: The resolver will now be able to access the operands directly and do the required operation. The result is returned as one number. For example, in
2,+,3, the evaluation step will get[2, 3]which will be evaluated as2+3=5. -
Cleanup: The resolver will now need to reflect this operation in the expression itself. Hence, result is put in place of the evaluated tokens. For example,
2,+,3will be cleaned into5(removing the+,3tokens and converting the2token into a5by setting its 'val' property).
File: calc/resolver.py
Methods: tostr, collect, eval, res, adj
Methods for subclasses: _deval
@staticmethod
tostr()
This method returns the string (or character) representation of the resolver class.
NOTE: This method should be implemented by subclasses.
@staticmethod
collect(exp, index)
This method returns the list of arguments required in the evaluation step, given the expression in which the unresolved token exists and its index.
NOTE: This method should be implemented by subclasses.
@staticmethod
eval(tlist)
This method takes the arguments returned by collect and solves the operands given. The result is returned as
a number.
NOTE: This method should be implemented by subclasses.
@classmethod
res(cls, exp, index)
This method takes the expression in which the unresolved token is placed at index and executes the three steps discussed above.
NOTE: This method should be implemented by subclasses. It should call collect, eval and then do cleanup with the help of _deval method provided by Resolver.
@staticmethod
adj(exp, index)
While the front-end solves the expression, it holds the index of the current token being used. For example, while calling res in '2','+','2' this index will be 1. adj specifies the position of this index the token is resolved. It should be called before res (before cleanup happens and the arguments are at their positions).
NOTE: This method should be implemented by subclasses. For an example implementation, see BinOp.
_deval(exp, stidx, enidx, result)
This method does the cleanup by removing the tokens between stidx and enidx (inclusive of stidx and enidx) and placing the result in place of them at the same position.
NOTE: This method actually removes the tokens between stidx and enidx excluding stidx, and then placing the result at stidx by changing the token. This is faster than the above method.