Skip to content

Commit

Permalink
Update the AST for functions as attributes
Browse files Browse the repository at this point in the history
The AST needed some updates to allow functions to be used as attributes,
effectively adding support for methods.
  • Loading branch information
zeroSteiner committed Jun 15, 2024
1 parent c6fff08 commit 8aa34ac
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 3 deletions.
6 changes: 6 additions & 0 deletions docs/source/rule_engine/ast.rst
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@ Literal Expressions
.. autoattribute:: result_type
:annotation: = FLOAT

.. autoclass:: FunctionExpression
:show-inheritance:

.. autoattribute:: result_type
:annotation: = FUNCTION

.. autoclass:: MappingExpression
:show-inheritance:

Expand Down
18 changes: 15 additions & 3 deletions lib/rule_engine/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ def __init__(self, context, value):
:param value: The native Python value.
"""
self.context = context
if not isinstance(value, self.result_type.python_type) and self.result_type.is_scalar:
if self.result_type.is_scalar and DataType.from_value(value) != self.result_type:
raise TypeError("__init__ argument 2 must be {}, not {}".format(self.result_type.python_type.__name__, type(value).__name__))
self.value = value

Expand Down Expand Up @@ -277,6 +277,12 @@ def __init__(self, context, value, **kwargs):
def from_string(cls, context, string):
return cls(context, parse_float(string))

class FunctionExpression(LiteralExpressionBase):
"""Literal mapping expression representing a function."""
# there's no syntax for defining functions, but this is required by the parser when a method is referred to on a
# literal value, e.g. `b"41".decode('utf-8')`
result_type = DataType.FUNCTION

class MappingExpression(LiteralExpressionBase):
"""Literal mapping expression representing a set of associations between keys and values."""
result_type = DataType.MAPPING
Expand Down Expand Up @@ -818,7 +824,10 @@ def evaluate(self, thing):
def reduce(self):
if not _is_reduced(self.object):
return self
return LiteralExpressionBase.from_value(self.context, self.evaluate(None))
literal = LiteralExpressionBase.from_value(self.context, self.evaluate(None))
if literal.result_type == DataType.FUNCTION and DataType.is_compatible(self.result_type, DataType.FUNCTION):
literal.result_type = self.result_type
return literal

def to_graphviz(self, digraph, *args, **kwargs):
digraph.node(str(id(self)), "{}\nname={!r}".format(self.__class__.__name__, self.name))
Expand Down Expand Up @@ -1063,7 +1072,10 @@ def __init__(self, context, function, arguments):
def build(cls, context, function, arguments):
return cls(context, function.build(), tuple(argument.build() for argument in arguments)).reduce()

# because there is no syntax for defining a function, they can't be reduced until symbols can be reduced
def reduce(self):
if not _is_reduced(self.function, *self.arguments):
return self
return LiteralExpressionBase.from_value(self.context, self.evaluate(None))

def evaluate(self, thing):
function = self.function.evaluate(thing)
Expand Down

0 comments on commit 8aa34ac

Please sign in to comment.