Problem 2¶
Evaluate mathematical expressions.
Please see page 3 and page 4 of Exercise 11.
First, provide a tokenizer and a parser for the mathematical expressions.
Second, evaluate the mathematical expressions. The functions cos
, sin
and
tan
need to be supported. The evaluation function is given a dictionary of parameter
values.
An exception should be raised if a parameter has not been specified.
Classes:
|
Define the token kind. |
|
Define a regular expression which specifies a token. |
|
Represent a token of the source code. |
|
Represent unary operators. |
|
Represent the associativity of a binary operator. |
|
Specify precedence and associativity. |
|
Represent binary operators. |
|
Represent an identifier of a variable or of a function. |
|
Represent a valid expression as an abstract syntax tree (AST). |
|
Represent a constant in the AST. |
|
Represent a variable in the AST. |
|
Represent an unary operation in the AST. |
|
Represent a binary operation in the AST. |
|
Represent a function call in the AST. |
|
Represent tokens without whitespace. |
Data:
Define rules so that we can map token kind 🠒 regular expression. |
|
Map token kind 🠒 rule to be matched for that token kind. |
|
Express an identifier of a variable or a function. |
Functions:
|
Tokenize the given |
|
Serialize the |
|
Parse the given tokens into an expression. |
|
Convert the AST given as |
|
Evaluate the given expression |
|
Go recursively over the expression and collect the variable names. |
- class TokenKind(value)[source]¶
Define the token kind.
Attributes:
Number literal
Variable (or function) identifier
Operator
Opening parenthesis
Closing parenthesis
Whitespace (including tabs etc.)
- NUM = 1¶
Number literal
- VAR = 2¶
Variable (or function) identifier
- OP = 4¶
Operator
- OPEN = 5¶
Opening parenthesis
- CLOSE = 6¶
Closing parenthesis
- WHITESPACE = 7¶
Whitespace (including tabs etc.)
- class TokenizationRule(kind: TokenKind, pattern: Pattern[str])[source]¶
Define a regular expression which specifies a token.
Methods:
__init__
(kind, pattern)Initialize with the given values.
__repr__
()Represent the instance as a string for debugging.
- TOKENIZATION = [TokenizationRule(1, re.compile('(inf|0|[1-9][0-9]*)(\\.[0-9]+)?(e[+\\-]?[0-9]+)?')), TokenizationRule(2, re.compile('[a-zA-Z_][a-zA-Z_0-9]*')), TokenizationRule(4, re.compile('[+\\-*/^]')), TokenizationRule(5, re.compile('\\(')), TokenizationRule(6, re.compile('\\)')), TokenizationRule(7, re.compile('\\s+'))]¶
Define rules so that we can map token kind 🠒 regular expression.
- TOKENIZATION_MAP: Mapping[TokenKind, TokenizationRule] = {<TokenKind.NUM: 1>: TokenizationRule(1, re.compile('(inf|0|[1-9][0-9]*)(\\.[0-9]+)?(e[+\\-]?[0-9]+)?')), <TokenKind.VAR: 2>: TokenizationRule(2, re.compile('[a-zA-Z_][a-zA-Z_0-9]*')), <TokenKind.OP: 4>: TokenizationRule(4, re.compile('[+\\-*/^]')), <TokenKind.OPEN: 5>: TokenizationRule(5, re.compile('\\(')), <TokenKind.CLOSE: 6>: TokenizationRule(6, re.compile('\\)')), <TokenKind.WHITESPACE: 7>: TokenizationRule(7, re.compile('\\s+'))}¶
Map token kind 🠒 rule to be matched for that token kind.
- class Token(value: str, start: int, end: int, kind: TokenKind)[source]¶
Represent a token of the source code.
Methods:
__init__
(value, start, end, kind)Initialize with the given values.
__eq__
(other)Compare against
other
of the same class based on all the properties.__repr__
()Represent the instance as a string for debugging.
- __init__(value: str, start: int, end: int, kind: TokenKind) None [source]¶
Initialize with the given values.
- Requires
start < end
TOKENIZATION_MAP[kind].pattern.fullmatch(value)
- tokenize(text: str) List[Token] [source]¶
Tokenize the given
text
.- Ensures
not (len(result) > 0) or result[0].start == 0
(Text tokenized from the start)
not (len(result) > 0) or result[-1].end == len(text)
(Text tokenized till the end)
all( token1.end == token2.start for token1, token2 in common.pairwise(result) )
(Tokens consecutive)
all( token.value == text[token.start:token.end] for token in result )
(Token values correct)
tokens_to_text(result) == text
- tokens_to_text(tokens: Sequence[Token]) str [source]¶
Serialize the
tokens
back into the original text.- Ensures
tokens == tokenize(result)
- class UnOp(value)[source]¶
Represent unary operators.
Attributes:
Unary negative
- MINUS = '-'¶
Unary negative
- class Associativity(value)[source]¶
Represent the associativity of a binary operator.
Attributes:
Left associative
Right associative
- LEFT = 'Left'¶
Left associative
- RIGHT = 'Right'¶
Right associative
- class BinOpInfo(precedence: int, associativity: Associativity)[source]¶
Specify precedence and associativity.
Methods:
__init__
(precedence, associativity)- __init__(precedence: int, associativity: Associativity) None [source]¶
- class BinOp(value)[source]¶
Represent binary operators.
Attributes:
Addition
Subtraction
Multiplication
Division
Power
- ADD = '+'¶
Addition
- SUB = '-'¶
Subtraction
- MUL = '*'¶
Multiplication
- DIV = '/'¶
Division
- POW = '^'¶
Power
- IDENTIFIER_RE = re.compile('[a-zA-Z_][a-zA-Z0-9]*')¶
Express an identifier of a variable or a function.
- class Identifier(value: str)[source]¶
Represent an identifier of a variable or of a function.
Methods:
__new__
(cls, value)Enforce the identifier properties on
value
.- static __new__(cls, value: str) Identifier [source]¶
Enforce the identifier properties on
value
.- Requires
IDENTIFIER_RE.fullmatch(value)
- class Constant(value: float)[source]¶
Represent a constant in the AST.
Methods:
__init__
(value)Initialize with the given values.
__eq__
(other)Compare against
other
of the same class based on the properties.__repr__
()Represent the instance as a string for debugging.
- __init__(value: float) None [source]¶
Initialize with the given values.
- Requires
not math.isnan(value)
value >= 0.0
- class Variable(identifier: Identifier)[source]¶
Represent a variable in the AST.
Methods:
__init__
(identifier)Initialize with the given values.
__eq__
(other)Compare against
other
of the same class based on the properties.__repr__
()Represent the instance as a string for debugging.
- __init__(identifier: Identifier) None [source]¶
Initialize with the given values.
- class UnaryOperation(target: Expr, operator: UnOp)[source]¶
Represent an unary operation in the AST.
Methods:
__init__
(target, operator)Initialize with the given values.
__eq__
(other)Compare against
other
of the same class based on the properties.__repr__
()Represent the instance as a string for debugging.
- class BinaryOperation(left: Expr, operator: BinOp, right: Expr)[source]¶
Represent a binary operation in the AST.
Methods:
__init__
(left, operator, right)Initialize with the given values.
__eq__
(other)Compare against
other
of the same class based on the properties.__repr__
()Represent the instance as a string for debugging.
- class Call(name: str, argument: Expr)[source]¶
Represent a function call in the AST.
Methods:
__init__
(name, argument)Initialize with the given values.
__eq__
(other)Compare against
other
of the same class based on the properties.__repr__
()Represent the instance as a string for debugging.
- __init__(name: str, argument: Expr) None [source]¶
Initialize with the given values.
- Requires
re.fullmatch(r"(sin|cos|tan)", name)
- class TokensWoWhitespace(tokens: Sequence[Token])[source]¶
Represent tokens without whitespace.
Methods:
__new__
(cls, tokens)Enforce the properties on
tokens
.Get the token(s) at the given index.
__len__
()Return the number of the tokens.
__iter__
()Iterate over the tokens.
- static __new__(cls, tokens: Sequence[Token]) TokensWoWhitespace [source]¶
Enforce the properties on
tokens
.- Requires
all(token.kind != TokenKind.WHITESPACE for token in tokens)
- __getitem__(index: int) Token [source]¶
- __getitem__(index: slice) TokensWoWhitespace
Get the token(s) at the given index.
- unparse(expr: Expr) str [source]¶
Convert the AST given as
expr
back to the source code as text.- Ensures
parse_tokens(tokenize(result)) == expr
- evaluate(expr: Expr, lookup: Mapping[Identifier, float]) float [source]¶
Evaluate the given expression
expr
substituting variables withlookup
.
- collect_variables(expr: Expr) Set[Identifier] [source]¶
Go recursively over the expression and collect the variable names.