def parse_evaluation_dict(eval_dict: Dict[str, str], results: dict) -> None:
    """Parse the dictionary of evaluation expressions, given that some of them
    may contain references to each other. Each evaluated value will be stored
    under its corresponding key in the results dict.

    :raises StringParserError: When there's an error parsing or resolving
        one of the expressions. The error will already contain key information.
    :raises ValueError: When there's a reference loop.
    """

    parser = get_expr_parser()
    transformer = EvaluationExprTransformer(results)
    var_ref_visitor = VarRefVisitor()

    unresolved = {}

    for key, expr in eval_dict.items():
        try:
            tree = parser.parse(expr)
        except (_lark.UnexpectedCharacters, _lark.UnexpectedToken) as err:
            # Try to figure out why the error happened based on examples.
            err_type = match_examples(err, parser.parse, BAD_EXAMPLES, expr)
            raise StringParserError(
                "Error evaluating expression '{}' for key '{}':\n{}".format(
                    expr, key, err_type), err.get_context(expr))

        var_refs = var_ref_visitor.visit(tree)

        unresolved[key] = (tree, var_refs, expr)

    while unresolved:
        resolved = []
        for key, (tree, var_refs, expr) in unresolved.items():
            for var in var_refs:
                if var in unresolved:
                    break
            else:
                try:
                    results[key] = transformer.transform(tree)
                except ParserValueError as err:
                    # Any value errors should be converted to this error type.
                    raise StringParserError(err.args[0], err.get_context(expr))
                resolved.append(key)

        if not resolved:
            # Pass up the unresolved
            raise ValueError("Reference loops found amongst evaluation keys "
                             "{}.".format(tuple(unresolved.keys())))

        for key in resolved:
            del unresolved[key]

    return
Beispiel #2
0
    def test_visitors(self):
        """Test the visitors used to collect all of the used variables."""

        expr_parser = parsers.get_expr_parser()

        # These visitors walk the parse tree and return the used
        # variables as a list (with unique items).
        expr = 'int1.3.foo + var.int2.*.bleh * 11 * - sum([int1, int1])'
        tree = expr_parser.parse(expr)
        visitor = parsers.common.VarRefVisitor()
        used_vars = visitor.visit(tree)

        self.assertEqual(used_vars, ['int1.3.foo', 'var.int2.*.bleh', 'int1'])

        # Pretty much the same as above, but for a whole string an not just
        # an expression (it uses the above visitor for the expressions).
        string_parser = parsers.get_string_parser()
        tree = string_parser.parse("hello {{var.foo.1.baz:1s}} world "
                                   "{{sys.bar}}")
        visitor = parsers.strings.StringVarRefVisitor()
        var_list = visitor.visit(tree)
        self.assertEqual(['var.foo.1.baz', 'sys.bar'], var_list)