fst.match
Structural node pattern matching. Uses AST-like and even AST nodes themselves for patterns. Ellipsis is used in
patterns as a wildcard and several extra functional pattern types are provided for setting tags, regex string matching,
logic operations, match check callbacks, match previously matched nodes (backreference) and greedy and non-greedy
quantifiers.
In the examples here you will see AST nodes used interchangeably with their MAST pattern counterparts. For the most
part this is fine and there are only a few small differences between using the two. Except if you are using a type
checker...
Note: Annoyingly this module breaks the convention that anything that has FST class methods imported into the main
FST class has a filename that starts with fst_. This is so that from fst.match import * and
from fst import match as m is cleaner.
An error during matching or substitution.
A falsey object returned if accessing a non-existent tag on the FSTMatch object as an attribute.
Successful match object.
The pattern used for the match. Can be any valid pattern including AST node, primitive values, compiled re.Pattern, etc...
What was matched. Does not have to be a node as list and primitive matches can be matched. If a node is matched then this will be AST or FST depending on which type the target of the match attempt was.
The base class for all non-primitive match patterns. The two main categories of patterns being MAST node
matchers and the functional matchers like MOR and MRE.
Match this pattern against given target. This can take FST nodes or AST (whether they are part of an
FST tree or not, meaning it can match against pure AST trees). Can also match primitives and lists of nodes
if the pattern is set up for that.
Parameters:
target: The target to match. Can be anASTorFSTnode or constant or a list ofASTnodes or constants.ctx: Whether to matchexpr_contextINSTANCES or not (expr_contexttypes are always matched). Defaults toFalseto allow matching backreferences with differentctxvalues with each other, such as aNameas a target of anAssignwith the same name later used in an expression (Storevs.Load). Also as a conveninence since when creatingASTnodes for patterns thectxfield may be created automatically if you don't specify it so may inadvertantly break matches where you don't want to take that into consideration.
Returns:
FSTMatch: The match object on successful match.None: Did not match.
Tagging pattern container. If the given pattern matches then tags specified here will be returned in the
FSTMatch result object. The tags can be static values but also the matched node can be returned in a given tag if
the pattern is passed as a keyword parameter (the first one). This is useful for matching subparts of a target as
the whole match target is already returned in FSTMatch.matched on success.
Parameters:
anon_pat: If the pattern to match is provided in this then the matched node is not returned in tags. If this is missing then there must be at least one element intagsand the first keyword there will be taken to be the pattern to match and the name of the tag to use for the matched node.tags: Any static tags to return on a successful match (including the pattern to match as the first keyword if not provided inanon_pat).
Examples:
>>> M('var') .match(Name(id='NOT_VAR'))
>>> M('var') .match(Name(id='var'))
<FSTMatch Name(id='var')>
>>> M('var', tag='static') .match(Name(id='var'))
<FSTMatch Name(id='var') {'tag': 'static'}>
>>> M(node='var', tag='static') .match(Name(id='var'))
<FSTMatch Name(id='var') {'node': Name(id='var'), 'tag': 'static'}>
>>> M(M(M(node='var'), add1=1), add2=2) .match(Name(id='var'))
<FSTMatch Name(id='var') {'node': Name(id='var'), 'add1': 1, 'add2': 2}>
Tagging NOT logic pattern container. If the given pattern DOES NOT match then tags specified here will be
returned in the FSTMatch result object. The tags can be specified with static values but also the unmatched node
can be returned in a given tag name.
Any tags that were being propagated up from successful matches are discarded since that constitutes an unsuccessful match for this node. And any unsuccessful matches were not propagating any tags up anyway. So this node guarantees that the only tags that it returns are ones that it itself provides.
Parameters:
anon_pat: If the pattern to not match is provided in this then the unmatched node is not returned in tags. If this is missing then there must be at least one element intagsand the first keyword there will be taken to be the pattern to not match and the name of the tag to use for the unmatched node.tags: Any static tags to return on an unsuccessful match (including the pattern to not match as the first keyword if not provided inanon_pat).
Examples:
>>> MNOT('var') .match(Name(id='NOT_VAR'))
<FSTMatch Name(id='NOT_VAR')>
>>> MNOT('var') .match(Name(id='var'))
>>> MNOT('var', tag='static') .match(Name(id='NOT_VAR'))
<FSTMatch Name(id='NOT_VAR') {'tag': 'static'}>
>>> MNOT(node='var', tag='static') .match(Name(id='NOT_VAR'))
<FSTMatch Name(id='NOT_VAR') {'node': Name(id='NOT_VAR'), 'tag': 'static'}>
>>> MNOT(MNOT(node='var', tag='static')) .match(Name(id='NOT_VAR'))
>>> MNOT(MNOT(MNOT(node='var'), add1=1), add2=2) .match(Name(id='NOT_VAR'))
<FSTMatch Name(id='NOT_VAR') {'add2': 2}>
Simple OR pattern. Matches if any of the given patterns match.
Parameters:
anon_pats: Patterns that constitute a successful match, only need one to match. Checked in order and exit on first successful match.tagged_pats: Patterns that constitute a successful match, only need one to match. Checked in order and exit on first successful match. The first target matched with any one of these patterns will be returned in its corresponding tag (keyword name).
Examples:
>>> MOR('a', this='b') .match(FST('a'))
<FSTMatch <Name ROOT 0,0..0,1>>
>>> MOR('a', this='b') .match(FST('b'))
<FSTMatch <Name ROOT 0,0..0,1> {'this': <Name ROOT 0,0..0,1>}>
>>> MOR('a', this='b') .match(FST('c'))
Mixed pattern types and nesting.
>>> pat = MOR(Name('good'), M(m=Call, static='tag'), st=Starred)
>>> pat.match(FST('bad'))
>>> pat.match(FST('good'))
<FSTMatch <Name ROOT 0,0..0,4>>
>>> pat.match(FST('*starred'))
<FSTMatch <Starred ROOT 0,0..0,8> {'st': <Starred ROOT 0,0..0,8>}>
>>> pat.match(FST('call()'))
<FSTMatch <Call ROOT 0,0..0,6> {'m': <Call ROOT 0,0..0,6>, 'static': 'tag'}>
>>> pat.match(FST('bin + op'))
Simple AND pattern. Matches only if all of the given patterns match. This pattern isn't terribly useful for straight node matches as if you try to combine different node types using this node their types become mutually exclusive and will make this always fail. Where this can come in handy for example is when examining list fields for multiple conditions.
Parameters:
anon_pats: Patterns that need to match to constitute a success. Checked in order.tagged_pats: Patterns that need to match to constitute a success. Checked in order. All the targets matched with these patterns will be returned in their corresponding tags (keyword names).
Examples:
The following will always fail as a node cannot be both a Name AND a Call at the same time.
>>> MAND(Name, Call) .match(FST('name'))
>>> MAND(Name, Call) .match(FST('call()'))
More sensical usage below, for example that a list starts with a and contains at least two b.
>>> pat = MList(MAND(['a', MQSTAR], [MQSTAR, 'b', MQSTAR, 'b', MQSTAR]))
>>> pat.match(FST('[a, b, c]'))
>>> pat.match(FST('[a, b, b, c]'))
<FSTMatch <List ROOT 0,0..0,12>>
>>> pat.match(FST('[d, a, b, b, c]'))
>>> pat.match(FST('[a, x, b, y, z, b, c, b]'))
<FSTMatch <List ROOT 0,0..0,24>>
This is a pattern or None match. It can be used to optionally match single-element fields which may or may not
be present. That is, both a normal value which matches the pattern and a None value are considered a successful
match. A non-None value which does NOT match the pattern is a failure.
This is essentially MOR(pattern, None).
Parameters:
anon_pat: If the pattern to match is provided in this then the matched node is not returned in tags. If this is missing then there must be at least one element intagsand the first keyword there will be taken to be the pattern to match and the name of the tag to use for the matched node.tags: Any static tags to return on a successful match (including the pattern to match as the first keyword if not provided inanon_pat). These are added AFTER all the child match tags.
Examples:
Single optional nodes.
>>> MFunctionDef(returns=MMAYBE('int')) .match(FST('def f(): pass'))
<FSTMatch <FunctionDef ROOT 0,0..0,13>>
>>> MFunctionDef(returns=MMAYBE('int')) .match(FST('def f() -> int: pass'))
<FSTMatch <FunctionDef ROOT 0,0..0,20>>
>>> MFunctionDef(returns=MMAYBE('int')) .match(FST('def f() -> str: pass'))
Parts of multinode elements.
>>> MDict([MMAYBE('a')], ['b']) .match(FST('{a: b}'))
<FSTMatch <Dict ROOT 0,0..0,6>>
>>> MDict([MMAYBE('a')], ['b']) .match(FST('{**b}'))
<FSTMatch <Dict ROOT 0,0..0,5>>
>>> MDict([MMAYBE('a')], ['b']) .match(FST('{x: b}'))
Non-node primitive fields.
>>> MExceptHandler(name=MMAYBE('n')) .match(FST('except x as n: pass'))
<FSTMatch <ExceptHandler ROOT 0,0..0,19>>
>>> MExceptHandler(name=MMAYBE('n')) .match(FST('except x as o: pass'))
>>> MExceptHandler(name=MMAYBE('n')) .match(FST('except x: pass'))
<FSTMatch <ExceptHandler ROOT 0,0..0,14>>
This pattern matches any one of the given types and arbitrary fields if present. This is essentially
MAND(MOR(*types), MAST(**fields)). But the types must be actual types, not other patterns.
Note: Since there are several fields which can be either an individual element or list of elements, matching a list pattern vs. a non-list and vice versa is just treated as a non-match.
Parameters:
anon_types: If the types to match are provided in this then the matched node is not returned in tags. If this is missing then there must be at least one element intagsand the first keyword there will be taken to be the types to match and the name of the tag to use for the matched node. The types to match are an iterable ofASTorMASTTYPES, not instances. In order to match successfully the target must be at least one of these types (non-leaf types likestmtincluded). If you just want to match any possibleASTtype with a given set of fields you can useMAST(**fields)instead of this.fields: Field names which must be present (unless wildcard...) along with the patterns they need to match.
Examples:
Whether a statement type that CAN have a docstring actually has a docstring.
>>> pat = MTYPES(
... (ClassDef, FunctionDef, AsyncFunctionDef),
... body=[MExpr(MConstant(str)), MQSTAR],
... )
>>> pat.match(FST('def f(): "docstr"; pass'))
<FSTMatch <FunctionDef ROOT 0,0..0,23>>
>>> pat.match(FST('if 1: "NOTdocstr"'))
>>> pat.match(FST('def f(): pass; "NOTdocstr"'))
>>> pat.match(FST('class cls: "docstr"; pass'))
<FSTMatch <ClassDef ROOT 0,0..0,25>>
>>> pat = MTYPES(
... tag=(ClassDef, FunctionDef, AsyncFunctionDef),
... body=[MExpr(MConstant(str)), MQSTAR],
... )
>>> pat.match(FST('class cls: "docstr"; pass'))
<FSTMatch <ClassDef ROOT 0,0..0,25> {'tag': <ClassDef ROOT 0,0..0,25>}>
Tagging regex pattern. Normal re.Pattern can be used and that will just be checked using re.match() and the
re.Match object is lost. If this pattern is used instead then you can specify the use of re.match() or
re.search(), as well as allowing the re.Match object to be returned as a tag.
Parameters:
anon_re_pat: If the pattern to match is provided in this (either as astrto compile or an already compiledre.Pattern) then the matchedre.Matchis not returned in tags. If this is missing then there must be at least one element intagsand the first keyword there will be taken to be the pattern to match and the name of the tag to use for the matchedre.Matchobject.flags: If passing astras the pattern then these are thereflags to pass tore.compile(). If passing an already compiledre.Patternthen this must remain0.tags: Any static tags to return on a successful match (including the pattern to match as the first keyword if not provided inanon_re_pat).
Examples:
>>> from fst.docs import ppmatch # pretty-print FSTMatch
>>> MRE('good|bad|ugly') .match(Name('ugly'))
<FSTMatch Name(id='ugly')>
>>> MRE('good|bad|ugly') .match(Name('passable'))
Has bad in it.
>>> ppmatch(MRE(tag='.*bad.*') .match(arg('this_arg_is_not_so_bad')))
<FSTMatch arg(arg='this_arg_is_not_so_bad', annotation=None, type_comment=None)
{'tag': <re.Match object; span=(0, 22), match='this_arg_is_not_so_bad'>}>
Another way so we can get the exact location from the re.Match object.
>>> ppmatch(MRE(tag='bad', search=True) .match(arg('this_arg_is_not_so_bad')))
<FSTMatch arg(arg='this_arg_is_not_so_bad', annotation=None, type_comment=None)
{'tag': <re.Match object; span=(19, 22), match='bad'>}>
>>> ppmatch(MDict(_all=[MQSTAR(t=MRE('a: .'))]) .match(FST('{a: b, a: z}')))
<FSTMatch <Dict ROOT 0,0..0,12>
't': [
<FSTMatch <<Dict ROOT 0,0..0,12>._all[:1]>>,
<FSTMatch <<Dict ROOT 0,0..0,12>._all[1:2]>>,
],
}>
Callback to check target, which can be a node (AST or FST depending on the type of tree the pattern was
called on) or constant or a list of ASTs or constants.
Parameters:
anon_callback: The function to call on each target to check. Depending on where in the pattern structure this pattern is used, the function may be called with a node or a primitive, or even a list of elements. Further, depending on the initial target of the match, the node may beFSTif the target was anFSTnode andASTotherwise. If the function returns a truthy value then the match is considered a success, and likewise falsey means failure. If this is not provided then the callback is taken from the first keyword intagsjust like for theMpattern.tag_ret: If this is set toTruethen the truthy return value is returned in the pattern tag instead of the target matched. This only applies if the callback is passed as the first keyword intagsinstead of inanon_callback, as in that case no target tag is available to return. Also keep in mind the "truthy value" bit in case a successful match might want to return a falsey value, it would need to be accomodated somehow (wrapped in a tuple maybe). Or just provide an explicitfail_objto check against to determine failure.fail_obj: An explicit object to check identity against to determine if the callback failed.pass_tags: IfFalsethen the normal single-object callback format is used. IfTruethen will call the callback with an additional parameter which is a tag getter function which allows the callback to get tags which have been set up to this point. The tag getter works in the same way asdict.get()and the default for no tag isfst.match.NotSet.
Examples:
>>> in_range = lambda x: 2 < x < 8
>>> pat = MConstant(MCB(in_range))
>>> pat.match(FST('1'))
>>> pat.match(FST('3'))
<FSTMatch <Constant ROOT 0,0..0,1>>
>>> pat.match(FST('7'))
<FSTMatch <Constant ROOT 0,0..0,1>>
>>> pat.match(FST('10'))
Check for only parenthesized tuples.
>>> pat = MCB(FST.is_parenthesized_tuple)
>>> pat.match(FST('x, y, z'))
>>> pat.match(FST('(x, y, z)'))
<FSTMatch <Tuple ROOT 0,0..0,9>>
>>> pat.match(FST('[x, y, z]'))
Get node along with name in uppercase.
>>> pat = M(node=Name(MCB(upper=str.upper, tag_ret=True)))
>>> pat.match(FST('a.b'))
>>> pat.match(FST('some_name'))
<FSTMatch <Name ROOT 0,0..0,9> {'upper': 'SOME_NAME', 'node': <Name ROOT 0,0..0,9>}>
An explicit fail object can be provided in case you want to be able to tag falsey values directly, it is checked by identity.
>>> MCB(tag=lambda f: False, tag_ret=True, fail_obj=None) .match(FST('a'))
<FSTMatch <Name ROOT 0,0..0,1> {'tag': False}>
>>> MCB(tag=lambda f: None, tag_ret=True, fail_obj=None) .match(FST('a'))
The type of node passed to the callback depends on the type of tree that match() is called on.
>>> pat = MCB(lambda n: print(type(n)))
>>> pat.match(Name('name'))
<class 'ast.Name'>
>>> pat.match(FST('name'))
<class 'fst.fst.FST'>
A tag getter function can be passed to the callback so it can request tags that have been set so far.
>>> m = M(prev=MCB(
... lambda t, g: (print(f"this: {t}, prev: {g('prev')}"),),
... pass_tags=True,
... ))
>>> MList([m, m, m]) .match(FST('[a, b, c]'))
this: <Name 0,1..0,2>, prev: <NotSet>
this: <Name 0,4..0,5>, prev: <Name 0,1..0,2>
this: <Name 0,7..0,8>, prev: <Name 0,4..0,5>
<FSTMatch <List ROOT 0,0..0,9> {'prev': <Name 0,7..0,8>}>
Depending on if the match target is an FST or AST and if the MCB parameter pass_tags is True or
False, this callback will be one of:
Callable[[[AST | constant]], object]Callable[[[FST | constant]], object]Callable[[[AST | constant], Callable[[str, object], object]], object]Callable[[[FST | constant], Callable[[str, object], object]], object]
Where constant is ellipsis | int | float | complex | str | bytes | bool | None.
Match previously matched node (backreference). Looks through tags of current matches and if found then attempts to match against the value of the tag. Meant for matching previously matched nodes but can match anything which can work as a valid pattern in a tag, however it got there.
For the sake of sanity and efficiency, does not recurse into lists of FSTMatch objects that have already been
built by quantifier patterns. Can match those tags if they are not being put into FSTMatch objects though.
Parameters:
anon_tag: If the source tag to match is provided in this then the new matched node is not returned in tags. If this is missing then there must be at least one element intagsand the first keyword there will be taken to be the source tag to match and the name of the new tag to use for the matched node.tags: Any static tags to return on a successful match (including the pattern to match as the first keyword if not provided inanon_pat).
Examples:
>>> from fst.docs import ppmatch # pretty-print FSTMatch
>>> MBinOp(M(left='a'), right=MTAG('left')) .match(FST('a + a'))
<FSTMatch <BinOp ROOT 0,0..0,5> {'left': <Name 0,0..0,1>}>
>>> MBinOp(M(left='a'), right=MTAG('left')) .match(FST('a + b'))
The tag must already have been matched, not be in the future.
>>> MBinOp(MTAG('right'), right=M(right='a')) .match(FST('a + a'))
Works just fine with quantifier patterns.
>>> pat = MList([M(first='a'), MQSTAR(st=MTAG('first'))])
>>> ppmatch(pat.match(FST('[a, a, a]')))
<FSTMatch <List ROOT 0,0..0,9>
'first': <Name 0,1..0,2>,
'st': [
<FSTMatch <Name 0,4..0,5>>,
<FSTMatch <Name 0,7..0,8>>,
],
}>
>>> pat.match(FST('[a, a, a, b]'))
Can match previously matched multinode items from Dict, MatchMapping or arguments.
>>> MDict(_all=[M(start=...), MQSTAR, MTAG('start')]) .match(FST('{1: a, 1: a}'))
<FSTMatch <Dict ROOT 0,0..0,12> {'start': <<Dict ROOT 0,0..0,12>._all[:1]>}>
Quantifier pattern. Matches at least min and at most max instances of the given pattern. Since this pattern
can match an arbitrary number of actual targets, if there is a tag for the matched patterns they are given as a list
of FSTMatch objects instead of just one.
This is the base class for MQSTAR, MQPLUS, MQOPT, MQMIN, MQMAX and MQN and can do everything they can
with the appropriate values for min and max. Those classes are provided regardless for convenience and cleaner
pattern structuring.
Using the wildcard pattern ... means match anything the given number of times and is analogous to the regex dot
. pattern.
Quantifiers can ONLY live as DIRECT children of a list field, so MList(elts=[MQSTAR]) is valid while
MList(elts=[M(MQSTAR)]) is not.
Unlike other patterns, all the quantifier patterns can take a sublist of patterns as a pattern which allows matching an arbitrary nunmber this complete sublist sequentially in the list field the quantifier pattern is matching.
Quantifier pattern sublists can ONLY live as DIRECT child patterns of a quantifier, so MQSTAR([...]) is
valid while MQSTAR(M([...])) is not.
This pattern is greedy by default but has a non-greedy version child class MQ.NG. This pattern can be used in
exactly the same way but will match non-greedily.
Parameters:
anon_pat: If the pattern to match is provided in this then the matched nodes tags are all merged in order and returned as normal tags, with later match tags overriding earlier if same. If this is missing then there must be at least one element intagsand the first keyword there will be taken to be the pattern to match. In this case the keyword is used as a tag which is used to return a list ofFSTMatchobjects for each node matched by the pattern.min: The minimum number of pattern matches needed for a successful match.max: The maximum number of pattern matches taken for a successful match.Nonemeans unbounded.tags: Any static tags to return on a successful match (including the pattern to match as the first keyword if not provided inanon_pat). These are added AFTER all the child match tags.
Examples:
>>> from fst.docs import ppmatch # pretty-print FSTMatch
>>> MList([MQ('a', 1, 2)]) .match(FST('[]'))
>>> MList([MQ('a', 1, 2)]) .match(FST('[a]'))
<FSTMatch <List ROOT 0,0..0,3>>
>>> MList([MQ('a', 1, 2)]) .match(FST('[a, a]'))
<FSTMatch <List ROOT 0,0..0,6>>
>>> MList([MQ('a', 1, 2)]) .match(FST('[a, a, a]'))
>>> MList([MQ('a', 1, 2), MQSTAR]) .match(FST('[a, a, a]'))
<FSTMatch <List ROOT 0,0..0,9>>
>>> pat = MGlobal([MQSTAR.NG, MQ(t='c', min=1, max=2), MQSTAR])
>>> ppmatch(FST('global a, b, c, c, d, e') .match(pat))
<FSTMatch <Global ROOT 0,0..0,23>
't': [
<FSTMatch <<Global ROOT 0,0..0,23>.names[2:3]>>,
<FSTMatch <<Global ROOT 0,0..0,23>.names[3:4]>>,
],
}>
>>> pat = MList([MQSTAR.NG, MQ(t=MRE('c|d'), min=1, max=3), MQSTAR])
>>> ppmatch(m := FST('[a, b, c, c, d, c, e]') .match(pat))
<FSTMatch <List ROOT 0,0..0,21>
't': [
<FSTMatch <Name 0,7..0,8>>,
<FSTMatch <Name 0,10..0,11>>,
<FSTMatch <Name 0,13..0,14>>,
],
}>
>>> [mm.matched.src for mm in m['t']]
['c', 'c', 'd']
Sublist matching, will match the sequence "a, b" twice in the example below.
>>> ppmatch(MList([MQ(t=['a', 'b'], min=1, max=2)]) .match(FST('[a, b, a, b]')))
<FSTMatch <List ROOT 0,0..0,12>
't': [
<FSTMatch [<Name 0,1..0,2>, <Name 0,4..0,5>]>,
<FSTMatch [<Name 0,7..0,8>, <Name 0,10..0,11>]>,
],
}>
Non-greedy version of MQ quantifier pattern.
Star quantifier pattern, zero or more. Shortcut for MQ(anon_pat, min=0, max=None, **tags). Has non-greedy
version child class MQSTAR.NG.
This class type itself as well as the the non-greedy version can be used directly as a shortcut for MQSTAR(...)
or MQSTAR.NG(...), the equivalents of regex .* and .*?.
Parameters:
anon_pat: If the pattern to match is provided in this then the matched nodes tags are all merged in order and returned as normal tags, with later match tags overriding earlier if same. If this is missing then there must be at least one element intagsand the first keyword there will be taken to be the pattern to match. In this case the keyword is used as a tag which is used to return a list ofFSTMatchobjects for each node matched by the pattern.tags: Any static tags to return on a successful match (including the pattern to match as the first keyword if not provided inanon_pat). These are added AFTER all the child match tags.
Examples:
>>> from fst.docs import ppmatch # pretty-print FSTMatch
>>> MList([MQSTAR(t='a')]) .match(FST('[]'))
<FSTMatch <List ROOT 0,0..0,2> {'t': []}>
>>> MList([MQSTAR(t='a')]) .match(FST('[a]'))
<FSTMatch <List ROOT 0,0..0,3> {'t': [<FSTMatch <Name 0,1..0,2>>]}>
>>> ppmatch(MList([MQSTAR(t='a')]) .match(FST('[a, a]')))
<FSTMatch <List ROOT 0,0..0,6>
{'t': [<FSTMatch <Name 0,1..0,2>>, <FSTMatch <Name 0,4..0,5>>]}>
>>> ppmatch(MList([MQSTAR(t='a')]) .match(FST('[a, a, a]')))
<FSTMatch <List ROOT 0,0..0,9>
't': [
<FSTMatch <Name 0,1..0,2>>,
<FSTMatch <Name 0,4..0,5>>,
<FSTMatch <Name 0,7..0,8>>,
],
}>
>>> ppmatch(MList([MQSTAR(t='a'), MQSTAR]) .match(FST('[a, a, a]')))
<FSTMatch <List ROOT 0,0..0,9>
't': [
<FSTMatch <Name 0,1..0,2>>,
<FSTMatch <Name 0,4..0,5>>,
<FSTMatch <Name 0,7..0,8>>,
],
}>
>>> MList([MQSTAR.NG(t='a'), MQSTAR]) .match(FST('[a, a, a]'))
<FSTMatch <List ROOT 0,0..0,9> {'t': []}>
Non-greedy version of MQSTAR quantifier pattern.
Plus quantifier pattern, one or more. Shortcut for MQ(anon_pat, min=1, max=None, **tags). Has non-greedy
version child class MQPLUS.NG.
This class type itself as well as the the non-greedy version can be used directly as a shortcut for MQPLUS(...)
or MQPLUS.NG(...), the equivalents of regex .+ and .+?.
Parameters:
anon_pat: If the pattern to match is provided in this then the matched nodes tags are all merged in order and returned as normal tags, with later match tags overriding earlier if same. If this is missing then there must be at least one element intagsand the first keyword there will be taken to be the pattern to match. In this case the keyword is used as a tag which is used to return a list ofFSTMatchobjects for each node matched by the pattern.tags: Any static tags to return on a successful match (including the pattern to match as the first keyword if not provided inanon_pat). These are added AFTER all the child match tags.
Examples:
>>> from fst.docs import ppmatch # pretty-print FSTMatch
>>> MList([MQPLUS(t='a')]) .match(FST('[]'))
>>> MList([MQPLUS(t='a')]) .match(FST('[a]'))
<FSTMatch <List ROOT 0,0..0,3> {'t': [<FSTMatch <Name 0,1..0,2>>]}>
>>> ppmatch(MList([MQPLUS(t='a')]) .match(FST('[a, a]')))
<FSTMatch <List ROOT 0,0..0,6>
{'t': [<FSTMatch <Name 0,1..0,2>>, <FSTMatch <Name 0,4..0,5>>]}>
>>> ppmatch(MList([MQPLUS(t='a')]) .match(FST('[a, a, a]')))
<FSTMatch <List ROOT 0,0..0,9>
't': [
<FSTMatch <Name 0,1..0,2>>,
<FSTMatch <Name 0,4..0,5>>,
<FSTMatch <Name 0,7..0,8>>,
],
}>
>>> ppmatch(MList([MQPLUS(t='a'), MQSTAR]) .match(FST('[a, a, a]')))
<FSTMatch <List ROOT 0,0..0,9>
't': [
<FSTMatch <Name 0,1..0,2>>,
<FSTMatch <Name 0,4..0,5>>,
<FSTMatch <Name 0,7..0,8>>,
],
}>
>>> MList([MQPLUS.NG(t='a'), MQSTAR]) .match(FST('[a, a, a]'))
<FSTMatch <List ROOT 0,0..0,9> {'t': [<FSTMatch <Name 0,1..0,2>>]}>
Non-greedy default version of MQPLUS quantifier pattern.
Zero or one (optional) quantifier pattern. Shortcut for MQ(anon_pat, min=0, max=1, **tags). Has non-greedy
version child class MQOPT.NG.
This class type itself as well as the the non-greedy version can be used directly as a shortcut for MQOPT(...) or
MQOPT.NG(...), the equivalents of regex .? and .??.
Parameters:
anon_pat: If the pattern to match is provided in this then the matched node tags are returned as normal tags. If this is missing then there must be at least one element intagsand the first keyword there will be taken to be the pattern to match. In this case the keyword is used as a tag which is used to return either an empty list or a list with a singleFSTMatchobject for the node matched by the pattern.tags: Any static tags to return on a successful match (including the pattern to match as the first keyword if not provided inanon_pat). These are added AFTER all the child match tags.
Examples:
>>> MList([MQOPT(t='a')]) .match(FST('[]'))
<FSTMatch <List ROOT 0,0..0,2> {'t': []}>
>>> MList([MQOPT(t='a')]) .match(FST('[a]'))
<FSTMatch <List ROOT 0,0..0,3> {'t': [<FSTMatch <Name 0,1..0,2>>]}>
>>> MList([MQOPT(t='a')]) .match(FST('[b]'))
>>> MList([MQOPT(t='a')]) .match(FST('[a, a]'))
>>> MList([MQOPT(t='a'), MQSTAR]) .match(FST('[a, a]'))
<FSTMatch <List ROOT 0,0..0,6> {'t': [<FSTMatch <Name 0,1..0,2>>]}>
>>> MList([MQOPT.NG(t='a'), MQSTAR]) .match(FST('[a, a]'))
<FSTMatch <List ROOT 0,0..0,6> {'t': []}>
>>> MList([MQOPT.NG(t='a'), 'b']) .match(FST('[a, b]'))
<FSTMatch <List ROOT 0,0..0,6> {'t': [<FSTMatch <Name 0,1..0,2>>]}>
Non-greedy version of MQOPT quantifier pattern.
Minimum count quantifier pattern. Shortcut for MQ(anon_pat, min=min, max=None, **tags). Has non-greedy version
child class MQMIN.NG.
Parameters:
anon_pat: If the pattern to match is provided in this then the matched nodes tags are all merged in order and returned as normal tags, with later match tags overriding earlier if same. If this is missing then there must be at least one element intagsand the first keyword there will be taken to be the pattern to match. In this case the keyword is used as a tag which is used to return a list ofFSTMatchobjects for each node matched by the pattern.min: The minimum number of pattern matches needed for a successful match.tags: Any static tags to return on a successful match (including the pattern to match as the first keyword if not provided inanon_pat). These are added AFTER all the child match tags.
Examples:
>>> from fst.docs import ppmatch # pretty-print FSTMatch
>>> MList([MQMIN(t='a', min=2)]) .match(FST('[]'))
>>> MList([MQMIN(t='a', min=2)]) .match(FST('[a]'))
>>> ppmatch(MList([MQMIN(t='a', min=2)]) .match(FST('[a, a]')))
<FSTMatch <List ROOT 0,0..0,6>
{'t': [<FSTMatch <Name 0,1..0,2>>, <FSTMatch <Name 0,4..0,5>>]}>
>>> ppmatch(MList([MQMIN(t='a', min=2)]) .match(FST('[a, a, a]')))
<FSTMatch <List ROOT 0,0..0,9>
't': [
<FSTMatch <Name 0,1..0,2>>,
<FSTMatch <Name 0,4..0,5>>,
<FSTMatch <Name 0,7..0,8>>,
],
}>
>>> ppmatch(MList([MQMIN(t='a', min=2), MQSTAR]) .match(FST('[a, a, a]')))
<FSTMatch <List ROOT 0,0..0,9>
't': [
<FSTMatch <Name 0,1..0,2>>,
<FSTMatch <Name 0,4..0,5>>,
<FSTMatch <Name 0,7..0,8>>,
],
}>
>>> ppmatch(MList([MQMIN.NG(t='a', min=2), MQSTAR]) .match(FST('[a, a, a]')))
<FSTMatch <List ROOT 0,0..0,9>
{'t': [<FSTMatch <Name 0,1..0,2>>, <FSTMatch <Name 0,4..0,5>>]}>
Non-greedy version of MQMIN quantifier pattern.
Maximum count quantifier pattern. Shortcut for MQ(anon_pat, min=0, max=max, **tags). Has non-greedy version
child class MQMAX.NG.
Parameters:
anon_pat: If the pattern to match is provided in this then the matched nodes tags are all merged in order and returned as normal tags, with later match tags overriding earlier if same. If this is missing then there must be at least one element intagsand the first keyword there will be taken to be the pattern to match. In this case the keyword is used as a tag which is used to return a list ofFSTMatchobjects for each node matched by the pattern.max: The maximum number of pattern matches taken for a successful match.Nonemeans unbounded.tags: Any static tags to return on a successful match (including the pattern to match as the first keyword if not provided inanon_pat). These are added AFTER all the child match tags.
Examples:
>>> from fst.docs import ppmatch # pretty-print FSTMatch
>>> MList([MQMAX(t='a', max=2)]) .match(FST('[]'))
<FSTMatch <List ROOT 0,0..0,2> {'t': []}>
>>> MList([MQMAX(t='a', max=2)]) .match(FST('[a]'))
<FSTMatch <List ROOT 0,0..0,3> {'t': [<FSTMatch <Name 0,1..0,2>>]}>
>>> ppmatch(MList([MQMAX(t='a', max=2)]) .match(FST('[a, a]')))
<FSTMatch <List ROOT 0,0..0,6>
{'t': [<FSTMatch <Name 0,1..0,2>>, <FSTMatch <Name 0,4..0,5>>]}>
>>> MList([MQMAX(t='a', max=2)]) .match(FST('[a, a, a]'))
>>> ppmatch(MList([MQMAX(t='a', max=2), MQSTAR]) .match(FST('[a, a, a]')))
<FSTMatch <List ROOT 0,0..0,9>
{'t': [<FSTMatch <Name 0,1..0,2>>, <FSTMatch <Name 0,4..0,5>>]}>
>>> MList([MQMAX.NG(t='a', max=2), MQSTAR]) .match(FST('[a, a, a]'))
<FSTMatch <List ROOT 0,0..0,9> {'t': []}>
Non-greedy version of MQMAX quantifier pattern.
Exact count quantifier pattern. Shortcut for MQ(anon_pat, min=n, max=n, **tags). Does NOT have a non-greedy
version child class MQN.NG as that would not make any sense. Instead has itself as an attribute NG so can still
be used as MQN.NG.
Parameters:
anon_pat: If the pattern to match is provided in this then the matched nodes tags are all merged in order and returned as normal tags, with later match tags overriding earlier if same. If this is missing then there must be at least one element intagsand the first keyword there will be taken to be the pattern to match. In this case the keyword is used as a tag which is used to return a list ofFSTMatchobjects for each node matched by the pattern.n: The exact number of pattern matches taken for a successful match.tags: Any static tags to return on a successful match (including the pattern to match as the first keyword if not provided inanon_pat). These are added AFTER all the child match tags.
Examples:
>>> MList([MQN(t='a', n=1)]) .match(FST('[]'))
>>> MList([MQN(t='a', n=1)]) .match(FST('[a]'))
<FSTMatch <List ROOT 0,0..0,3> {'t': [<FSTMatch <Name 0,1..0,2>>]}>
>>> MList([MQN(t='a', n=1)]) .match(FST('[a, a]'))
>>> MList([MQN(t='a', n=1), MQSTAR]) .match(FST('[a]'))
<FSTMatch <List ROOT 0,0..0,3> {'t': [<FSTMatch <Name 0,1..0,2>>]}>
This class (and its non-leaf subclasses like Mstmt and Mexpr) can be used as an arbitrary field(s) pattern.
Can match one or more fields (list or not, without list type errors), as long as the type matches (e.g. an Assign
matches an Mstmt but not an Mexpr). This arbitrary field matching behavior is unique to non-leaf AST types,
concrete types like MAssign always have fixed fields.
Note: Since there are several fields which can be either an individual element or list of elements, matching a list pattern vs. a non-list and vice versa is just treated as a non-match.
Parameters:
fields: If provided then is an arbitrary list of fields to match. Otherwise will just match based on type.
Examples:
Will match any AST node which has a value which is a Call. So will match return f(),
await something(a, b, c) and just an Expr call(args), but not yield x or *(a, b, c).
>>> pat = MAST(value=Call)
>>> pat.match(FST('return f()'))
<FSTMatch <Return ROOT 0,0..0,10>>
>>> pat.match(FST('await something(a, b, c)'))
<FSTMatch <Await ROOT 0,0..0,24>>
>>> pat.match(FST('call(args)', Expr))
<FSTMatch <Expr ROOT 0,0..0,10>>
>>> pat.match(FST('yield x'))
>>> pat.match(FST('*(a, b, c)'))
Will match any statement which has a Constant string as the first element of its body, in other words a docstring.
>>> pat = Mstmt(body=[MExpr(MConstant(str)), MQSTAR])
>>> pat.match(FST('def f(): "docstr"; pass'))
<FSTMatch <FunctionDef ROOT 0,0..0,23>>
>>> pat.match(FST('class cls: "docstr"'))
<FSTMatch <ClassDef ROOT 0,0..0,19>>
>>> pat.match(FST('class cls: 1'))
>>> pat.match(FST('class cls: 1; "NOTdocstr"'))
>>> pat.match(FST('class cls: pass'))
This works like all the other match pattern classes except that it has an extra _strict parameter which
controls how it matches against individual arguments._all. This parameter can also be set on a normal AST
arguments class for the same effect.
Parameters:
_strict: Set this to control matching, if not set defaults toFalse.True:posonlyargsonly match toposonlyargs,kwonlyargstokwonlyargsandargsonly toargs. Their associated defaults only match to the same type of default as well.False: Same asTrueexcept thatargsin the pattern matches toargs,posonlyargsorkwonlyargsin the target anddefaultslikewise can also match tokw_defaults. This allows the use of the standard args to search in all the args fields.None: All types of args and defaults can match to each other.