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