Add optional filtering expression to loop operator

This commit is contained in:
Bartłomiej Pluta
2019-07-15 20:43:33 +02:00
parent 83c7b92741
commit 44e63ed18d
4 changed files with 47 additions and 12 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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),

View File

@@ -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):