CHAR_COMMENT = '#'
CHAR_DESIGN_ALT = '='
CHAR_ATTR_EQ = '='
+CHAR_ATTR_LESS = '<'
+CHAR_ATTR_MORE = '>'
CHAR_TEXTCONTAINS = ':'
SEP_DESIGN_DESC = '#'
CHAR_COLL_INDENT = ' '
ARG_QMARK = '?'
PARAM_Q_ID = f'id{CHAR_ATTR_EQ}'
-PARAM_Q_DELIMITERS = {CHAR_ATTR_EQ, CHAR_TEXTCONTAINS}
+MATCH_OPERATORS = {
+ CHAR_ATTR_EQ,
+ CHAR_TEXTCONTAINS,
+ CHAR_ATTR_LESS,
+ CHAR_ATTR_MORE
+ }
TOK_SORT_BOX = 'box'
TOK_SORT_ID = 'id'
@classmethod
def matchers(
cls
- ) -> dict[str, tuple[str, Callable[[Self, str], bool]]]:
+ ) -> dict[str, tuple[str, dict[str, Callable[[Self, str], bool]]]]:
'Available lookup matchers.'
return {
- PARAM_Q_ID: (
- 'by ID of individual item',
- lambda item, q_body: item.id_ == q_body),
- f'text{CHAR_TEXTCONTAINS}': (
+ TOK_SORT_ID: (
+ 'ID of individual item',
+ {CHAR_ATTR_EQ: (
+ lambda item, val: item.id_ == val)}
+ ),
+ 'text': (
'text contained in item description',
- lambda item, q_body: q_body.upper() in item.show().upper())
+ {CHAR_TEXTCONTAINS: (
+ lambda item, val: val.upper() in item.show().upper())}
+ ),
}
def match(
self,
- query_char: str,
- query_body: str
+ target: str,
+ operator: str,
+ val: str
) -> bool:
'Return if self matches query.'
- return self.matchers()[query_char][1](self, query_body)
+ target_matchers = self.matchers()[target][1]
+ assert operator in target_matchers, (target, operator)
+ return target_matchers[operator](self, val)
@classmethod
def _by_box_sorter(
@classmethod
def matchers(
cls
- ) -> dict[str, tuple[str, Callable[[Self, str], bool]]]:
+ ) -> dict[str, tuple[str, dict[str, Callable[[Self, str], bool]]]]:
def attr_matcher(
attr_name: str
- ) -> tuple[str, Callable[[Self, str], bool]]:
- def match(
+ ) -> tuple[str, dict[str, Callable[[Self, str], bool]]]:
+ def cmp(
item: Self,
q_body: str
- ) -> bool:
+ ) -> float:
assert q_body.isdigit()
- return getattr(item, attr_name) == int(q_body)
- return 'by ' + BrickDesignData.usage(attr_name), match
+ return int(q_body) - float(getattr(item, attr_name))
+ return 'by ' + BrickDesignData.usage(attr_name), {
+ CHAR_ATTR_EQ: lambda item, v: 0 == cmp(item, v),
+ CHAR_ATTR_LESS: lambda item, v: 0 < cmp(item, v),
+ CHAR_ATTR_MORE: lambda item, v: 0 > cmp(item, v)}
return super().matchers() | {
- f'{attr_name}{CHAR_ATTR_EQ}': attr_matcher(attr_name)
+ f'{attr_name}': attr_matcher(attr_name)
for attr_name, attr_type in BrickDesignData.sortables().items()
if attr_type in {int, float}}
) -> str:
'Return formatted result of inquiry on table of table_name.'
assert table_name in self.lookupables
- match_keys_vals = []
+ matchers = []
for instruction in match_by:
- match_key = match_val = ''
+ match_key = match_val = match_operator = ''
for idx, c in enumerate(instruction):
- match_key += c
- if c in PARAM_Q_DELIMITERS:
+ if c in MATCH_OPERATORS:
+ match_operator = c
match_val = instruction[idx+1:]
break
- match_keys_vals += [(match_key, match_val)]
+ match_key += c
+ matchers += [(match_key, match_operator, match_val)]
cls = self.lookupables[table_name]
for arg_keys, x_ers in [
- (arg, m()) for arg, m in ((tuple(t[0] for t in match_keys_vals),
+ (arg, m()) for arg, m in ((tuple(t[0] for t in matchers),
cls.matchers),
(sort_by, cls.sorters),
((formatter, ), cls.formatters))]:
for arg_key in arg_keys:
assert (not arg_key) or arg_key in keys, (arg_key, keys)
if (items := tuple(getattr(self, table_name).values())):
- for match_key, match_val in match_keys_vals:
- items = tuple(x for x in items
- if x.match(match_key, match_val))
+ for key, operator, val in matchers:
+ items = tuple(x for x in items if x.match(key, operator, val))
for sorter in sort_by:
items = cls.sorters()[sorter][1](self, items)
return CHAR_NEWLINE.join([cls.formatters()[formatter][1](item)