pharo-users@lists.pharo.org

Any question about pharo is welcome

View all threads

Can the rewrite tool replace a single statement with multiple statements?

TM
Tim Mackinnon
Mon, Jun 3, 2024 8:43 AM

I've never really learned the rewrite tool but while waiting for changes to propagate through P12, I thought maybe I could use it to safely patch changes until there are official updates (and it was a good chance to get more familiar with it).

I thought there was more documentation available for how it works (interested in any old articles - but the online help in P12 is ok - although it doesn't display in markdown - in fact give there is markdown support now - I am a bit confused what should appear more readable these days - and how to fix/contribute changes to help).

This aside - I wanted to patch the SystemWindow>>taskbarButtonMenu: (and add my fix to move tabs to any position).

I started writing the following search expression - the find the last call to #addToggle:... method call that has a literal referencing #canBeMovedRight - as I want to add my new menu item after it.

e.g.

moveSubmenu @addToggle: { :node | node isLiteralNode and: [ node value = #canBeMovedToRight] }

but I'm not sure my {} filter block is ever called, as the above matches both addToggle:... calls, and not the one I'm after? I don't understand this - any have ideas on the proper syntax for generic matching?

Anyway - I decided that in my case there is only one - so I can match it explicitly and so I  search on the full call:

and I entered this:
moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight.

Which matches exactly - then I want to replace the single statement with 2 statements e.g.

moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight.

moveSubmenu
addToggle: 'Move to' translated
target: self
selector: #taskbarMoveTo
getStateSelector: nil.

However this gives an error in the rewrite tool (actually it results in a zero change, that then gives an error for EpMonitor).

But my question is - why can't you rewrite a single statement to multiple ones? I have noticed that in this case I can cascade the message send - and this then rewrites properly - so it looks like it wants to replace a single node with a single node.

If I couldn't cascade - It seems I would have to rewrite with something like: [ "put multiple expressions here" ] value.

This seems an odd choice - but am I missing something obvious?

Do you have to do what I'm proposing in 2 steps - rewrite to a block (or some equivalent) and then a 2nd rewrite to simplify the expression  (like a refactor step) - so its more provably correct?

I'm just curious on this - as it seemed time to learn this properly?

Tim

I've never really learned the rewrite tool but while waiting for changes to propagate through P12, I thought maybe I could use it to safely patch changes until there are official updates (and it was a good chance to get more familiar with it). I thought there was more documentation available for how it works (interested in any old articles - but the online help in P12 is ok - although it doesn't display in markdown - in fact give there is markdown support now - I am a bit confused what should appear more readable these days - and how to fix/contribute changes to help). This aside - I wanted to patch the SystemWindow>>taskbarButtonMenu: (and add my fix to move tabs to any position). I started writing the following search expression - the find the last call to #addToggle:... method call that has a literal referencing #canBeMovedRight - as I want to add my new menu item after it. e.g. moveSubmenu `@addToggle: `{ :node | node isLiteralNode and: [ node value = #canBeMovedToRight] } but I'm not sure my {} filter block is ever called, as the above matches both addToggle:... calls, and not the one I'm after? I don't understand this - any have ideas on the proper syntax for generic matching? Anyway - I decided that in my case there is only one - so I can match it explicitly and so I search on the full call: and I entered this: moveSubmenu addToggle: 'Move right' translated target: self selector: #taskbarMoveRight getStateSelector: nil enablementSelector: #canBeMovedToRight. Which matches exactly - then I want to replace the single statement with 2 statements e.g. moveSubmenu addToggle: 'Move right' translated target: self selector: #taskbarMoveRight getStateSelector: nil enablementSelector: #canBeMovedToRight. moveSubmenu addToggle: 'Move to' translated target: self selector: #taskbarMoveTo getStateSelector: nil. However this gives an error in the rewrite tool (actually it results in a zero change, that then gives an error for EpMonitor). But my question is - why can't you rewrite a single statement to multiple ones? I have noticed that in this case I can cascade the message send - and this then rewrites properly - so it looks like it wants to replace a single node with a single node. If I couldn't cascade - It seems I would have to rewrite with something like: [ "put multiple expressions here" ] value. This seems an odd choice - but am I missing something obvious? Do you have to do what I'm proposing in 2 steps - rewrite to a block (or some equivalent) and then a 2nd rewrite to simplify the expression (like a refactor step) - so its more provably correct? I'm just curious on this - as it seemed time to learn this properly? Tim
GC
Gabriel Cotelli
Mon, Jun 3, 2024 11:19 AM

I've used the rewrite tool extensively but never tried to create more than
one expression from the original one. I think that this probably won't work
because in your example the code is just statements, but the matching
expression can be in code like this:

self doSomething: (
moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight)

and in this case, you cannot replace this with two statements. So I'm
tempted to say that what you want is not supported.

Regards, Gabriel

On Mon, Jun 3, 2024 at 5:44 AM Tim Mackinnon tim@testit.works wrote:

I've never really learned the rewrite tool but while waiting for changes
to propagate through P12, I thought maybe I could use it to safely patch
changes until there are official updates (and it was a good chance to get
more familiar with it).

I thought there was more documentation available for how it works
(interested in any old articles - but the online help in P12 is ok -
although it doesn't display in markdown - in fact give there is markdown
support now - I am a bit confused what should appear more readable these
days - and how to fix/contribute changes to help).

This aside - I wanted to patch the SystemWindow>>taskbarButtonMenu: (and
add my fix to move tabs to any position).

I started writing the following search expression - the find the last call
to #addToggle:... method call that has a literal referencing
#canBeMovedRight - as I want to add my new menu item after it.

e.g.

moveSubmenu @addToggle: { :node | node isLiteralNode and: [ node value =
#canBeMovedToRight] }

but I'm not sure my {} filter block is ever called, as the above matches
both addToggle:... calls, and not the one I'm after? I don't understand
this - any have ideas on the proper syntax for generic matching?

Anyway - I decided that in my case there is only one - so I can match it
explicitly and so I  search on the full call:

and I entered this:
moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight.

Which matches exactly - then I want to replace the single statement with 2
statements e.g.

moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight.

moveSubmenu
addToggle: 'Move to' translated
target: self
selector: #taskbarMoveTo
getStateSelector: nil.

However this gives an error in the rewrite tool (actually it results in a
zero change, that then gives an error for EpMonitor).

But my question is - why can't you rewrite a single statement to multiple
ones? I have noticed that in this case I can cascade the message send - and
this then rewrites properly - so it looks like it wants to replace a single
node with a single node.

If I couldn't cascade - It seems I would have to rewrite with something
like: [ "put multiple expressions here" ] value.

This seems an odd choice - but am I missing something obvious?

Do you have to do what I'm proposing in 2 steps - rewrite to a block (or
some equivalent) and then a 2nd rewrite to simplify the expression  (like a
refactor step) - so its more provably correct?

I'm just curious on this - as it seemed time to learn this properly?

Tim

I've used the rewrite tool extensively but never tried to create more than one expression from the original one. I think that this probably won't work because in your example the code is just statements, but the matching expression can be in code like this: self doSomething: ( moveSubmenu addToggle: 'Move right' translated target: self selector: #taskbarMoveRight getStateSelector: nil enablementSelector: #canBeMovedToRight) and in this case, you cannot replace this with two statements. So I'm tempted to say that what you want is not supported. Regards, Gabriel On Mon, Jun 3, 2024 at 5:44 AM Tim Mackinnon <tim@testit.works> wrote: > I've never really learned the rewrite tool but while waiting for changes > to propagate through P12, I thought maybe I could use it to safely patch > changes until there are official updates (and it was a good chance to get > more familiar with it). > > I thought there was more documentation available for how it works > (interested in any old articles - but the online help in P12 is ok - > although it doesn't display in markdown - in fact give there is markdown > support now - I am a bit confused what should appear more readable these > days - and how to fix/contribute changes to help). > > This aside - I wanted to patch the SystemWindow>>taskbarButtonMenu: (and > add my fix to move tabs to any position). > > I started writing the following search expression - the find the last call > to #addToggle:... method call that has a literal referencing > #canBeMovedRight - as I want to add my new menu item after it. > > e.g. > > moveSubmenu `@addToggle: `{ :node | node isLiteralNode and: [ node value = > #canBeMovedToRight] } > > but I'm not sure my {} filter block is ever called, as the above matches > both addToggle:... calls, and not the one I'm after? I don't understand > this - any have ideas on the proper syntax for generic matching? > > Anyway - I decided that in my case there is only one - so I can match it > explicitly and so I search on the full call: > > and I entered this: > moveSubmenu > addToggle: 'Move right' translated > target: self > selector: #taskbarMoveRight > getStateSelector: nil > enablementSelector: #canBeMovedToRight. > > Which matches exactly - then I want to replace the single statement with 2 > statements e.g. > > moveSubmenu > addToggle: 'Move right' translated > target: self > selector: #taskbarMoveRight > getStateSelector: nil > enablementSelector: #canBeMovedToRight. > > moveSubmenu > addToggle: 'Move to' translated > target: self > selector: #taskbarMoveTo > getStateSelector: nil. > > > However this gives an error in the rewrite tool (actually it results in a > zero change, that then gives an error for EpMonitor). > > But my question is - why can't you rewrite a single statement to multiple > ones? I have noticed that in this case I can cascade the message send - and > this then rewrites properly - so it looks like it wants to replace a single > node with a single node. > > If I couldn't cascade - It seems I would have to rewrite with something > like: [ "put multiple expressions here" ] value. > > This seems an odd choice - but am I missing something obvious? > > Do you have to do what I'm proposing in 2 steps - rewrite to a block (or > some equivalent) and then a 2nd rewrite to simplify the expression (like a > refactor step) - so its more provably correct? > > I'm just curious on this - as it seemed time to learn this properly? > > Tim >
TM
Tim Mackinnon
Mon, Jun 3, 2024 11:35 PM

Hey Gabriel - thanks for chipping in - you've made me realise that its perhaps easier to give a simpler example (for the multiple replacement)., and while trying to explain in simpler terms I managed to solve the problem myself - but it does demonstrate the docs around this need a bit more work as I didn't find it easy to reason on.

I am still confused what the setting  "this rule is for a method" does - so am keen for someone to shed light.

Anyway for my example it seems that I have to be very specific in how I match things for example I had to use the search pattern:

taskbarButtonMenu: arg | @temps |
`.@before.

moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight.

`.@after

And it took me ages to realise that I have to specify the temp vars matching, and also understand that the @before and @after both needed the "." to represent lists of expressions both before and after and then the really subtle  "." after @before which I guess prevents greedy matching.

With all of that in place,  could then have the write rule add an extra statement after the moveSubmenu (or as many as I want)

e.g.

taskbarButtonMenu: arg | @temps |
`.@before.

moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight.

moveSubmenu
addToggle: 'Move to...' translated
target: self
selector: #taskbarMoveTo
getStateSelector: nil.

`.@after

I'm curious now whether there is a more efficient way to do what I have done? Possibly the {} syntax could help now i've figured out the basic matching.

I really find it easy to confuse that if you don't specify the @temps section that can leave out a lot of matches (it essentialy means 0 or more temps vs. leaving it out means no temps at all). This is a trap that the docs should call out better I think.

Anyway - now I just need to figure out how to do this programatically - and possibly I can write a few more useful refactorings I've also thought it was odd that were missing.

On Mon, 3 Jun 2024, at 12:19 PM, Gabriel Cotelli wrote:

I've used the rewrite tool extensively but never tried to create more than one expression from the original one. I think that this probably won't work because in your example the code is just statements, but the matching expression can be in code like this:

self doSomething: (
moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight)

and in this case, you cannot replace this with two statements. So I'm tempted to say that what you want is not supported.

Regards, Gabriel

On Mon, Jun 3, 2024 at 5:44 AM Tim Mackinnon tim@testit.works wrote:

__
I've never really learned the rewrite tool but while waiting for changes to propagate through P12, I thought maybe I could use it to safely patch changes until there are official updates (and it was a good chance to get more familiar with it).

I thought there was more documentation available for how it works (interested in any old articles - but the online help in P12 is ok - although it doesn't display in markdown - in fact give there is markdown support now - I am a bit confused what should appear more readable these days - and how to fix/contribute changes to help).

This aside - I wanted to patch the SystemWindow>>taskbarButtonMenu: (and add my fix to move tabs to any position).

I started writing the following search expression - the find the last call to #addToggle:... method call that has a literal referencing #canBeMovedRight - as I want to add my new menu item after it.

e.g.

moveSubmenu @addToggle: { :node | node isLiteralNode and: [ node value = #canBeMovedToRight] }

but I'm not sure my {} filter block is ever called, as the above matches both addToggle:... calls, and not the one I'm after? I don't understand this - any have ideas on the proper syntax for generic matching?

Anyway - I decided that in my case there is only one - so I can match it explicitly and so I  search on the full call:

and I entered this:
moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight.

Which matches exactly - then I want to replace the single statement with 2 statements e.g.

moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight.

moveSubmenu
addToggle: 'Move to' translated
target: self
selector: #taskbarMoveTo
getStateSelector: nil.

However this gives an error in the rewrite tool (actually it results in a zero change, that then gives an error for EpMonitor).

But my question is - why can't you rewrite a single statement to multiple ones? I have noticed that in this case I can cascade the message send - and this then rewrites properly - so it looks like it wants to replace a single node with a single node.

If I couldn't cascade - It seems I would have to rewrite with something like: [ "put multiple expressions here" ] value.

This seems an odd choice - but am I missing something obvious?

Do you have to do what I'm proposing in 2 steps - rewrite to a block (or some equivalent) and then a 2nd rewrite to simplify the expression  (like a refactor step) - so its more provably correct?

I'm just curious on this - as it seemed time to learn this properly?

Tim

Hey Gabriel - thanks for chipping in - you've made me realise that its perhaps easier to give a simpler example (for the multiple replacement)., and while trying to explain in simpler terms I managed to solve the problem myself - but it does demonstrate the docs around this need a bit more work as I didn't find it easy to reason on. I am still confused what the setting "this rule is for a method" does - so am keen for someone to shed light. Anyway for my example it seems that I have to be very specific in how I match things for example I had to use the search pattern: taskbarButtonMenu: `arg | `@temps | `.@before. moveSubmenu addToggle: 'Move right' translated target: self selector: #taskbarMoveRight getStateSelector: nil enablementSelector: #canBeMovedToRight. `.@after And it took me ages to realise that I have to specify the temp vars matching, and also understand that the @before and @after both needed the "." to represent lists of expressions both before and after and then the really subtle "." after @before which I guess prevents greedy matching. With all of that in place, could then have the write rule add an extra statement after the moveSubmenu (or as many as I want) e.g. taskbarButtonMenu: `arg | `@temps | `.@before. moveSubmenu addToggle: 'Move right' translated target: self selector: #taskbarMoveRight getStateSelector: nil enablementSelector: #canBeMovedToRight. moveSubmenu addToggle: 'Move to...' translated target: self selector: #taskbarMoveTo getStateSelector: nil. `.@after I'm curious now whether there is a more efficient way to do what I have done? Possibly the {} syntax could help now i've figured out the basic matching. I really find it easy to confuse that if you don't specify the @temps section that can leave out a lot of matches (it essentialy means 0 or more temps vs. leaving it out means no temps at all). This is a trap that the docs should call out better I think. Anyway - now I just need to figure out how to do this programatically - and possibly I can write a few more useful refactorings I've also thought it was odd that were missing. On Mon, 3 Jun 2024, at 12:19 PM, Gabriel Cotelli wrote: > I've used the rewrite tool extensively but never tried to create more than one expression from the original one. I think that this probably won't work because in your example the code is just statements, but the matching expression can be in code like this: > > self doSomething: ( > moveSubmenu > addToggle: 'Move right' translated > target: self > selector: #taskbarMoveRight > getStateSelector: nil > enablementSelector: #canBeMovedToRight) > > and in this case, you cannot replace this with two statements. So I'm tempted to say that what you want is not supported. > > Regards, Gabriel > > On Mon, Jun 3, 2024 at 5:44 AM Tim Mackinnon <tim@testit.works> wrote: >> __ >> I've never really learned the rewrite tool but while waiting for changes to propagate through P12, I thought maybe I could use it to safely patch changes until there are official updates (and it was a good chance to get more familiar with it). >> >> I thought there was more documentation available for how it works (interested in any old articles - but the online help in P12 is ok - although it doesn't display in markdown - in fact give there is markdown support now - I am a bit confused what should appear more readable these days - and how to fix/contribute changes to help). >> >> This aside - I wanted to patch the SystemWindow>>taskbarButtonMenu: (and add my fix to move tabs to any position). >> >> I started writing the following search expression - the find the last call to #addToggle:... method call that has a literal referencing #canBeMovedRight - as I want to add my new menu item after it. >> >> e.g. >> >> moveSubmenu `@addToggle: `{ :node | node isLiteralNode and: [ node value = #canBeMovedToRight] } >> >> but I'm not sure my {} filter block is ever called, as the above matches both addToggle:... calls, and not the one I'm after? I don't understand this - any have ideas on the proper syntax for generic matching? >> >> Anyway - I decided that in my case there is only one - so I can match it explicitly and so I search on the full call: >> >> and I entered this: >> moveSubmenu >> addToggle: 'Move right' translated >> target: self >> selector: #taskbarMoveRight >> getStateSelector: nil >> enablementSelector: #canBeMovedToRight. >> >> Which matches exactly - then I want to replace the single statement with 2 statements e.g. >> >> moveSubmenu >> addToggle: 'Move right' translated >> target: self >> selector: #taskbarMoveRight >> getStateSelector: nil >> enablementSelector: #canBeMovedToRight. >> >> moveSubmenu >> addToggle: 'Move to' translated >> target: self >> selector: #taskbarMoveTo >> getStateSelector: nil. >> >> >> However this gives an error in the rewrite tool (actually it results in a zero change, that then gives an error for EpMonitor). >> >> But my question is - why can't you rewrite a single statement to multiple ones? I have noticed that in this case I can cascade the message send - and this then rewrites properly - so it looks like it wants to replace a single node with a single node. >> >> If I couldn't cascade - It seems I would have to rewrite with something like: [ "put multiple expressions here" ] value. >> >> This seems an odd choice - but am I missing something obvious? >> >> Do you have to do what I'm proposing in 2 steps - rewrite to a block (or some equivalent) and then a 2nd rewrite to simplify the expression (like a refactor step) - so its more provably correct? >> >> I'm just curious on this - as it seemed time to learn this properly? >> >> Tim
SD
stephane ducasse
Wed, Jun 5, 2024 5:35 AM

Hi Tim

This is on my todo to have a serious look at the pattern matcher
Now one of the few documentation that we have in the
blog and I ported it in the blog post in /doc folder.

S

On 4 Jun 2024, at 01:35, Tim Mackinnon tim@testit.works wrote:

Hey Gabriel - thanks for chipping in - you've made me realise that its perhaps easier to give a simpler example (for the multiple replacement)., and while trying to explain in simpler terms I managed to solve the problem myself - but it does demonstrate the docs around this need a bit more work as I didn't find it easy to reason on.

I am still confused what the setting  "this rule is for a method" does - so am keen for someone to shed light.

Anyway for my example it seems that I have to be very specific in how I match things for example I had to use the search pattern:

taskbarButtonMenu: arg | @temps |
`.@before.

moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight.

`.@after

And it took me ages to realise that I have to specify the temp vars matching, and also understand that the @before and @after both needed the "." to represent lists of expressions both before and after and then the really subtle  "." after @before which I guess prevents greedy matching.

With all of that in place,  could then have the write rule add an extra statement after the moveSubmenu (or as many as I want)

e.g.

taskbarButtonMenu: arg | @temps |
`.@before.

moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight.

moveSubmenu
addToggle: 'Move to...' translated
target: self
selector: #taskbarMoveTo
getStateSelector: nil.

`.@after

I'm curious now whether there is a more efficient way to do what I have done? Possibly the {} syntax could help now i've figured out the basic matching.

I really find it easy to confuse that if you don't specify the @temps section that can leave out a lot of matches (it essentialy means 0 or more temps vs. leaving it out means no temps at all). This is a trap that the docs should call out better I think.

Anyway - now I just need to figure out how to do this programatically - and possibly I can write a few more useful refactorings I've also thought it was odd that were missing.

On Mon, 3 Jun 2024, at 12:19 PM, Gabriel Cotelli wrote:

I've used the rewrite tool extensively but never tried to create more than one expression from the original one. I think that this probably won't work because in your example the code is just statements, but the matching expression can be in code like this:

self doSomething: (
moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight)

and in this case, you cannot replace this with two statements. So I'm tempted to say that what you want is not supported.

Regards, Gabriel

On Mon, Jun 3, 2024 at 5:44 AM Tim Mackinnon tim@testit.works wrote:

I've never really learned the rewrite tool but while waiting for changes to propagate through P12, I thought maybe I could use it to safely patch changes until there are official updates (and it was a good chance to get more familiar with it).

I thought there was more documentation available for how it works (interested in any old articles - but the online help in P12 is ok - although it doesn't display in markdown - in fact give there is markdown support now - I am a bit confused what should appear more readable these days - and how to fix/contribute changes to help).

This aside - I wanted to patch the SystemWindow>>taskbarButtonMenu: (and add my fix to move tabs to any position).

I started writing the following search expression - the find the last call to #addToggle:... method call that has a literal referencing #canBeMovedRight - as I want to add my new menu item after it.

e.g.

moveSubmenu @addToggle: { :node | node isLiteralNode and: [ node value = #canBeMovedToRight] }

but I'm not sure my {} filter block is ever called, as the above matches both addToggle:... calls, and not the one I'm after? I don't understand this - any have ideas on the proper syntax for generic matching?

Anyway - I decided that in my case there is only one - so I can match it explicitly and so I  search on the full call:

and I entered this:
moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight.

Which matches exactly - then I want to replace the single statement with 2 statements e.g.

moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight.

moveSubmenu
addToggle: 'Move to' translated
target: self
selector: #taskbarMoveTo
getStateSelector: nil.

However this gives an error in the rewrite tool (actually it results in a zero change, that then gives an error for EpMonitor).

But my question is - why can't you rewrite a single statement to multiple ones? I have noticed that in this case I can cascade the message send - and this then rewrites properly - so it looks like it wants to replace a single node with a single node.

If I couldn't cascade - It seems I would have to rewrite with something like: [ "put multiple expressions here" ] value.

This seems an odd choice - but am I missing something obvious?

Do you have to do what I'm proposing in 2 steps - rewrite to a block (or some equivalent) and then a 2nd rewrite to simplify the expression  (like a refactor step) - so its more provably correct?

I'm just curious on this - as it seemed time to learn this properly?

Tim

Stéphane Ducasse
http://stephane.ducasse.free.fr
06 30 93 66 73

"If you knew today was your last day on earth, what would you do differently? ....ESPECIALLY if, by doing something different, today might not be your last day on earth.” Calvin & Hobbes

Hi Tim This is on my todo to have a serious look at the pattern matcher Now one of the few documentation that we have in the blog and I ported it in the blog post in /doc folder. S > On 4 Jun 2024, at 01:35, Tim Mackinnon <tim@testit.works> wrote: > > Hey Gabriel - thanks for chipping in - you've made me realise that its perhaps easier to give a simpler example (for the multiple replacement)., and while trying to explain in simpler terms I managed to solve the problem myself - but it does demonstrate the docs around this need a bit more work as I didn't find it easy to reason on. > > I am still confused what the setting "this rule is for a method" does - so am keen for someone to shed light. > > Anyway for my example it seems that I have to be very specific in how I match things for example I had to use the search pattern: > > taskbarButtonMenu: `arg > | `@temps | > `.@before. > > moveSubmenu > addToggle: 'Move right' translated > target: self > selector: #taskbarMoveRight > getStateSelector: nil > enablementSelector: #canBeMovedToRight. > > `.@after > > And it took me ages to realise that I have to specify the temp vars matching, and also understand that the @before and @after both needed the "." to represent lists of expressions both before and after and then the really subtle "." after @before which I guess prevents greedy matching. > > With all of that in place, could then have the write rule add an extra statement after the moveSubmenu (or as many as I want) > > e.g. > > taskbarButtonMenu: `arg > | `@temps | > `.@before. > > moveSubmenu > addToggle: 'Move right' translated > target: self > selector: #taskbarMoveRight > getStateSelector: nil > enablementSelector: #canBeMovedToRight. > > moveSubmenu > addToggle: 'Move to...' translated > target: self > selector: #taskbarMoveTo > getStateSelector: nil. > > `.@after > > > I'm curious now whether there is a more efficient way to do what I have done? Possibly the {} syntax could help now i've figured out the basic matching. > > I really find it easy to confuse that if you don't specify the @temps section that can leave out a lot of matches (it essentialy means 0 or more temps vs. leaving it out means no temps at all). This is a trap that the docs should call out better I think. > > Anyway - now I just need to figure out how to do this programatically - and possibly I can write a few more useful refactorings I've also thought it was odd that were missing. > > On Mon, 3 Jun 2024, at 12:19 PM, Gabriel Cotelli wrote: >> I've used the rewrite tool extensively but never tried to create more than one expression from the original one. I think that this probably won't work because in your example the code is just statements, but the matching expression can be in code like this: >> >> self doSomething: ( >> moveSubmenu >> addToggle: 'Move right' translated >> target: self >> selector: #taskbarMoveRight >> getStateSelector: nil >> enablementSelector: #canBeMovedToRight) >> >> and in this case, you cannot replace this with two statements. So I'm tempted to say that what you want is not supported. >> >> Regards, Gabriel >> >> On Mon, Jun 3, 2024 at 5:44 AM Tim Mackinnon <tim@testit.works> wrote: >> >> I've never really learned the rewrite tool but while waiting for changes to propagate through P12, I thought maybe I could use it to safely patch changes until there are official updates (and it was a good chance to get more familiar with it). >> >> I thought there was more documentation available for how it works (interested in any old articles - but the online help in P12 is ok - although it doesn't display in markdown - in fact give there is markdown support now - I am a bit confused what should appear more readable these days - and how to fix/contribute changes to help). >> >> This aside - I wanted to patch the SystemWindow>>taskbarButtonMenu: (and add my fix to move tabs to any position). >> >> I started writing the following search expression - the find the last call to #addToggle:... method call that has a literal referencing #canBeMovedRight - as I want to add my new menu item after it. >> >> e.g. >> >> moveSubmenu `@addToggle: `{ :node | node isLiteralNode and: [ node value = #canBeMovedToRight] } >> >> but I'm not sure my {} filter block is ever called, as the above matches both addToggle:... calls, and not the one I'm after? I don't understand this - any have ideas on the proper syntax for generic matching? >> >> Anyway - I decided that in my case there is only one - so I can match it explicitly and so I search on the full call: >> >> and I entered this: >> moveSubmenu >> addToggle: 'Move right' translated >> target: self >> selector: #taskbarMoveRight >> getStateSelector: nil >> enablementSelector: #canBeMovedToRight. >> >> Which matches exactly - then I want to replace the single statement with 2 statements e.g. >> >> moveSubmenu >> addToggle: 'Move right' translated >> target: self >> selector: #taskbarMoveRight >> getStateSelector: nil >> enablementSelector: #canBeMovedToRight. >> >> moveSubmenu >> addToggle: 'Move to' translated >> target: self >> selector: #taskbarMoveTo >> getStateSelector: nil. >> >> >> However this gives an error in the rewrite tool (actually it results in a zero change, that then gives an error for EpMonitor). >> >> But my question is - why can't you rewrite a single statement to multiple ones? I have noticed that in this case I can cascade the message send - and this then rewrites properly - so it looks like it wants to replace a single node with a single node. >> >> If I couldn't cascade - It seems I would have to rewrite with something like: [ "put multiple expressions here" ] value. >> >> This seems an odd choice - but am I missing something obvious? >> >> Do you have to do what I'm proposing in 2 steps - rewrite to a block (or some equivalent) and then a 2nd rewrite to simplify the expression (like a refactor step) - so its more provably correct? >> >> I'm just curious on this - as it seemed time to learn this properly? >> >> Tim Stéphane Ducasse http://stephane.ducasse.free.fr 06 30 93 66 73 "If you knew today was your last day on earth, what would you do differently? ....ESPECIALLY if, by doing something different, today might not be your last day on earth.” Calvin & Hobbes
SD
stephane ducasse
Wed, Jun 5, 2024 5:36 AM

Hi Tim

This is on my todo to have a serious look at the pattern matcher
Now one of the few documentation that we have in the
blog and I ported it in the blog post in /doc folder.

S

On 4 Jun 2024, at 01:35, Tim Mackinnon tim@testit.works wrote:

Hey Gabriel - thanks for chipping in - you've made me realise that its perhaps easier to give a simpler example (for the multiple replacement)., and while trying to explain in simpler terms I managed to solve the problem myself - but it does demonstrate the docs around this need a bit more work as I didn't find it easy to reason on.

I am still confused what the setting  "this rule is for a method" does - so am keen for someone to shed light.

Anyway for my example it seems that I have to be very specific in how I match things for example I had to use the search pattern:

taskbarButtonMenu: arg | @temps |
`.@before.

moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight.

`.@after

And it took me ages to realise that I have to specify the temp vars matching, and also understand that the @before and @after both needed the "." to represent lists of expressions both before and after and then the really subtle  "." after @before which I guess prevents greedy matching.

With all of that in place,  could then have the write rule add an extra statement after the moveSubmenu (or as many as I want)

e.g.

taskbarButtonMenu: arg | @temps |
`.@before.

moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight.

moveSubmenu
addToggle: 'Move to...' translated
target: self
selector: #taskbarMoveTo
getStateSelector: nil.

`.@after

I'm curious now whether there is a more efficient way to do what I have done? Possibly the {} syntax could help now i've figured out the basic matching.

I really find it easy to confuse that if you don't specify the @temps section that can leave out a lot of matches (it essentialy means 0 or more temps vs. leaving it out means no temps at all). This is a trap that the docs should call out better I think.

Anyway - now I just need to figure out how to do this programatically - and possibly I can write a few more useful refactorings I've also thought it was odd that were missing.

On Mon, 3 Jun 2024, at 12:19 PM, Gabriel Cotelli wrote:

I've used the rewrite tool extensively but never tried to create more than one expression from the original one. I think that this probably won't work because in your example the code is just statements, but the matching expression can be in code like this:

self doSomething: (
moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight)

and in this case, you cannot replace this with two statements. So I'm tempted to say that what you want is not supported.

Regards, Gabriel

On Mon, Jun 3, 2024 at 5:44 AM Tim Mackinnon tim@testit.works wrote:

I've never really learned the rewrite tool but while waiting for changes to propagate through P12, I thought maybe I could use it to safely patch changes until there are official updates (and it was a good chance to get more familiar with it).

I thought there was more documentation available for how it works (interested in any old articles - but the online help in P12 is ok - although it doesn't display in markdown - in fact give there is markdown support now - I am a bit confused what should appear more readable these days - and how to fix/contribute changes to help).

This aside - I wanted to patch the SystemWindow>>taskbarButtonMenu: (and add my fix to move tabs to any position).

I started writing the following search expression - the find the last call to #addToggle:... method call that has a literal referencing #canBeMovedRight - as I want to add my new menu item after it.

e.g.

moveSubmenu @addToggle: { :node | node isLiteralNode and: [ node value = #canBeMovedToRight] }

but I'm not sure my {} filter block is ever called, as the above matches both addToggle:... calls, and not the one I'm after? I don't understand this - any have ideas on the proper syntax for generic matching?

Anyway - I decided that in my case there is only one - so I can match it explicitly and so I  search on the full call:

and I entered this:
moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight.

Which matches exactly - then I want to replace the single statement with 2 statements e.g.

moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight.

moveSubmenu
addToggle: 'Move to' translated
target: self
selector: #taskbarMoveTo
getStateSelector: nil.

However this gives an error in the rewrite tool (actually it results in a zero change, that then gives an error for EpMonitor).

But my question is - why can't you rewrite a single statement to multiple ones? I have noticed that in this case I can cascade the message send - and this then rewrites properly - so it looks like it wants to replace a single node with a single node.

If I couldn't cascade - It seems I would have to rewrite with something like: [ "put multiple expressions here" ] value.

This seems an odd choice - but am I missing something obvious?

Do you have to do what I'm proposing in 2 steps - rewrite to a block (or some equivalent) and then a 2nd rewrite to simplify the expression  (like a refactor step) - so its more provably correct?

I'm just curious on this - as it seemed time to learn this properly?

Tim

Stéphane Ducasse
http://stephane.ducasse.free.fr
06 30 93 66 73

"If you knew today was your last day on earth, what would you do differently? ....ESPECIALLY if, by doing something different, today might not be your last day on earth.” Calvin & Hobbes

Hi Tim This is on my todo to have a serious look at the pattern matcher Now one of the few documentation that we have in the blog and I ported it in the blog post in /doc folder. S > On 4 Jun 2024, at 01:35, Tim Mackinnon <tim@testit.works> wrote: > > Hey Gabriel - thanks for chipping in - you've made me realise that its perhaps easier to give a simpler example (for the multiple replacement)., and while trying to explain in simpler terms I managed to solve the problem myself - but it does demonstrate the docs around this need a bit more work as I didn't find it easy to reason on. > > I am still confused what the setting "this rule is for a method" does - so am keen for someone to shed light. > > Anyway for my example it seems that I have to be very specific in how I match things for example I had to use the search pattern: > > taskbarButtonMenu: `arg > | `@temps | > `.@before. > > moveSubmenu > addToggle: 'Move right' translated > target: self > selector: #taskbarMoveRight > getStateSelector: nil > enablementSelector: #canBeMovedToRight. > > `.@after > > And it took me ages to realise that I have to specify the temp vars matching, and also understand that the @before and @after both needed the "." to represent lists of expressions both before and after and then the really subtle "." after @before which I guess prevents greedy matching. > > With all of that in place, could then have the write rule add an extra statement after the moveSubmenu (or as many as I want) > > e.g. > > taskbarButtonMenu: `arg > | `@temps | > `.@before. > > moveSubmenu > addToggle: 'Move right' translated > target: self > selector: #taskbarMoveRight > getStateSelector: nil > enablementSelector: #canBeMovedToRight. > > moveSubmenu > addToggle: 'Move to...' translated > target: self > selector: #taskbarMoveTo > getStateSelector: nil. > > `.@after > > > I'm curious now whether there is a more efficient way to do what I have done? Possibly the {} syntax could help now i've figured out the basic matching. > > I really find it easy to confuse that if you don't specify the @temps section that can leave out a lot of matches (it essentialy means 0 or more temps vs. leaving it out means no temps at all). This is a trap that the docs should call out better I think. > > Anyway - now I just need to figure out how to do this programatically - and possibly I can write a few more useful refactorings I've also thought it was odd that were missing. > > On Mon, 3 Jun 2024, at 12:19 PM, Gabriel Cotelli wrote: >> I've used the rewrite tool extensively but never tried to create more than one expression from the original one. I think that this probably won't work because in your example the code is just statements, but the matching expression can be in code like this: >> >> self doSomething: ( >> moveSubmenu >> addToggle: 'Move right' translated >> target: self >> selector: #taskbarMoveRight >> getStateSelector: nil >> enablementSelector: #canBeMovedToRight) >> >> and in this case, you cannot replace this with two statements. So I'm tempted to say that what you want is not supported. >> >> Regards, Gabriel >> >> On Mon, Jun 3, 2024 at 5:44 AM Tim Mackinnon <tim@testit.works> wrote: >> >> I've never really learned the rewrite tool but while waiting for changes to propagate through P12, I thought maybe I could use it to safely patch changes until there are official updates (and it was a good chance to get more familiar with it). >> >> I thought there was more documentation available for how it works (interested in any old articles - but the online help in P12 is ok - although it doesn't display in markdown - in fact give there is markdown support now - I am a bit confused what should appear more readable these days - and how to fix/contribute changes to help). >> >> This aside - I wanted to patch the SystemWindow>>taskbarButtonMenu: (and add my fix to move tabs to any position). >> >> I started writing the following search expression - the find the last call to #addToggle:... method call that has a literal referencing #canBeMovedRight - as I want to add my new menu item after it. >> >> e.g. >> >> moveSubmenu `@addToggle: `{ :node | node isLiteralNode and: [ node value = #canBeMovedToRight] } >> >> but I'm not sure my {} filter block is ever called, as the above matches both addToggle:... calls, and not the one I'm after? I don't understand this - any have ideas on the proper syntax for generic matching? >> >> Anyway - I decided that in my case there is only one - so I can match it explicitly and so I search on the full call: >> >> and I entered this: >> moveSubmenu >> addToggle: 'Move right' translated >> target: self >> selector: #taskbarMoveRight >> getStateSelector: nil >> enablementSelector: #canBeMovedToRight. >> >> Which matches exactly - then I want to replace the single statement with 2 statements e.g. >> >> moveSubmenu >> addToggle: 'Move right' translated >> target: self >> selector: #taskbarMoveRight >> getStateSelector: nil >> enablementSelector: #canBeMovedToRight. >> >> moveSubmenu >> addToggle: 'Move to' translated >> target: self >> selector: #taskbarMoveTo >> getStateSelector: nil. >> >> >> However this gives an error in the rewrite tool (actually it results in a zero change, that then gives an error for EpMonitor). >> >> But my question is - why can't you rewrite a single statement to multiple ones? I have noticed that in this case I can cascade the message send - and this then rewrites properly - so it looks like it wants to replace a single node with a single node. >> >> If I couldn't cascade - It seems I would have to rewrite with something like: [ "put multiple expressions here" ] value. >> >> This seems an odd choice - but am I missing something obvious? >> >> Do you have to do what I'm proposing in 2 steps - rewrite to a block (or some equivalent) and then a 2nd rewrite to simplify the expression (like a refactor step) - so its more provably correct? >> >> I'm just curious on this - as it seemed time to learn this properly? >> >> Tim Stéphane Ducasse http://stephane.ducasse.free.fr 06 30 93 66 73 "If you knew today was your last day on earth, what would you do differently? ....ESPECIALLY if, by doing something different, today might not be your last day on earth.” Calvin & Hobbes
TM
Tim Mackinnon
Wed, Jun 5, 2024 8:22 AM

Hi Stef - I saw that blog post - which looks like its essentially the help text that comes up in the rewrite tool when you press the help button. I was going to submit to add to the help text as I think a better example that pulls all the ideas together is helpful. However, I have realised the Rewrite tool is not the main pharo-project repo and comes under NewTools? How do I find out where that repo is to submit a pr?

I think the help button on that tool should launch the normal doc browser with the text in the normal help format vs a seperate window? The help browser could probably use some work but at least if help is one place it all will get updated together?

I was going to propose the following example which helped me a lot -  if I wanted to rewrite how logging was done in the following example (and separate the Transcript line into 2 lines):

appLog: anObject
"Log anObject for debugging"
| date |
date := Date today.
Transcript traceCr: 'debug: ', date printString, anObject printString.
date isLeapYear ifTrue: [ ^nil ].
^anObject

I could write a match expression like this:

appLog: arg | @temps |
.@before. Transcript traceCr: #str , @exp1, @exp2.
`.@after

and a rewrite rule like this:

appLog: arg | @temps |
.@before. Transcript cr; traceCr: #str, @exp1. arg traceCr.
`.@after

I think this demonstrates most of the concepts that caught me out - namely: you need to call out the variable matching explicitly -= the .@before (list statement matching doesn't match variables - and using @temps will match 0 or more vars, so its safer to include it if matching expressions that may have any otherwise nothing will match.

If you want to match a string you need to match it as a literal e.g. `#str.

You may need to match 0 or more statements before and after your target expression hence  .@before and .@after but its important to include the trailing "." on the @before.  to cut off before the expression you are trying to match next.

I didn't fully understand in my example is why I can't match the remaining statements after the transcript string using just `.@exp1. I seem to have to call out both expressions which I now realise (and worth documenting) is because the AST for that line is actually VariableNode(transcript) + MsgNode(MsgNode(Literal+MsgNode(exp1)), MsgNode(exp2) and so you have to call them all out to match things or match the entire sequence with @stmnt (in my case I wanted to pick them apart).  I think the top tip in all of this is to use the handy source code ast inspector to help you reason on the structure.

The above is probably the gist of the kinds of docs we would need, and happy to submit a pr with this in it.

On Wed, 5 Jun 2024, at 6:35 AM, stephane ducasse wrote:

Hi Tim

This is on my todo to have a serious look at the pattern matcher
Now one of the few documentation that we have in the
blog and I ported it in the blog post in /doc folder.

S

On 4 Jun 2024, at 01:35, Tim Mackinnon tim@testit.works wrote:

Hey Gabriel - thanks for chipping in - you've made me realise that its perhaps easier to give a simpler example (for the multiple replacement)., and while trying to explain in simpler terms I managed to solve the problem myself - but it does demonstrate the docs around this need a bit more work as I didn't find it easy to reason on.

I am still confused what the setting  "this rule is for a method" does - so am keen for someone to shed light.

Anyway for my example it seems that I have to be very specific in how I match things for example I had to use the search pattern:

taskbarButtonMenu: arg | @temps |
`.@before.

moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight.

`.@after

And it took me ages to realise that I have to specify the temp vars matching, and also understand that the @before and @after both needed the "." to represent lists of expressions both before and after and then the really subtle  "." after @before which I guess prevents greedy matching.

With all of that in place,  could then have the write rule add an extra statement after the moveSubmenu (or as many as I want)

e.g.

taskbarButtonMenu: arg | @temps |
`.@before.

moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight.

moveSubmenu
addToggle: 'Move to...' translated
target: self
selector: #taskbarMoveTo
getStateSelector: nil.

`.@after

I'm curious now whether there is a more efficient way to do what I have done? Possibly the {} syntax could help now i've figured out the basic matching.

I really find it easy to confuse that if you don't specify the @temps section that can leave out a lot of matches (it essentialy means 0 or more temps vs. leaving it out means no temps at all). This is a trap that the docs should call out better I think.

Anyway - now I just need to figure out how to do this programatically - and possibly I can write a few more useful refactorings I've also thought it was odd that were missing.

On Mon, 3 Jun 2024, at 12:19 PM, Gabriel Cotelli wrote:

I've used the rewrite tool extensively but never tried to create more than one expression from the original one. I think that this probably won't work because in your example the code is just statements, but the matching expression can be in code like this:

self doSomething: (
moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight)

and in this case, you cannot replace this with two statements. So I'm tempted to say that what you want is not supported.

Regards, Gabriel

On Mon, Jun 3, 2024 at 5:44 AM Tim Mackinnon tim@testit.works wrote:

__
I've never really learned the rewrite tool but while waiting for changes to propagate through P12, I thought maybe I could use it to safely patch changes until there are official updates (and it was a good chance to get more familiar with it).

I thought there was more documentation available for how it works (interested in any old articles - but the online help in P12 is ok - although it doesn't display in markdown - in fact give there is markdown support now - I am a bit confused what should appear more readable these days - and how to fix/contribute changes to help).

This aside - I wanted to patch the SystemWindow>>taskbarButtonMenu: (and add my fix to move tabs to any position).

I started writing the following search expression - the find the last call to #addToggle:... method call that has a literal referencing #canBeMovedRight - as I want to add my new menu item after it.

e.g.

moveSubmenu @addToggle: { :node | node isLiteralNode and: [ node value = #canBeMovedToRight] }

but I'm not sure my {} filter block is ever called, as the above matches both addToggle:... calls, and not the one I'm after? I don't understand this - any have ideas on the proper syntax for generic matching?

Anyway - I decided that in my case there is only one - so I can match it explicitly and so I  search on the full call:

and I entered this:
moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight.

Which matches exactly - then I want to replace the single statement with 2 statements e.g.

moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight.

moveSubmenu
addToggle: 'Move to' translated
target: self
selector: #taskbarMoveTo
getStateSelector: nil.

However this gives an error in the rewrite tool (actually it results in a zero change, that then gives an error for EpMonitor).

But my question is - why can't you rewrite a single statement to multiple ones? I have noticed that in this case I can cascade the message send - and this then rewrites properly - so it looks like it wants to replace a single node with a single node.

If I couldn't cascade - It seems I would have to rewrite with something like: [ "put multiple expressions here" ] value.

This seems an odd choice - but am I missing something obvious?

Do you have to do what I'm proposing in 2 steps - rewrite to a block (or some equivalent) and then a 2nd rewrite to simplify the expression  (like a refactor step) - so its more provably correct?

I'm just curious on this - as it seemed time to learn this properly?

Tim

Stéphane Ducasse
http://stephane.ducasse.free.fr
06 30 93 66 73

"If you knew today was your last day on earth, what would you do differently? ....ESPECIALLY if, by doing something different, today might not be your last day on earth.” Calvin & Hobbes

Hi Stef - I saw that blog post - which looks like its essentially the help text that comes up in the rewrite tool when you press the help button. I was going to submit to add to the help text as I think a better example that pulls all the ideas together is helpful. However, I have realised the Rewrite tool is not the main pharo-project repo and comes under NewTools? How do I find out where that repo is to submit a pr? I think the help button on that tool should launch the normal doc browser with the text in the normal help format vs a seperate window? The help browser could probably use some work but at least if help is one place it all will get updated together? I was going to propose the following example which helped me a lot - if I wanted to rewrite how logging was done in the following example (and separate the Transcript line into 2 lines): appLog: anObject "Log anObject for debugging" | date | date := Date today. Transcript traceCr: 'debug: ', date printString, anObject printString. date isLeapYear ifTrue: [ ^nil ]. ^anObject I could write a match expression like this: appLog: `arg | `@temps | `.@before. Transcript traceCr: `#str , `@exp1, `@exp2. `.@after and a rewrite rule like this: appLog: `arg | `@temps | `.@before. Transcript cr; traceCr: `#str, `@exp1. `arg traceCr. `.@after I think this demonstrates most of the concepts that caught me out - namely: you need to call out the variable matching explicitly -= the .@before (list statement matching doesn't match variables - and using @temps will match 0 or more vars, so its safer to include it if matching expressions that may have any otherwise nothing will match. If you want to match a string you need to match it as a literal e.g. `#str. You may need to match 0 or more statements before and after your target expression hence `.@before and `.@after but its important to include the trailing "." on the @before. to cut off before the expression you are trying to match next. I didn't fully understand in my example is why I can't match the remaining statements after the transcript string using just `.@exp1. I seem to have to call out both expressions which I now realise (and worth documenting) is because the AST for that line is actually VariableNode(transcript) + MsgNode(MsgNode(Literal+MsgNode(exp1)), MsgNode(exp2) and so you have to call them all out to match things or match the entire sequence with @stmnt (in my case I wanted to pick them apart). I think the top tip in all of this is to use the handy source code ast inspector to help you reason on the structure. The above is probably the gist of the kinds of docs we would need, and happy to submit a pr with this in it. On Wed, 5 Jun 2024, at 6:35 AM, stephane ducasse wrote: > Hi Tim > > This is on my todo to have a serious look at the pattern matcher > Now one of the few documentation that we have in the > blog and I ported it in the blog post in /doc folder. > > S > > > > >> On 4 Jun 2024, at 01:35, Tim Mackinnon <tim@testit.works> wrote: >> >> Hey Gabriel - thanks for chipping in - you've made me realise that its perhaps easier to give a simpler example (for the multiple replacement)., and while trying to explain in simpler terms I managed to solve the problem myself - but it does demonstrate the docs around this need a bit more work as I didn't find it easy to reason on. >> >> I am still confused what the setting "this rule is for a method" does - so am keen for someone to shed light. >> >> Anyway for my example it seems that I have to be very specific in how I match things for example I had to use the search pattern: >> >> taskbarButtonMenu: `arg >> | `@temps | >> `.@before. >> >> moveSubmenu >> addToggle: 'Move right' translated >> target: self >> selector: #taskbarMoveRight >> getStateSelector: nil >> enablementSelector: #canBeMovedToRight. >> >> `.@after >> >> And it took me ages to realise that I have to specify the temp vars matching, and also understand that the @before and @after both needed the "." to represent lists of expressions both before and after and then the really subtle "." after @before which I guess prevents greedy matching. >> >> With all of that in place, could then have the write rule add an extra statement after the moveSubmenu (or as many as I want) >> >> e.g. >> >> taskbarButtonMenu: `arg >> | `@temps | >> `.@before. >> >> moveSubmenu >> addToggle: 'Move right' translated >> target: self >> selector: #taskbarMoveRight >> getStateSelector: nil >> enablementSelector: #canBeMovedToRight. >> >> moveSubmenu >> addToggle: 'Move to...' translated >> target: self >> selector: #taskbarMoveTo >> getStateSelector: nil. >> >> `.@after >> >> >> I'm curious now whether there is a more efficient way to do what I have done? Possibly the {} syntax could help now i've figured out the basic matching. >> >> I really find it easy to confuse that if you don't specify the @temps section that can leave out a lot of matches (it essentialy means 0 or more temps vs. leaving it out means no temps at all). This is a trap that the docs should call out better I think. >> >> Anyway - now I just need to figure out how to do this programatically - and possibly I can write a few more useful refactorings I've also thought it was odd that were missing. >> >> On Mon, 3 Jun 2024, at 12:19 PM, Gabriel Cotelli wrote: >>> I've used the rewrite tool extensively but never tried to create more than one expression from the original one. I think that this probably won't work because in your example the code is just statements, but the matching expression can be in code like this: >>> >>> self doSomething: ( >>> moveSubmenu >>> addToggle: 'Move right' translated >>> target: self >>> selector: #taskbarMoveRight >>> getStateSelector: nil >>> enablementSelector: #canBeMovedToRight) >>> >>> and in this case, you cannot replace this with two statements. So I'm tempted to say that what you want is not supported. >>> >>> Regards, Gabriel >>> >>> On Mon, Jun 3, 2024 at 5:44 AM Tim Mackinnon <tim@testit.works> wrote: >>>> __ >>>> I've never really learned the rewrite tool but while waiting for changes to propagate through P12, I thought maybe I could use it to safely patch changes until there are official updates (and it was a good chance to get more familiar with it). >>>> >>>> I thought there was more documentation available for how it works (interested in any old articles - but the online help in P12 is ok - although it doesn't display in markdown - in fact give there is markdown support now - I am a bit confused what should appear more readable these days - and how to fix/contribute changes to help). >>>> >>>> This aside - I wanted to patch the SystemWindow>>taskbarButtonMenu: (and add my fix to move tabs to any position). >>>> >>>> I started writing the following search expression - the find the last call to #addToggle:... method call that has a literal referencing #canBeMovedRight - as I want to add my new menu item after it. >>>> >>>> e.g. >>>> >>>> moveSubmenu `@addToggle: `{ :node | node isLiteralNode and: [ node value = #canBeMovedToRight] } >>>> >>>> but I'm not sure my {} filter block is ever called, as the above matches both addToggle:... calls, and not the one I'm after? I don't understand this - any have ideas on the proper syntax for generic matching? >>>> >>>> Anyway - I decided that in my case there is only one - so I can match it explicitly and so I search on the full call: >>>> >>>> and I entered this: >>>> moveSubmenu >>>> addToggle: 'Move right' translated >>>> target: self >>>> selector: #taskbarMoveRight >>>> getStateSelector: nil >>>> enablementSelector: #canBeMovedToRight. >>>> >>>> Which matches exactly - then I want to replace the single statement with 2 statements e.g. >>>> >>>> moveSubmenu >>>> addToggle: 'Move right' translated >>>> target: self >>>> selector: #taskbarMoveRight >>>> getStateSelector: nil >>>> enablementSelector: #canBeMovedToRight. >>>> >>>> moveSubmenu >>>> addToggle: 'Move to' translated >>>> target: self >>>> selector: #taskbarMoveTo >>>> getStateSelector: nil. >>>> >>>> >>>> However this gives an error in the rewrite tool (actually it results in a zero change, that then gives an error for EpMonitor). >>>> >>>> But my question is - why can't you rewrite a single statement to multiple ones? I have noticed that in this case I can cascade the message send - and this then rewrites properly - so it looks like it wants to replace a single node with a single node. >>>> >>>> If I couldn't cascade - It seems I would have to rewrite with something like: [ "put multiple expressions here" ] value. >>>> >>>> This seems an odd choice - but am I missing something obvious? >>>> >>>> Do you have to do what I'm proposing in 2 steps - rewrite to a block (or some equivalent) and then a 2nd rewrite to simplify the expression (like a refactor step) - so its more provably correct? >>>> >>>> I'm just curious on this - as it seemed time to learn this properly? >>>> >>>> Tim > > Stéphane Ducasse > http://stephane.ducasse.free.fr > 06 30 93 66 73 > > "If you knew today was your last day on earth, what would you do differently? ....ESPECIALLY if, by doing something different, today might not be your last day on earth.” Calvin & Hobbes > >
SD
stephane ducasse
Wed, Jun 5, 2024 7:46 PM

On 5 Jun 2024, at 10:22, Tim Mackinnon tim@testit.works wrote:

Hi Stef - I saw that blog post - which looks like its essentially the help text that comes up in the rewrite tool when you press the help button. I was going to submit to add to the help text as I think a better example that pulls all the ideas together is helpful. However, I have realised the Rewrite tool is not the main pharo-project repo and comes under NewTools? How do I find out where that repo is to submit a pr?

I think the help button on that tool should launch the normal doc browser with the text in the normal help format vs a seperate window? The help browser could probably use some work but at least if help is one place it all will get updated together?

Yes it can be nice to have a single documentation so that when we improve it will benefit directly
to the tools.

I was going to propose the following example which helped me a lot -  if I wanted to rewrite how logging was done in the following example (and separate the Transcript line into 2 lines):

appLog: anObject
"Log anObject for debugging"
| date |
date := Date today.
Transcript traceCr: 'debug: ', date printString, anObject printString.
date isLeapYear ifTrue: [ ^nil ].
^anObject

I could write a match expression like this:

appLog: arg | @temps |
.@before. Transcript traceCr: #str , @exp1, @exp2.
`.@after

and a rewrite rule like this:

appLog: arg | @temps |
.@before. Transcript cr; traceCr: #str, @exp1. arg traceCr.
`.@after

I think this demonstrates most of the concepts that caught me out - namely: you need to call out the variable matching explicitly -= the .@before (list statement matching doesn't match variables - and using @temps will match 0 or more vars, so its safer to include it if matching expressions that may have any otherwise nothing will match.

If you want to match a string you need to match it as a literal e.g. `#str.

You may need to match 0 or more statements before and after your target expression hence  .@before and .@after but its important to include the trailing "." on the @before.  to cut off before the expression you are trying to match next.

I didn't fully understand in my example is why I can't match the remaining statements after the transcript string using just `.@exp1. I seem to have to call out both expressions which I now realise (and worth documenting) is because the AST for that line is actually VariableNode(transcript) + MsgNode(MsgNode(Literal+MsgNode(exp1)), MsgNode(exp2) and so you have to call them all out to match things or match the entire sequence with @stmnt (in my case I wanted to pick them apart).  I think the top tip in all of this is to use the handy source code ast inspector to help you reason on the structure.

Sometimes this is arcane to me.
I think that I would have to concentrate and do a full review of code using tons of new tests :)

The above is probably the gist of the kinds of docs we would need, and happy to submit a pr with this in it.

please do.

On Wed, 5 Jun 2024, at 6:35 AM, stephane ducasse wrote:

Hi Tim

This is on my todo to have a serious look at the pattern matcher
Now one of the few documentation that we have in the
blog and I ported it in the blog post in /doc folder.

S

On 4 Jun 2024, at 01:35, Tim Mackinnon tim@testit.works wrote:

Hey Gabriel - thanks for chipping in - you've made me realise that its perhaps easier to give a simpler example (for the multiple replacement)., and while trying to explain in simpler terms I managed to solve the problem myself - but it does demonstrate the docs around this need a bit more work as I didn't find it easy to reason on.

I am still confused what the setting  "this rule is for a method" does - so am keen for someone to shed light.

Anyway for my example it seems that I have to be very specific in how I match things for example I had to use the search pattern:

taskbarButtonMenu: arg | @temps |
`.@before.

moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight.

`.@after

And it took me ages to realise that I have to specify the temp vars matching, and also understand that the @before and @after both needed the "." to represent lists of expressions both before and after and then the really subtle  "." after @before which I guess prevents greedy matching.

With all of that in place,  could then have the write rule add an extra statement after the moveSubmenu (or as many as I want)

e.g.

taskbarButtonMenu: arg | @temps |
`.@before.

moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight.

moveSubmenu
addToggle: 'Move to...' translated
target: self
selector: #taskbarMoveTo
getStateSelector: nil.

`.@after

I'm curious now whether there is a more efficient way to do what I have done? Possibly the {} syntax could help now i've figured out the basic matching.

I really find it easy to confuse that if you don't specify the @temps section that can leave out a lot of matches (it essentialy means 0 or more temps vs. leaving it out means no temps at all). This is a trap that the docs should call out better I think.

Anyway - now I just need to figure out how to do this programatically - and possibly I can write a few more useful refactorings I've also thought it was odd that were missing.

On Mon, 3 Jun 2024, at 12:19 PM, Gabriel Cotelli wrote:

I've used the rewrite tool extensively but never tried to create more than one expression from the original one. I think that this probably won't work because in your example the code is just statements, but the matching expression can be in code like this:

self doSomething: (
moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight)

and in this case, you cannot replace this with two statements. So I'm tempted to say that what you want is not supported.

Regards, Gabriel

On Mon, Jun 3, 2024 at 5:44 AM Tim Mackinnon tim@testit.works wrote:

I've never really learned the rewrite tool but while waiting for changes to propagate through P12, I thought maybe I could use it to safely patch changes until there are official updates (and it was a good chance to get more familiar with it).

I thought there was more documentation available for how it works (interested in any old articles - but the online help in P12 is ok - although it doesn't display in markdown - in fact give there is markdown support now - I am a bit confused what should appear more readable these days - and how to fix/contribute changes to help).

This aside - I wanted to patch the SystemWindow>>taskbarButtonMenu: (and add my fix to move tabs to any position).

I started writing the following search expression - the find the last call to #addToggle:... method call that has a literal referencing #canBeMovedRight - as I want to add my new menu item after it.

e.g.

moveSubmenu @addToggle: { :node | node isLiteralNode and: [ node value = #canBeMovedToRight] }

but I'm not sure my {} filter block is ever called, as the above matches both addToggle:... calls, and not the one I'm after? I don't understand this - any have ideas on the proper syntax for generic matching?

Anyway - I decided that in my case there is only one - so I can match it explicitly and so I  search on the full call:

and I entered this:
moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight.

Which matches exactly - then I want to replace the single statement with 2 statements e.g.

moveSubmenu
addToggle: 'Move right' translated
target: self
selector: #taskbarMoveRight
getStateSelector: nil
enablementSelector: #canBeMovedToRight.

moveSubmenu
addToggle: 'Move to' translated
target: self
selector: #taskbarMoveTo
getStateSelector: nil.

However this gives an error in the rewrite tool (actually it results in a zero change, that then gives an error for EpMonitor).

But my question is - why can't you rewrite a single statement to multiple ones? I have noticed that in this case I can cascade the message send - and this then rewrites properly - so it looks like it wants to replace a single node with a single node.

If I couldn't cascade - It seems I would have to rewrite with something like: [ "put multiple expressions here" ] value.

This seems an odd choice - but am I missing something obvious?

Do you have to do what I'm proposing in 2 steps - rewrite to a block (or some equivalent) and then a 2nd rewrite to simplify the expression  (like a refactor step) - so its more provably correct?

I'm just curious on this - as it seemed time to learn this properly?

Tim

Stéphane Ducasse
http://stephane.ducasse.free.fr
06 30 93 66 73

"If you knew today was your last day on earth, what would you do differently? ....ESPECIALLY if, by doing something different, today might not be your last day on earth.” Calvin & Hobbes

Stéphane Ducasse
http://stephane.ducasse.free.fr
06 30 93 66 73

"If you knew today was your last day on earth, what would you do differently? ....ESPECIALLY if, by doing something different, today might not be your last day on earth.” Calvin & Hobbes

> On 5 Jun 2024, at 10:22, Tim Mackinnon <tim@testit.works> wrote: > > Hi Stef - I saw that blog post - which looks like its essentially the help text that comes up in the rewrite tool when you press the help button. I was going to submit to add to the help text as I think a better example that pulls all the ideas together is helpful. However, I have realised the Rewrite tool is not the main pharo-project repo and comes under NewTools? How do I find out where that repo is to submit a pr? > > I think the help button on that tool should launch the normal doc browser with the text in the normal help format vs a seperate window? The help browser could probably use some work but at least if help is one place it all will get updated together? Yes it can be nice to have a single documentation so that when we improve it will benefit directly to the tools. > > I was going to propose the following example which helped me a lot - if I wanted to rewrite how logging was done in the following example (and separate the Transcript line into 2 lines): > > appLog: anObject > "Log anObject for debugging" > | date | > date := Date today. > Transcript traceCr: 'debug: ', date printString, anObject printString. > date isLeapYear ifTrue: [ ^nil ]. > ^anObject > > I could write a match expression like this: > > appLog: `arg > | `@temps | > `.@before. > Transcript traceCr: `#str , `@exp1, `@exp2. > `.@after > > and a rewrite rule like this: > > appLog: `arg > | `@temps | > `.@before. > Transcript cr; traceCr: `#str, `@exp1. > `arg traceCr. > `.@after > > I think this demonstrates most of the concepts that caught me out - namely: you need to call out the variable matching explicitly -= the .@before (list statement matching doesn't match variables - and using @temps will match 0 or more vars, so its safer to include it if matching expressions that may have any otherwise nothing will match. > > If you want to match a string you need to match it as a literal e.g. `#str. > > You may need to match 0 or more statements before and after your target expression hence `.@before and `.@after but its important to include the trailing "." on the @before. to cut off before the expression you are trying to match next. > > I didn't fully understand in my example is why I can't match the remaining statements after the transcript string using just `.@exp1. I seem to have to call out both expressions which I now realise (and worth documenting) is because the AST for that line is actually VariableNode(transcript) + MsgNode(MsgNode(Literal+MsgNode(exp1)), MsgNode(exp2) and so you have to call them all out to match things or match the entire sequence with @stmnt (in my case I wanted to pick them apart). I think the top tip in all of this is to use the handy source code ast inspector to help you reason on the structure. > Sometimes this is arcane to me. I think that I would have to concentrate and do a full review of code using tons of new tests :) > The above is probably the gist of the kinds of docs we would need, and happy to submit a pr with this in it. please do. > > On Wed, 5 Jun 2024, at 6:35 AM, stephane ducasse wrote: >> Hi Tim >> >> This is on my todo to have a serious look at the pattern matcher >> Now one of the few documentation that we have in the >> blog and I ported it in the blog post in /doc folder. >> >> S >> >> >> >> >>> On 4 Jun 2024, at 01:35, Tim Mackinnon <tim@testit.works> wrote: >>> >>> Hey Gabriel - thanks for chipping in - you've made me realise that its perhaps easier to give a simpler example (for the multiple replacement)., and while trying to explain in simpler terms I managed to solve the problem myself - but it does demonstrate the docs around this need a bit more work as I didn't find it easy to reason on. >>> >>> I am still confused what the setting "this rule is for a method" does - so am keen for someone to shed light. >>> >>> Anyway for my example it seems that I have to be very specific in how I match things for example I had to use the search pattern: >>> >>> taskbarButtonMenu: `arg >>> | `@temps | >>> `.@before. >>> >>> moveSubmenu >>> addToggle: 'Move right' translated >>> target: self >>> selector: #taskbarMoveRight >>> getStateSelector: nil >>> enablementSelector: #canBeMovedToRight. >>> >>> `.@after >>> >>> And it took me ages to realise that I have to specify the temp vars matching, and also understand that the @before and @after both needed the "." to represent lists of expressions both before and after and then the really subtle "." after @before which I guess prevents greedy matching. >>> >>> With all of that in place, could then have the write rule add an extra statement after the moveSubmenu (or as many as I want) >>> >>> e.g. >>> >>> taskbarButtonMenu: `arg >>> | `@temps | >>> `.@before. >>> >>> moveSubmenu >>> addToggle: 'Move right' translated >>> target: self >>> selector: #taskbarMoveRight >>> getStateSelector: nil >>> enablementSelector: #canBeMovedToRight. >>> >>> moveSubmenu >>> addToggle: 'Move to...' translated >>> target: self >>> selector: #taskbarMoveTo >>> getStateSelector: nil. >>> >>> `.@after >>> >>> >>> I'm curious now whether there is a more efficient way to do what I have done? Possibly the {} syntax could help now i've figured out the basic matching. >>> >>> I really find it easy to confuse that if you don't specify the @temps section that can leave out a lot of matches (it essentialy means 0 or more temps vs. leaving it out means no temps at all). This is a trap that the docs should call out better I think. >>> >>> Anyway - now I just need to figure out how to do this programatically - and possibly I can write a few more useful refactorings I've also thought it was odd that were missing. >>> >>> On Mon, 3 Jun 2024, at 12:19 PM, Gabriel Cotelli wrote: >>>> I've used the rewrite tool extensively but never tried to create more than one expression from the original one. I think that this probably won't work because in your example the code is just statements, but the matching expression can be in code like this: >>>> >>>> self doSomething: ( >>>> moveSubmenu >>>> addToggle: 'Move right' translated >>>> target: self >>>> selector: #taskbarMoveRight >>>> getStateSelector: nil >>>> enablementSelector: #canBeMovedToRight) >>>> >>>> and in this case, you cannot replace this with two statements. So I'm tempted to say that what you want is not supported. >>>> >>>> Regards, Gabriel >>>> >>>> On Mon, Jun 3, 2024 at 5:44 AM Tim Mackinnon <tim@testit.works> wrote: >>>> >>>> I've never really learned the rewrite tool but while waiting for changes to propagate through P12, I thought maybe I could use it to safely patch changes until there are official updates (and it was a good chance to get more familiar with it). >>>> >>>> I thought there was more documentation available for how it works (interested in any old articles - but the online help in P12 is ok - although it doesn't display in markdown - in fact give there is markdown support now - I am a bit confused what should appear more readable these days - and how to fix/contribute changes to help). >>>> >>>> This aside - I wanted to patch the SystemWindow>>taskbarButtonMenu: (and add my fix to move tabs to any position). >>>> >>>> I started writing the following search expression - the find the last call to #addToggle:... method call that has a literal referencing #canBeMovedRight - as I want to add my new menu item after it. >>>> >>>> e.g. >>>> >>>> moveSubmenu `@addToggle: `{ :node | node isLiteralNode and: [ node value = #canBeMovedToRight] } >>>> >>>> but I'm not sure my {} filter block is ever called, as the above matches both addToggle:... calls, and not the one I'm after? I don't understand this - any have ideas on the proper syntax for generic matching? >>>> >>>> Anyway - I decided that in my case there is only one - so I can match it explicitly and so I search on the full call: >>>> >>>> and I entered this: >>>> moveSubmenu >>>> addToggle: 'Move right' translated >>>> target: self >>>> selector: #taskbarMoveRight >>>> getStateSelector: nil >>>> enablementSelector: #canBeMovedToRight. >>>> >>>> Which matches exactly - then I want to replace the single statement with 2 statements e.g. >>>> >>>> moveSubmenu >>>> addToggle: 'Move right' translated >>>> target: self >>>> selector: #taskbarMoveRight >>>> getStateSelector: nil >>>> enablementSelector: #canBeMovedToRight. >>>> >>>> moveSubmenu >>>> addToggle: 'Move to' translated >>>> target: self >>>> selector: #taskbarMoveTo >>>> getStateSelector: nil. >>>> >>>> >>>> However this gives an error in the rewrite tool (actually it results in a zero change, that then gives an error for EpMonitor). >>>> >>>> But my question is - why can't you rewrite a single statement to multiple ones? I have noticed that in this case I can cascade the message send - and this then rewrites properly - so it looks like it wants to replace a single node with a single node. >>>> >>>> If I couldn't cascade - It seems I would have to rewrite with something like: [ "put multiple expressions here" ] value. >>>> >>>> This seems an odd choice - but am I missing something obvious? >>>> >>>> Do you have to do what I'm proposing in 2 steps - rewrite to a block (or some equivalent) and then a 2nd rewrite to simplify the expression (like a refactor step) - so its more provably correct? >>>> >>>> I'm just curious on this - as it seemed time to learn this properly? >>>> >>>> Tim >> >> >> Stéphane Ducasse >> http://stephane.ducasse.free.fr >> 06 30 93 66 73 >> >> "If you knew today was your last day on earth, what would you do differently? ....ESPECIALLY if, by doing something different, today might not be your last day on earth.” Calvin & Hobbes Stéphane Ducasse http://stephane.ducasse.free.fr 06 30 93 66 73 "If you knew today was your last day on earth, what would you do differently? ....ESPECIALLY if, by doing something different, today might not be your last day on earth.” Calvin & Hobbes