Properties: add a membership test expression and an equality operation

Change-Id: I82ac2b77854c36b52a90e1e5b082e21826fa2bbf
TN: OC10-027
This commit is contained in:
Pierre-Marie de Rodat
2015-12-17 15:44:06 +01:00
parent a4d7adfb62
commit 2991067997

View File

@@ -121,6 +121,12 @@ class AbstractExpression(Frozable):
def _build_cast(astnode):
return Cast(self, astnode)
def _build_contains(other):
return Contains(self, other)
def _build_equals(other):
return Eq(self, other)
def _build_filter(filter, var=None):
return Map(var, var or Var, self, filter)
@@ -137,6 +143,8 @@ class AbstractExpression(Frozable):
'all': _build_all,
'any': _build_any,
'cast': _build_cast,
'contains': _build_contains,
'equals': _build_equals,
'filter': _build_filter,
'is_a': _build_is_a,
'map': _build_map,
@@ -181,6 +189,56 @@ class AbstractExpression(Frozable):
return BinaryBooleanOperator(BinaryBooleanOperator.AND, self, other)
class CollectionExpression(AbstractExpression):
"""
Base class to provide common code for abstract expressions working on
collections.
"""
def __init__(self, expr, collection, induction_var=None):
"""
:param induction_var: Variable to use in "expr".
:type induction_var: None|InductionVariable
:param AbstractExpression expr: Expression to evaluate for each item in
"collection".
:param AbstractExpression collection: Collection on which this map
operation works.
"""
super(CollectionExpression, self).__init__()
self.expr = expr
self.collection = collection
self.induction_var = induction_var
def construct_collection(self):
"""
Construct the collection expression and determine the type of the
induction variable.
:rtype: (ResolvedExpression, langkit.compiled_types.CompiledType)
"""
collection_expr = self.collection.construct()
assert (collection_expr.type.is_list_type or
issubclass(collection_expr.type, compiled_types.ArrayType)), (
'Map cannot iterate on {}, which is not a collection'
).format(collection_expr.type)
return (collection_expr, collection_expr.type.element_type)
def bind_induction_var(self, type):
"""
Return a context manager to bind temporarily the induction variable to
a specific type. Also create the induction variable first if there is
none.
:param langkit.compiled_types.CompiledType type: Type for the induction
variable.
"""
default_induction_var = not self.induction_var
if default_induction_var:
self.induction_var = Vars.create_default()
return self.induction_var.vars.bind(self.induction_var, type)
class OpCall(AbstractExpression):
"""
Abstract expression that is the result of a call expression evaluation.
@@ -264,6 +322,84 @@ class Cast(AbstractExpression):
return CastExpr(expr, self.astnode)
class Contains(CollectionExpression):
"""
Abstract expression for a membership test expression.
"""
def __init__(self, collection, item):
"""
:param AbstractExpression collection: The collection of which to check
membership.
:param AbstractExpression item: The item to check in "collection".
"""
self.item = item
super(Contains, self).__init__(
Vars.membership_test_item.equals(self.item),
collection,
Vars.membership_test_item,
)
def construct(self):
"""
Construct a resolved expression for this.
:rtype: QuantifierExpr
"""
collection, elt_type = self.construct_collection()
with self.bind_induction_var(elt_type):
ind_var = self.induction_var.construct()
# "collection" contains "item" if at least one element in "collection"
# is equal to "item".
return QuantifierExpr(Quantifier.ANY, collection,
EqExpr(ind_var, self.item.construct()),
ind_var)
class Eq(AbstractExpression):
"""
Abstract expressinon for equality test expression.
"""
def __init__(self, lhs, rhs):
"""
:param AbstractExpression lhs: Left operand.
:param AbstractExpression rhs: Right operand.
"""
self.lhs = lhs
self.rhs = rhs
def construct(self):
"""
Construct a resolved expression for this.
:rtype: EqExpr
"""
lhs = self.lhs.construct()
rhs = self.rhs.construct()
if issubclass(compiled_types.ASTNode, lhs.type):
# Handle checks between two subclasses without explicit casts. In
# order to help users to detect dubious checks, forbid operands
# that can never be equal because they have no subclass in common.
if issubclass(lhs.type, rhs.type):
lhs = CastExpr(lhs, rhs.type)
elif issubclass(rhs.type, lhs.type):
rhs = CastExpr(rhs, lhs.type)
else:
assert False, '{} and {} values are never equal'.format(
lhs.type.name().camel, rhs.type.name().camel
)
else:
assert lhs.type == rhs.type, (
'Incompatible types for equality: {} and {}'
).format(lhs.type.name().camel, rhs.type.name().camel)
return EqExpr(lhs, rhs)
class If(AbstractExpression):
"""
Abstract expression for a conditional expression.
@@ -330,56 +466,6 @@ class IsA(AbstractExpression):
return IsAExpr(expr, self.astnode)
class CollectionExpression(AbstractExpression):
"""
Base class to provide common code for abstract expressions working on
collections.
"""
def __init__(self, expr, collection, induction_var=None):
"""
:param induction_var: Variable to use in "expr".
:type induction_var: None|InductionVariable
:param AbstractExpression expr: Expression to evaluate for each item in
"collection".
:param AbstractExpression collection: Collection on which this map
operation works.
"""
super(CollectionExpression, self).__init__()
self.expr = expr
self.collection = collection
self.induction_var = induction_var
def construct_collection(self):
"""
Construct the collection expression and determine the type of the
induction variable.
:rtype: (ResolvedExpression, langkit.compiled_types.CompiledType)
"""
collection_expr = self.collection.construct()
assert (collection_expr.type.is_list_type or
issubclass(collection_expr.type, compiled_types.ArrayType)), (
'Map cannot iterate on {}, which is not a collection'
).format(collection_expr.type)
return (collection_expr, collection_expr.type.element_type)
def bind_induction_var(self, type):
"""
Return a context manager to bind temporarily the induction variable to
a specific type. Also create the induction variable first if there is
none.
:param langkit.compiled_types.CompiledType type: Type for the induction
variable.
"""
default_induction_var = not self.induction_var
if default_induction_var:
self.induction_var = Vars.create_default()
return self.induction_var.vars.bind(self.induction_var, type)
class Map(CollectionExpression):
"""
Abstract expression that is the result of a map expression evaluation.
@@ -772,6 +858,29 @@ class CastExpr(ResolvedExpression):
)
class EqExpr(ResolvedExpression):
"""
Resolved expression for an equality test expression.
"""
def __init__(self, lhs, rhs):
self.lhs = lhs
self.rhs = rhs
@property
def type(self):
return compiled_types.BoolType
def render_pre(self):
return '{}\n{}'.format(
self.lhs.render_pre(),
self.rhs.render_pre()
)
def render_expr(self):
return '{} = {}'.format(self.lhs.render_expr(), self.rhs.render_expr())
class IfExpr(ResolvedExpression):
"""
Resolved expression for a conditional expression.