If take a look in the metavariable patterns you can see that it can get as complex as you want.
But, let’s focus in a particular token. We end up with a metavariable, but we can see a pattern from simplicity to complexity:
1. Just abstract the token as a metavariable: we do not care if the method contains the exact word we want everything that contains something acting as that.
Maybe we do not care about who is the sender of the message #size. In order to do that, we will abstract the variable ‘aVariable’ as a metavariable:
2. Abstract a list of tokens: we do not care if the method cointain not only one element also a lot of elements.
If the pattern is `object size we are too specific because we are only matching the send to size whom receiver is an object, if we have something like this:
(aCollection select: #even) asOrderedCollection size.
Is not a result, if we want that, we have to specify that we do not care:
3. Abstract a recursive pattern: still maybe we want matches if the pattern is repeated
aVariable size size.
If our pattern is: `object size, this does not match. If it’s: `@some size we have one match when some = ‘aVariable size’.
If we put a recursive pattern:
We have two matches: one when some=’aVariable size’ and another when some= ‘aVariable’
4. Abstract a statement: Maybe we do not even care about the hole statement, and for this we should use the dot.
We can think that in the abstraction patterns you have a variety in abstraction, since one more concret with more information until one more abstract.
And for that we can conclude that we have a flow during the metavariable abstraction:
NoAbstraction <=> Simple <=> List <=> Recursive <=>Statement
In order to support this in flamel I’ve added #FlamelAbstractionStep that basically have 3 main responsabilities:
- next step: abstract
- previous step: unAbstract
- pattern: pattern
You can take an expression, obtain the AST representation and then just send to the node the messages abstract and unAbstract to let the abstration proccess to continue, once you finish just use the expression builder to obtain the expression.
testAbstractVariableNodeWritesRightPattern | searchExpression variable root expression | expression := 'aVariable aMessage'. root := RBParser parseExpression: expression. variable := root receiver . variable abstractOnMatching. searchExpression := FlamelSearchExpressionBuilder new searchExpressionFor: root. self assert: searchExpression equals: '`aVariable aMessage'
If we do:
variable variable abstractOnMatching. variable variable abstractOnMatching.
We will end up with: `@aVariable aMessage.
If you want to get fun annotating nodes I added some protocols to maintain the state in a node:
- abstractOnMatching: instead the source code for the node we are interested in the metavariable. Each time you say abstractOnMatching you are going up in the matching steps
- deleteOnMatching: when we want to match, we should not include the code contained inside the node
- ignoreOnMatching: we go to the last step in the matching process
- unAbstractOnMatching: we want the previous step for the metavariable
I Also wrote a visitor to genrate the matching expression, so in order to obtain the expression, you can evaluate:
FlamelSearchExpressionBuilder new searchExpressionFor: root.