Add optional filtering expression to loop operator
This commit is contained in:
@@ -25,7 +25,7 @@ class Or(BinaryOperator):
|
||||
class Loop(BinaryOperator):
|
||||
def __init__(self, pos):
|
||||
super().__init__(pos)
|
||||
self.children.append(NoneNode())
|
||||
self.children.extend([NoneNode(), NoneNode()])
|
||||
|
||||
@property
|
||||
def parameters(self):
|
||||
@@ -35,13 +35,22 @@ class Loop(BinaryOperator):
|
||||
def parameters(self, value):
|
||||
self[3] = value
|
||||
|
||||
@property
|
||||
def filter(self):
|
||||
return self[4]
|
||||
|
||||
@filter.setter
|
||||
def filter(self, value):
|
||||
self[4] = value
|
||||
|
||||
@classmethod
|
||||
def loop(cls, left, parameters, operator, right):
|
||||
def loop(cls, left, parameters, operator, right, filter):
|
||||
node = cls(left.pos)
|
||||
node.left = left
|
||||
node.parameters = parameters
|
||||
node.operator = operator
|
||||
node.right = right
|
||||
node.filter = filter
|
||||
return node
|
||||
|
||||
|
||||
@@ -94,11 +103,19 @@ def LoopParser(input):
|
||||
name="loop parameters"
|
||||
)
|
||||
|
||||
loopFilter = Parser.allOf(
|
||||
Parser.terminal(TokenType.PERCENT),
|
||||
Parser.doAssert(ExpressionWithoutLoopParser, "filter as bool expression"),
|
||||
createNode=lambda percent, expr: expr,
|
||||
name="loop filter"
|
||||
)
|
||||
|
||||
return Parser.allOf(
|
||||
ExpressionWithoutLoopParser,
|
||||
Parser.optional(loopParameters),
|
||||
Parser.terminal(TokenType.DASH, createNode=Operator.withValue),
|
||||
StatementParser,
|
||||
Parser.optional(loopFilter),
|
||||
createNode=Loop.loop,
|
||||
name="dash-loop"
|
||||
)(input)
|
||||
|
||||
@@ -20,7 +20,7 @@ class LoopEvaluator(Evaluator):
|
||||
Type.BOOL: cls.boolEvaluator,
|
||||
Type.LIST: cls.listEvaluator,
|
||||
Type.MAP: cls.mapEvaluator
|
||||
}[iterator.type](node, environment, iterator, parameters)
|
||||
}[iterator.type](node, environment, iterator, parameters, node.filter)
|
||||
|
||||
environment.scopes.pop(-1)
|
||||
except KeyError:
|
||||
@@ -29,7 +29,7 @@ class LoopEvaluator(Evaluator):
|
||||
return Type.list(output)
|
||||
|
||||
@classmethod
|
||||
def numberEvaluator(cls, node, environment, evaluatedIterator, parameters):
|
||||
def numberEvaluator(cls, node, environment, evaluatedIterator, parameters, filter):
|
||||
output = []
|
||||
|
||||
|
||||
@@ -40,14 +40,28 @@ class LoopEvaluator(Evaluator):
|
||||
if len(parameters) > 0:
|
||||
environment.scopes[-1][parameters[0]] = Type.integer(i)
|
||||
|
||||
output.append(evaluate(node.right, environment).value)
|
||||
if cls.doFilter(filter, environment):
|
||||
output.append(evaluate(node.right, environment).value)
|
||||
|
||||
|
||||
|
||||
return output
|
||||
|
||||
@classmethod
|
||||
def boolEvaluator(cls, node, environment, evaluatedIterator, parameters):
|
||||
def doFilter(cls, filter, environment):
|
||||
if type(filter) is not NoneNode:
|
||||
evaluation = expressionEvaluator(doAssert=True)(filter, environment).value
|
||||
if evaluation.type != Type.BOOL:
|
||||
raise RuntimeException(f"Expected {Type.BOOL.name.lower()} as filter expression, found {evaluation.type.name.lower()}", filter.pos)
|
||||
|
||||
return evaluation.value
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
||||
@classmethod
|
||||
def boolEvaluator(cls, node, environment, evaluatedIterator, parameters, filter):
|
||||
output = []
|
||||
|
||||
if len(parameters) > 0:
|
||||
@@ -55,13 +69,14 @@ class LoopEvaluator(Evaluator):
|
||||
|
||||
condition = evaluatedIterator
|
||||
while condition.value:
|
||||
output.append(evaluate(node.right, environment).value)
|
||||
if cls.doFilter(filter, environment):
|
||||
output.append(evaluate(node.right, environment).value)
|
||||
condition = expressionEvaluator(doAssert=True)(node.left, environment).value
|
||||
|
||||
return output
|
||||
|
||||
@classmethod
|
||||
def listEvaluator(cls, node, environment, evaluatedIterator, parameters):
|
||||
def listEvaluator(cls, node, environment, evaluatedIterator, parameters, filter):
|
||||
output = []
|
||||
|
||||
if len(parameters) > 2:
|
||||
@@ -74,13 +89,14 @@ class LoopEvaluator(Evaluator):
|
||||
environment.scopes[-1][parameters[0]] = Type.integer(i)
|
||||
environment.scopes[-1][parameters[1]] = value
|
||||
|
||||
output.append(evaluate(node.right, environment).value)
|
||||
if cls.doFilter(filter, environment):
|
||||
output.append(evaluate(node.right, environment).value)
|
||||
|
||||
return output
|
||||
|
||||
|
||||
@classmethod
|
||||
def mapEvaluator(cls, node, environment, evaluatedIterator, parameters):
|
||||
def mapEvaluator(cls, node, environment, evaluatedIterator, parameters, filter):
|
||||
output = []
|
||||
|
||||
if len(parameters) > 3:
|
||||
@@ -99,6 +115,7 @@ class LoopEvaluator(Evaluator):
|
||||
environment.scopes[-1][parameters[2]] = value
|
||||
i += 1
|
||||
|
||||
output.append(evaluate(node.right, environment).value)
|
||||
if cls.doFilter(filter, environment):
|
||||
output.append(evaluate(node.right, environment).value)
|
||||
|
||||
return output
|
||||
|
||||
@@ -29,6 +29,7 @@ tokenizers = (
|
||||
defaultTokenizer(TokenType.CLOSE_ANGLE),
|
||||
defaultTokenizer(TokenType.SEMICOLON),
|
||||
defaultTokenizer(TokenType.ASTERISK),
|
||||
defaultTokenizer(TokenType.PERCENT),
|
||||
defaultTokenizer(TokenType.ASSIGN),
|
||||
defaultTokenizer(TokenType.COMMA),
|
||||
defaultTokenizer(TokenType.SLASH),
|
||||
|
||||
@@ -14,6 +14,7 @@ class TokenType(Enum):
|
||||
CLOSE_ANGLE = '>'
|
||||
SEMICOLON = ';'
|
||||
ASTERISK = '*'
|
||||
PERCENT = '%'
|
||||
ASSIGN = '='
|
||||
ARROW = '->'
|
||||
COMMA = ','
|
||||
@@ -44,7 +45,6 @@ class TokenType(Enum):
|
||||
AS = 'as'
|
||||
IDENTIFIER = 'identifier'
|
||||
COMMENT = 'comment'
|
||||
PERCENT = 'percent'
|
||||
|
||||
@property
|
||||
def key(self):
|
||||
|
||||
Reference in New Issue
Block a user