def tmatch() -> TypeMatcher: "A fixture for type matches." matcher = TypeMatcher({ int: (lambda x: x + 4), str: (lambda s: f"Hello, {s}!"), (int, str): (lambda amount, chr_: amount * chr_), (int, Any): (lambda amount, x: [x for n in range(amount)]), (int, int, int): lambda a,b,c: [matcher(a), matcher(b), matcher(c)], ASupertype: lambda x: f"super: {x.__class__.__name__}", ASubtype: lambda x: f"spec: subtype" }) # yapf: disable @matcher.case def its_a_float(x: float): return x - 1.1 @matcher.case def superspecial_float_substraction(x: float, y: float, present: str): return present.format(x + y) @matcher.case def only_kwargs(**kwargs: Any): return kwargs['x'] + 3 @matcher.case def sum_variadic_only(a: int, b: int, *c: int): return sum(c) @matcher.case def mix(a: str, b: str, *pos: Any, **kw: Any): return sum(pos) + sum(kw.values()) return matcher
def test_vmatch_missed_decl() -> None: "Should handle unmatched calls if ´Miss` is defined when initialized" match = TypeMatcher({Miss: lambda *args, **kwargs: 'default'}) assert match('lksflkjl', x=1) == 'default'
from kingston.match import (matches, match, move, Matcher, TypeMatcher, ValueMatcher, Miss, Mismatch, Conflict) from kingston.testing import between, diff_ints, same pytestmark = pytest.mark.wbox @fixture.params( "value, pattern, expected", (1, 1, True), ('x', 1, False), (1.1, 1, False), (object(), 1, False), (TypeMatcher(), 1, False), ((1,2,3), (1,2,3), True), ) # yapf: disable def test_match(value, pattern, expected) -> None: "Should match_hit" assert match(value, pattern) == expected @fixture.params( "value, pattern, expected", (int, int, True), (int, float, False), # (1.0, 5.0, True), ((int, ), tuple, True), ((float, ), list, False), # (int, tuple, False),
def empty_matcher(): return TypeMatcher()
Example of a stupid but hopefully thought-provoking expression builder/evaluator. """ from kingston.match import Matcher, TypeMatcher, ValueMatcher from typing import Any stupid: Matcher[Any, int] = TypeMatcher({ int: lambda x: x, str: lambda x: 99999, (Any, str, Any): ValueMatcher({ (Any, '+', Any): lambda a, op, b: a + b, (Any, '-', Any): lambda a, op, b: a - b, (Any, '*', Any): lambda a, op, b: a * b, (Any, '/', Any): lambda a, op, b: a / b, }), (int, int, ...): lambda begin, end, *seq: seq[begin:end], (int, ...): lambda n, *seq: seq[0:n] # (Callable, ...): ... # Dr McCarthy, I presume? ;-) -- not supported (yet?) }) if __name__ == '__main__': print(f"Single int -> just an identity op: {stupid(1)} (should be 1)") print( f"A string -> just an arbitrary recognizable value: {stupid('hello')} (should be 9999)" ) print(f"Addition: stupid(1, '+', 1) == {stupid(1, '+', 1)} (should be 2)")
# yapf import ast from kingston.match import Matcher, TypeMatcher # type: ignore[attr-defined] from kingston.decl import box from typing import Any, Collection, List, Dict from types import CodeType, FunctionType, ModuleType import traceback from functools import singledispatch from ormsnack import make, mappings compiler: Matcher[ast.AST, CodeType] = TypeMatcher() fix = ast.fix_missing_locations def autoname(node: ast.AST, idx: int) -> str: return f"{node.__class__.__name__}_{idx}" @compiler.case def compile_module(module: ast.Module) -> CodeType: back, *_ = traceback.extract_stack() if any(mappings.countnames(node) == 0 for node in module.body): for idx, node in enumerate(module.body): if mappings.countnames(node) == 0: name = autoname(node, idx) assigned = make.make(name, node) module.body[idx] = fix(assigned)
N = _node_factory def module(nodes: Collection, type_ignores: List = [], lineno: int = 0, col_offset: int = 0) -> ast.Module: "Does module" mod = N(ast.Module, body=list(nodes), type_ignores=type_ignores) mod.lineno = lineno mod.col_offset = col_offset return mod assign: Matcher[Any, ast.Assign] = TypeMatcher() def arith(Op: ast.AST) -> Callable: "Returns a function that generates a basic arithmetic operation" def assemble(left: Any, _: Any, right: Any) -> ast.BinOp: return ast.BinOp(left=ast.Name(id=left), op=Op(), right=make(right)) return assemble def rescue(mystery: Any) -> ast.AST: if isinstance(mystery, ast.AST): return mystery else:
def examine(top: ast.AST) -> int: """Recur down ``top`` to find if there are nodes that wouldn't be findable after exec.""" if isinstance(top, ast.AST): nodec = count_nodes(top) print(f'XXX How to count children in {top}???') import pdb pdb.set_trace() return 0 else: raise TypeError(f"Not possible to count names in {top}") never = lambda: 0 always = lambda: 1 countnames: Matcher[ast.AST, int] = TypeMatcher({ ast.Lambda: never, ast.Assign: always, ast.FunctionDef: always, ast.BinOp: never, ast.Constant: never, ast.Module: never, Miss: examine }) # yapf: disable @countnames.case def interactive_names(inter: ast.Interactive) -> int: return sum([countnames(sub) for sub in inter.body])
~~~~~~~~~~~~~~~~~~~~~~~~ Hm. """ import ast from kingston.match import Matcher, TypeMatcher, ValueMatcher nodeRep: Matcher[ast.AST, str] = TypeMatcher({ ast.Interactive: lambda: '', ast.FunctionDef: lambda node: f"def {node.name}(", ast.arguments: (lambda node: ','.join(arg.arg for arg in node.args) + '):\n'), ast.BinOp: lambda node: '', ast.Constant: lambda node: str(node.value), ast.Return: lambda node: ' return ', ast.Add: lambda: ' + ', }) topnode = compile(""" def helo(): return 1 + 1 """, 'examples.ormsnackis', 'single', ast.PyCF_ONLY_AST) def test(tree):