csvpath 0.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. csvpath/__init__.py +1 -0
  2. csvpath/csvpath.py +368 -0
  3. csvpath/matching/__init__.py +1 -0
  4. csvpath/matching/expression_encoder.py +108 -0
  5. csvpath/matching/expression_math.py +123 -0
  6. csvpath/matching/expression_utility.py +29 -0
  7. csvpath/matching/functions/above.py +36 -0
  8. csvpath/matching/functions/add.py +24 -0
  9. csvpath/matching/functions/below.py +36 -0
  10. csvpath/matching/functions/concat.py +25 -0
  11. csvpath/matching/functions/count.py +44 -0
  12. csvpath/matching/functions/count_lines.py +12 -0
  13. csvpath/matching/functions/count_scans.py +13 -0
  14. csvpath/matching/functions/divide.py +30 -0
  15. csvpath/matching/functions/end.py +18 -0
  16. csvpath/matching/functions/every.py +33 -0
  17. csvpath/matching/functions/first.py +46 -0
  18. csvpath/matching/functions/function.py +31 -0
  19. csvpath/matching/functions/function_factory.py +114 -0
  20. csvpath/matching/functions/inf.py +38 -0
  21. csvpath/matching/functions/is_instance.py +95 -0
  22. csvpath/matching/functions/length.py +33 -0
  23. csvpath/matching/functions/lower.py +21 -0
  24. csvpath/matching/functions/minf.py +167 -0
  25. csvpath/matching/functions/multiply.py +27 -0
  26. csvpath/matching/functions/no.py +10 -0
  27. csvpath/matching/functions/notf.py +26 -0
  28. csvpath/matching/functions/now.py +33 -0
  29. csvpath/matching/functions/orf.py +28 -0
  30. csvpath/matching/functions/percent.py +29 -0
  31. csvpath/matching/functions/random.py +33 -0
  32. csvpath/matching/functions/regex.py +38 -0
  33. csvpath/matching/functions/subtract.py +28 -0
  34. csvpath/matching/functions/tally.py +36 -0
  35. csvpath/matching/functions/upper.py +21 -0
  36. csvpath/matching/matcher.py +215 -0
  37. csvpath/matching/matching_lexer.py +66 -0
  38. csvpath/matching/parser.out +1287 -0
  39. csvpath/matching/parsetab.py +1427 -0
  40. csvpath/matching/productions/equality.py +158 -0
  41. csvpath/matching/productions/expression.py +16 -0
  42. csvpath/matching/productions/header.py +30 -0
  43. csvpath/matching/productions/matchable.py +41 -0
  44. csvpath/matching/productions/term.py +11 -0
  45. csvpath/matching/productions/variable.py +15 -0
  46. csvpath/parser_utility.py +39 -0
  47. csvpath/scanning/__init__.py +1 -0
  48. csvpath/scanning/parser.out +1 -0
  49. csvpath/scanning/parsetab.py +231 -0
  50. csvpath/scanning/scanner.py +165 -0
  51. csvpath/scanning/scanning_lexer.py +47 -0
  52. csvpath-0.0.2.dist-info/METADATA +184 -0
  53. csvpath-0.0.2.dist-info/RECORD +54 -0
  54. csvpath-0.0.2.dist-info/WHEEL +4 -0
@@ -0,0 +1,36 @@
1
+ from typing import Any
2
+ from csvpath.matching.functions.function import Function, ChildrenException
3
+
4
+
5
+ class Below(Function):
6
+ def to_value(self, *, skip=[]) -> Any:
7
+ if self in skip:
8
+ return True
9
+ if len(self.children) != 1:
10
+ self.matcher.print(
11
+ f"In.to_value: must have 1 equality child: {self.children}"
12
+ )
13
+ raise ChildrenException("Below function must have 1 child")
14
+ if self.children[0].op != ",":
15
+ raise ChildrenException(
16
+ f"Below function must have an equality with the ',' operation, not {self.children[0].op}"
17
+ )
18
+ thischild = self.children[0].children[0]
19
+ belowthatchild = self.children[0].children[1]
20
+
21
+ this_is = thischild.to_value(skip=skip)
22
+ below_that = belowthatchild.to_value(skip=skip)
23
+ this = -1
24
+ that = -1
25
+ try:
26
+ this = float(this_is)
27
+ that = float(below_that)
28
+ except Exception:
29
+ raise Exception(
30
+ f"Below.to_value: this: {this}, a {this.__class__}, and {that}, a {that.__class__}"
31
+ )
32
+ b = this < that
33
+ return b
34
+
35
+ def matches(self, *, skip=[]) -> bool:
36
+ return self.to_value(skip=skip)
@@ -0,0 +1,25 @@
1
+ from typing import Any
2
+ from csvpath.matching.functions.function import Function, ChildrenException
3
+
4
+
5
+ class Concat(Function):
6
+ def to_value(self, *, skip=[]) -> Any:
7
+ if self in skip:
8
+ return True
9
+ if len(self.children) != 1:
10
+ self.matcher.print(
11
+ f"In.to_value: must have 1 equality child: {self.children}"
12
+ )
13
+ raise ChildrenException("In function must have 1 child")
14
+ if self.children[0].op != ",":
15
+ raise ChildrenException(
16
+ f"In function must have an equality with the ',' operation, not {self.children[0].op}"
17
+ )
18
+ left = self.children[0].children[0]
19
+ right = self.children[0].children[1]
20
+ value = f"{left.to_value(skip=skip)}{right.to_value(skip=skip)}"
21
+ return value
22
+
23
+ def matches(self, *, skip=[]) -> bool:
24
+ v = self.to_value(skip=skip)
25
+ return v is not None
@@ -0,0 +1,44 @@
1
+ from typing import Any
2
+ from csvpath.matching.functions.function import Function
3
+
4
+
5
+ class Count(Function):
6
+ def print(self, msg):
7
+ if self.matcher:
8
+ self.matcher.print(msg)
9
+
10
+ def to_value(self, *, skip=[]) -> Any:
11
+ if self in skip:
12
+ return True
13
+ if self.value is None:
14
+ if self._function_or_equality:
15
+ self.value = self._get_contained_value(skip=skip)
16
+ else:
17
+ self.value = (
18
+ self._get_match_count() + 1
19
+ ) # we're eager to +1 because we don't
20
+ # contribute to if there's a match
21
+ return self.value # or not. we have to act as if.
22
+
23
+ def _get_match_count(self) -> int:
24
+ if not self.matcher or not self.matcher.csvpath:
25
+ print("WARNING: no csvpath. are we testing?")
26
+ return -1
27
+ return self.matcher.csvpath.current_match_count()
28
+
29
+ def _get_contained_value(self, *, skip=[]) -> Any:
30
+ #
31
+ # need to apply this count function to the contained obj's value
32
+ #
33
+ b = self._function_or_equality.matches(skip=skip)
34
+ self._id = self.get_id(self._function_or_equality)
35
+ #
36
+ # to_value() is often going to be a bool based on matches().
37
+ # but in a case like: count(now('yyyy-mm-dd')) it would not be
38
+ #
39
+ tracked_value = self._function_or_equality.to_value(skip=skip)
40
+ cnt = self.matcher.get_variable(self._id, tracking=tracked_value, set_if_none=0)
41
+ if b:
42
+ cnt += 1
43
+ self.matcher.set_variable(self._id, tracking=tracked_value, value=cnt)
44
+ return cnt
@@ -0,0 +1,12 @@
1
+ from typing import Any
2
+ from csvpath.matching.functions.function import Function
3
+
4
+
5
+ class CountLines(Function):
6
+ def print(self, msg):
7
+ if self.matcher:
8
+ self.matcher.print(msg)
9
+
10
+ def to_value(self, *, skip=[]) -> Any:
11
+ if self.matcher:
12
+ return self.matcher.csvpath.current_line_number()
@@ -0,0 +1,13 @@
1
+ from typing import Any
2
+ from csvpath.matching.functions.function import Function
3
+
4
+
5
+ class CountScans(Function):
6
+ def print(self, msg):
7
+ if self.matcher:
8
+ self.matcher.print(msg)
9
+
10
+ def to_value(self, *, skip=[]) -> Any:
11
+ if self in skip:
12
+ return True
13
+ return self.matcher.csvpath.current_scan_count()
@@ -0,0 +1,30 @@
1
+ from typing import Any
2
+ from csvpath.matching.functions.function import Function, ChildrenException
3
+ from csvpath.matching.productions.equality import Equality
4
+
5
+
6
+ class Divide(Function):
7
+ def to_value(self, *, skip=[]) -> Any:
8
+ if not self.value:
9
+ if len(self.children) != 1:
10
+ raise ChildrenException("no children. there must be 1 equality child")
11
+ child = self.children[0]
12
+ if not isinstance(child, Equality):
13
+ raise ChildrenException("must be 1 equality child")
14
+
15
+ siblings = child.commas_to_list()
16
+ ret = 0
17
+ for i, sib in enumerate(siblings):
18
+ v = sib.to_value(skip=skip)
19
+ if i == 0:
20
+ ret = v
21
+ else:
22
+ if ret == float("nan") or v == 0:
23
+ ret = float("nan")
24
+ else:
25
+ ret = ret / v
26
+ self.value = ret
27
+ return self.value
28
+
29
+ def matches(self, *, skip=[]) -> bool:
30
+ return True
@@ -0,0 +1,18 @@
1
+ from typing import Any
2
+ from csvpath.matching.functions.function import Function, ChildrenException
3
+
4
+
5
+ class End(Function):
6
+ def to_value(self, *, skip=[]) -> Any:
7
+ if self in skip:
8
+ return True
9
+ if self.children and len(self.children) > 0:
10
+ ChildrenException("end must not have a child")
11
+ if not self.value:
12
+ i = self.matcher.last_header_index()
13
+ if i:
14
+ self.value = self.matcher.line[i]
15
+ return self.value
16
+
17
+ def matches(self, *, skip=[]) -> bool:
18
+ return self.to_value(skip=skip) is not None
@@ -0,0 +1,33 @@
1
+ from typing import Any
2
+ from csvpath.matching.functions.function import Function, ChildrenException
3
+ from csvpath.matching.productions.equality import Equality
4
+
5
+
6
+ class Every(Function):
7
+ def to_value(self, *, skip=[]) -> Any:
8
+ return self.matches(skip=skip)
9
+
10
+ def matches(self, *, skip=[]) -> bool:
11
+ if self.value is None:
12
+ if len(self.children) != 1:
13
+ raise ChildrenException("no children. there must be 1 equality child")
14
+ child = self.children[0]
15
+ if not isinstance(child, Equality):
16
+ raise ChildrenException("must be 1 equality child")
17
+
18
+ tracked_value = self.children[0].left.matches(skip=skip)
19
+ if tracked_value:
20
+ every = self.children[0].right.to_value()
21
+ self._id = self.get_id(self)
22
+ cnt = self.matcher.get_variable(
23
+ self._id, tracking=tracked_value, set_if_none=0
24
+ )
25
+ cnt += 1
26
+ self.matcher.set_variable(self._id, tracking=tracked_value, value=cnt)
27
+ if cnt % every == 0:
28
+ self.value = True
29
+ else:
30
+ self.value = False
31
+ else:
32
+ self.value = False
33
+ return self.value
@@ -0,0 +1,46 @@
1
+ from typing import Any
2
+ from csvpath.matching.functions.function import Function, ChildrenException
3
+ from csvpath.matching.productions.equality import Equality
4
+
5
+
6
+ class First(Function):
7
+ def __init__(self, matcher, name: str = None, child: Any = None):
8
+ super().__init__(matcher, child=child, name=name)
9
+ self._my_value_or_none = -9999 # when this var is None we match
10
+
11
+ def to_value(self, *, skip=[]) -> Any:
12
+ if len(self.children) != 1:
13
+ self.matcher.print(f"First.to_value: must have 1 child: {self.children}")
14
+ raise ChildrenException("First function must have 1 child")
15
+ if self._my_value_or_none == -9999:
16
+
17
+ child = self.children[0]
18
+ value = ""
19
+ if isinstance(child, Equality):
20
+ for _ in child.commas_to_list():
21
+ value += f"{_.to_value(skip=skip)}"
22
+ else:
23
+ value = f"{child.to_value(skip=skip)}"
24
+
25
+ my_id = self.get_id()
26
+ v = self.matcher.get_variable(my_id, tracking=value)
27
+ if v is None:
28
+ self.matcher.set_variable(
29
+ my_id,
30
+ tracking=value,
31
+ value=self.matcher.csvpath.current_line_number(),
32
+ )
33
+ #
34
+ # when we have no earlier value we are first, so we match
35
+ #
36
+ self._my_value_or_none = v
37
+ return self._my_value_or_none
38
+
39
+ def matches(self, *, skip=[]) -> bool:
40
+ #
41
+ # when there is no earlier value we match
42
+ #
43
+ if self._my_value_or_none == -9999:
44
+ self.to_value(skip=skip)
45
+ v = self._my_value_or_none
46
+ return v is None
@@ -0,0 +1,31 @@
1
+ from typing import Any
2
+ from csvpath.matching.productions.matchable import Matchable
3
+
4
+
5
+ class NoChildrenException(Exception):
6
+ pass
7
+
8
+
9
+ class ChildrenException(Exception):
10
+ pass
11
+
12
+
13
+ class Function(Matchable):
14
+ def __init__(self, matcher: Any, name: str, child: Matchable = None) -> None:
15
+ super().__init__(matcher, name=name)
16
+ self.matcher = matcher # atm, circular dep
17
+ self._function_or_equality = child
18
+ if child:
19
+ self.add_child(child)
20
+
21
+ def __str__(self) -> str:
22
+ return f"""\n{self.__class__}{self.name}({self._function_or_equality})"""
23
+
24
+ def to_value(self, *, skip=[]) -> bool:
25
+ if self in skip:
26
+ return True
27
+ if self._function_or_equality:
28
+ if not self._function_or_equality.matches(skip=skip):
29
+ return False
30
+ print("WARNING: function getting to_value defaulting to True")
31
+ return True # leave this for now for testing
@@ -0,0 +1,114 @@
1
+ from csvpath.matching.productions.expression import Matchable
2
+ from csvpath.matching.functions.function import Function
3
+ from csvpath.matching.functions.count import Count
4
+ from csvpath.matching.functions.regex import Regex
5
+ from csvpath.matching.functions.length import Length
6
+ from csvpath.matching.functions.notf import Not
7
+ from csvpath.matching.functions.now import Now
8
+ from csvpath.matching.functions.inf import In
9
+ from csvpath.matching.functions.concat import Concat
10
+ from csvpath.matching.functions.lower import Lower
11
+ from csvpath.matching.functions.upper import Upper
12
+ from csvpath.matching.functions.percent import Percent
13
+ from csvpath.matching.functions.below import Below
14
+ from csvpath.matching.functions.above import Above
15
+ from csvpath.matching.functions.first import First
16
+ from csvpath.matching.functions.count_lines import CountLines
17
+ from csvpath.matching.functions.count_scans import CountScans
18
+ from csvpath.matching.functions.is_instance import IsInstance
19
+ from csvpath.matching.functions.orf import Or
20
+ from csvpath.matching.functions.no import No
21
+ from csvpath.matching.functions.minf import Min, Max, Average
22
+ from csvpath.matching.functions.end import End
23
+ from csvpath.matching.functions.random import Random
24
+ from csvpath.matching.functions.add import Add
25
+ from csvpath.matching.functions.subtract import Subtract
26
+ from csvpath.matching.functions.multiply import Multiply
27
+ from csvpath.matching.functions.divide import Divide
28
+ from csvpath.matching.functions.tally import Tally
29
+ from csvpath.matching.functions.every import Every
30
+
31
+
32
+ class UnknownFunctionException(Exception):
33
+ pass
34
+
35
+
36
+ class InvalidChildException(Exception):
37
+ pass
38
+
39
+
40
+ class FunctionFactory:
41
+ @classmethod
42
+ def get_function( # noqa: C901
43
+ cls, matcher, *, name: str, child: Matchable = None
44
+ ) -> Function:
45
+ if child and not isinstance(child, Matchable):
46
+ raise InvalidChildException(f"{child} is not a valid child")
47
+ f = None
48
+ if name == "count":
49
+ f = Count(matcher, name, child)
50
+ elif name == "length":
51
+ f = Length(matcher, name, child)
52
+ elif name == "regex":
53
+ f = Regex(matcher, name, child)
54
+ elif name == "not":
55
+ f = Not(matcher, name, child)
56
+ elif name == "now":
57
+ f = Now(matcher, name, child)
58
+ elif name == "in":
59
+ f = In(matcher, name, child)
60
+ elif name == "concat":
61
+ f = Concat(matcher, name, child)
62
+ elif name == "lower":
63
+ f = Lower(matcher, name, child)
64
+ elif name == "upper":
65
+ f = Upper(matcher, name, child)
66
+ elif name == "percent":
67
+ f = Percent(matcher, name, child)
68
+ elif name == "below":
69
+ f = Below(matcher, name, child)
70
+ elif name == "above":
71
+ f = Above(matcher, name, child)
72
+ elif name == "first":
73
+ f = First(matcher, name, child)
74
+ elif name == "count_lines":
75
+ f = CountLines(matcher, name, child)
76
+ elif name == "count_scans":
77
+ f = CountScans(matcher, name, child)
78
+ elif name == "isinstance":
79
+ f = IsInstance(matcher, name, child)
80
+ elif name == "or":
81
+ f = Or(matcher, name, child)
82
+ elif name == "no":
83
+ f = No(matcher, name, child)
84
+ elif name == "max":
85
+ f = Max(matcher, name, child)
86
+ elif name == "min":
87
+ f = Min(matcher, name, child)
88
+ elif name == "average":
89
+ f = Average(matcher, name, child, "average")
90
+ elif name == "median":
91
+ f = Average(matcher, name, child, "median")
92
+ elif name == "random":
93
+ f = Random(matcher, name, child)
94
+ elif name == "end":
95
+ f = End(matcher, name, child)
96
+ elif name == "length":
97
+ f = Length(matcher, name, child)
98
+ elif name == "add":
99
+ f = Add(matcher, name, child)
100
+ elif name == "subtract":
101
+ f = Subtract(matcher, name, child)
102
+ elif name == "multiply":
103
+ f = Multiply(matcher, name, child)
104
+ elif name == "divide":
105
+ f = Divide(matcher, name, child)
106
+ elif name == "tally":
107
+ f = Tally(matcher, name, child)
108
+ elif name == "every":
109
+ f = Every(matcher, name, child)
110
+ else:
111
+ raise UnknownFunctionException(f"{name}")
112
+ if child:
113
+ child.parent = f
114
+ return f
@@ -0,0 +1,38 @@
1
+ from typing import Any
2
+ from csvpath.matching.functions.function import Function, ChildrenException
3
+
4
+
5
+ class In(Function):
6
+ def to_value(self, *, skip=[]) -> Any:
7
+ if self in skip:
8
+ return True
9
+ if len(self.children) != 1:
10
+ self.matcher.print(
11
+ f"In.to_value: must have 1 equality child: {self.children}"
12
+ )
13
+ raise ChildrenException("In function must have 1 child")
14
+ if self.children[0].op != ",":
15
+ raise ChildrenException(
16
+ f"In function must have an equality with the ',' operation, not {self.children[0].op}"
17
+ )
18
+ vchild = self.children[0].children[0]
19
+ lchild = self.children[0].children[1]
20
+
21
+ mylist = []
22
+ liststr = lchild.to_value(skip=skip)
23
+ # print(f"In.to_value: list str: {liststr}")
24
+ mylist = liststr.split("|")
25
+ # print(f"In.to_value: child: {vchild}, a {vchild.__class__}")
26
+ v = vchild.to_value()
27
+ # print(f"In.to_value: self.matcher.csvpath.headers: {self.matcher.csvpath.headers}")
28
+
29
+ # print(f"In.to_value: list: {mylist}, value: {v}")
30
+ if v in mylist:
31
+ return True
32
+ elif v.__class__ != str and f"{v}" in mylist:
33
+ return True
34
+ else:
35
+ return False
36
+
37
+ def matches(self, *, skip=[]) -> bool:
38
+ return self.to_value(skip=skip)
@@ -0,0 +1,95 @@
1
+ from typing import Any
2
+ from csvpath.matching.functions.function import Function, ChildrenException
3
+ from csvpath.matching.productions.equality import Equality
4
+ from dateutil import parser
5
+ from numbers import Number
6
+
7
+
8
+ class IsInstance(Function):
9
+ def to_value(self, *, skip=[]) -> Any:
10
+ return self.matches(skip=skip)
11
+
12
+ def matches(self, *, skip=[]) -> bool:
13
+ if self.value is not None:
14
+ return self.value
15
+ elif self in skip:
16
+ return True
17
+ if len(self.children) != 1:
18
+ raise ChildrenException(
19
+ "no children. there must be 1 equality child with 2 non-equality children"
20
+ )
21
+ child = self.children[0]
22
+ if not isinstance(child, Equality):
23
+ raise ChildrenException(
24
+ "must be 1 equality child with 2 non-equality children"
25
+ )
26
+ left = child.left
27
+ right = child.right
28
+ thevalue = left.to_value(skip=skip)
29
+ thetype = right.to_value(skip=skip)
30
+ self.value = self._get_match_value(thetype, thevalue)
31
+ return self.value
32
+
33
+ def _get_match_value(self, thetype, thevalue) -> bool:
34
+ ret = False
35
+ if thetype == "int":
36
+ try:
37
+ v = int(thevalue)
38
+ ret = f"{v}" == f"{thevalue}"
39
+ except Exception:
40
+ pass
41
+ elif thetype == "float":
42
+ try:
43
+ v = float(thevalue)
44
+ ret = f"{v}" == f"{thevalue}"
45
+ except Exception:
46
+ pass
47
+ elif thetype == "str":
48
+ v = str(thevalue)
49
+ ret = f"{v}" == f"{thevalue}"
50
+ elif thetype == "complex":
51
+ try:
52
+ v = complex(thevalue)
53
+ ret = f"{v}" == f"{thevalue}"
54
+ except Exception:
55
+ pass
56
+ elif thetype == "bool":
57
+ v = isinstance(thevalue, bool)
58
+ if not v:
59
+ if thevalue == 1 or thevalue == 0:
60
+ v = True
61
+ if thevalue == "None":
62
+ v = True
63
+ ret = v
64
+ elif thetype == "usd":
65
+ ret = self.to_usd(thevalue)
66
+ elif thetype == "datetime":
67
+ ret = self.to_datetime(thevalue)
68
+ else:
69
+ raise Exception(
70
+ f"""the type must one of:
71
+ "int","float","str","bool",
72
+ "complex","usd","datetime, not {thetype}"
73
+ """
74
+ )
75
+ return ret
76
+
77
+ def to_usd(self, v) -> bool:
78
+ if isinstance(v, Number):
79
+ return False
80
+ try:
81
+ float(f"{v}".replace("$", "").replace(",", ""))
82
+ except Exception:
83
+ return False
84
+ v = v.strip().lower()
85
+ if v[0] == "$":
86
+ return True
87
+ return False
88
+
89
+ def to_datetime(self, v) -> bool:
90
+ try:
91
+ parser.parse(f"{v}")
92
+ except Exception as e:
93
+ print(e)
94
+ return False
95
+ return True
@@ -0,0 +1,33 @@
1
+ from typing import Any
2
+ from csvpath.matching.functions.function import (
3
+ Function,
4
+ NoChildrenException,
5
+ ChildrenException,
6
+ )
7
+
8
+
9
+ class Length(Function):
10
+ def to_value(self, *, skip=[]) -> Any:
11
+ if self in skip:
12
+ return True
13
+ if not self.children:
14
+ NoChildrenException(
15
+ "length function must have a child that produces a value"
16
+ )
17
+ if not len(self.children) == 1:
18
+ self.matcher.print(
19
+ f"Length.to_value: should be 1 children: {self.children}"
20
+ )
21
+ ChildrenException(
22
+ "length function must have a single child that produces a value"
23
+ )
24
+ val = self.children[0].to_value(skip=skip)
25
+ self.matcher.print(f"Length.to_value: val: {val}")
26
+ ret = 0
27
+ if val:
28
+ ret = len(f"{val}")
29
+ self.matcher.print(f"Length.to_value: val: {val}")
30
+ return ret
31
+
32
+ def matches(self, *, skip=[]) -> bool:
33
+ return self.to_value(skip=skip) > 0
@@ -0,0 +1,21 @@
1
+ from typing import Any
2
+ from csvpath.matching.functions.function import Function, ChildrenException
3
+
4
+
5
+ class Lower(Function):
6
+ def to_value(self, *, skip=[]) -> Any:
7
+ if self in skip:
8
+ return True
9
+ if len(self.children) != 1:
10
+ self.matcher.print(
11
+ f"Lower.to_value: must have 1 equality child: {self.children}"
12
+ )
13
+ raise ChildrenException("Lower function must have 1 child")
14
+
15
+ value = self.children[0].to_value(skip=skip)
16
+ value = f"{value}".lower()
17
+ return value
18
+
19
+ def matches(self, *, skip=[]) -> bool:
20
+ v = self.to_value(skip=skip)
21
+ return v is not None