And, just like you have types of parser combinators for composite or primitive data types, you can also have a type of parser combinators for … parser combinators. You’ll find a nice exposition of this idea in:
“Even higher-order functions for parsing or Why would anyone ever want to use a sixth-order function?” in JFP ’98 by Chris Okasaki
On the pragmatic side of things, that type of parser combinator is also the key approach behind coarsely and generically parsing many kinds of language syntax in comby (disclaimer: I’m the author of said tool).
That’s definitely an interesting idea! Just thinking about functions of that sort of order is a mental workout. I haven’t read it carefully yet so maybe I don’t fully understand the consequences of using success and failure continuations, but wouldn’t using a mutable lazy type like OCaml’s Lazy.t be a bit more pragmatic? I never thought about using it for backtracking but I’ve implemented a lazy list to use as input for a streaming XML parser, maybe something similar could be used for backtracking too.
Edit: I’ve heard about comby before but never ended up looking into it, it looks very nice! I’ll try using it for refactoring some code at work.
Hi there, I’m the author of this tool. Belated reply (unfortunately I found this post a little late). For your example, it depends what assumptions can be made. But in the simplest case, you can:
Match: ::[x] => :[y]_val
Rewrite: :[x]: :[y]_val
which repeats over both matching instances (https://bit.ly/30l5yfA). I suspect that it makes more sense to perform this within %{...}, in which case you want to repeat over the body. So, the way this is possible is by matching on the body of %{...} at the toplevel, and then use a rewrite rule to rewrite each instance inside: https://bit.ly/30gfoj1.
Thanks for you sharing your example, always interested in learning about the sorts of changes that come to mind. If you have more thoughts or questions, feel free to reach out to me on the tool’s Gitter chat.
The point is that the _val isn’t common suffix like in an example, it is more like example, where a_val can be anything (including another map). This makes everything a little more problematic. While pattern ::[] => and rewrite :[]: will do, it was strange for me that ::[x] => :[y.] do not treat commas as punctuation.
I would be nice to see a comparison between Comby and Coccinelle from the point of view of the users (the “patch writer”).
For example, it seems that it would take more to create a patch using Comby rather than using Coccinelle. The syntax of Comby has the obvious advantage of being quite language-agnostic, but it requires the users to type out the whole set of code lines that they want to modify. The semantic patch language used by Coccinelle is instead an extension of the unified patch format. For the users that means that they just need to annotate an existing and valid patch created by another tool, rather than typing out code.
Another topic: context. I haven’t found in the documentation whether there is any support for “context” or if the context has to be programmatically checked. In other words: how do I write a patch that only applies to instructions that happen to be after or before other instructions? Do I need to match something in the surrounding code and then use a where rule?
Belated reply (found this post a bit late): Coccinelle is a great tool, and I’d also be interested in a comparison from the pov of users. There’s recent work on extending Coccinelle to Java. My impression is that it takes more effort to write a fully-fledged grammar for different languages depending on their semantic properties, and that can introduce more complexity in the “patch language”, if we take Coccinelle as an analog.
Re context: it’s difficult to say where the expressivity overlaps here, because it depends a lot on the rewrite (i.e., what syntactic or semantic context you need to achieve a rewrite, which also bears on the syntax/semantics of the language). Comby doesn’t use any semantic information right now, but syntactic patterns can often serve as proxy for semantic properties. For example, in Go we can assume some of the structure following the func keyword, match on the arguments or or statements in the body syntactically, and learn something about static properties like variable uses of the function. As you intuited, where rules exist in part to refine match/rewrite behavior inside of surrounding syntactic code/context, like nested terms.
At the end of the day, I look at program transformation tools as lying on a spectrum, from something rudimentary like sed up to extremely sophisticated and language specific-tools like Clang. These tools make different design choices that impose varying levels of effort, (language) generality, and power/expressivity. I.e., there is always going to be a need for these different flavors of tooling depending on the task. In my research I worked a lot with modifying small program fragments for various applications, and I wanted something minimally declarative, language-general, and sensitive to certain syntactic structures. Existing solutions were a bit limiting for those needs, and now there’s this tool. Thanks for your thoughts and I’m happy to share more. In the meantime, let’s keep our fingers crossed for future comparative studies of user experiences with program transformation tools :)
And, just like you have types of parser combinators for composite or primitive data types, you can also have a type of parser combinators for … parser combinators. You’ll find a nice exposition of this idea in:
On the pragmatic side of things, that type of parser combinator is also the key approach behind coarsely and generically parsing many kinds of language syntax in comby (disclaimer: I’m the author of said tool).
That’s definitely an interesting idea! Just thinking about functions of that sort of order is a mental workout. I haven’t read it carefully yet so maybe I don’t fully understand the consequences of using success and failure continuations, but wouldn’t using a mutable lazy type like OCaml’s Lazy.t be a bit more pragmatic? I never thought about using it for backtracking but I’ve implemented a lazy list to use as input for a streaming XML parser, maybe something similar could be used for backtracking too.
Edit: I’ve heard about comby before but never ended up looking into it, it looks very nice! I’ll try using it for refactoring some code at work.
Is it possible to match something like
And change it to:
With this tool? Because I haven’t succeeded with such simple task.
Not such an elegant solution, but here it goes:
https://comby.live/index.html#%7B%22source%22%3A%22%25%7B%3Aa%20%3D%3E%20a_val%2C%20%3Ab%20%3D%3E%20b_val%7D%22%2C%22match%22%3A%22%25%7B%3A%3A%5B1%5D%20%3D%3E%20%3A%5B2%5D%2C%20%3A%3A%5B3%5D%20%3D%3E%20%3A%5B4%5D%7D%22%2C%22rule%22%3A%22where%20true%22%2C%22rewrite%22%3A%22%25%7B%3A%5B1%5D%3A%20%3A%5B2%5D%2C%20%3A%5B3%5D%3A%20%3A%5B4%5D%7D%22%2C%22language%22%3A%22.generic%22%2C%22substitution_kind%22%3A%22in_place%22%2C%22id%22%3A0%7D
Hi there, I’m the author of this tool. Belated reply (unfortunately I found this post a little late). For your example, it depends what assumptions can be made. But in the simplest case, you can:
Match:
::[x] => :[y]_val
Rewrite:
:[x]: :[y]_val
which repeats over both matching instances (https://bit.ly/30l5yfA). I suspect that it makes more sense to perform this within
%{...}
, in which case you want to repeat over the body. So, the way this is possible is by matching on the body of%{...}
at the toplevel, and then use arewrite
rule to rewrite each instance inside: https://bit.ly/30gfoj1.Thanks for you sharing your example, always interested in learning about the sorts of changes that come to mind. If you have more thoughts or questions, feel free to reach out to me on the tool’s Gitter chat.
The point is that the
_val
isn’t common suffix like in an example, it is more like example, wherea_val
can be anything (including another map). This makes everything a little more problematic. While pattern::[] =>
and rewrite:[]:
will do, it was strange for me that::[x] => :[y.]
do not treat commas as punctuation.I would be nice to see a comparison between Comby and Coccinelle from the point of view of the users (the “patch writer”).
For example, it seems that it would take more to create a patch using Comby rather than using Coccinelle. The syntax of Comby has the obvious advantage of being quite language-agnostic, but it requires the users to type out the whole set of code lines that they want to modify. The semantic patch language used by Coccinelle is instead an extension of the unified patch format. For the users that means that they just need to annotate an existing and valid patch created by another tool, rather than typing out code.
Another topic: context. I haven’t found in the documentation whether there is any support for “context” or if the context has to be programmatically checked. In other words: how do I write a patch that only applies to instructions that happen to be after or before other instructions? Do I need to match something in the surrounding code and then use a
where
rule?Belated reply (found this post a bit late): Coccinelle is a great tool, and I’d also be interested in a comparison from the pov of users. There’s recent work on extending Coccinelle to Java. My impression is that it takes more effort to write a fully-fledged grammar for different languages depending on their semantic properties, and that can introduce more complexity in the “patch language”, if we take Coccinelle as an analog.
Re context: it’s difficult to say where the expressivity overlaps here, because it depends a lot on the rewrite (i.e., what syntactic or semantic context you need to achieve a rewrite, which also bears on the syntax/semantics of the language). Comby doesn’t use any semantic information right now, but syntactic patterns can often serve as proxy for semantic properties. For example, in Go we can assume some of the structure following the
func
keyword, match on the arguments or or statements in the body syntactically, and learn something about static properties like variable uses of the function. As you intuited,where
rules exist in part to refine match/rewrite behavior inside of surrounding syntactic code/context, like nested terms.At the end of the day, I look at program transformation tools as lying on a spectrum, from something rudimentary like sed up to extremely sophisticated and language specific-tools like Clang. These tools make different design choices that impose varying levels of effort, (language) generality, and power/expressivity. I.e., there is always going to be a need for these different flavors of tooling depending on the task. In my research I worked a lot with modifying small program fragments for various applications, and I wanted something minimally declarative, language-general, and sensitive to certain syntactic structures. Existing solutions were a bit limiting for those needs, and now there’s this tool. Thanks for your thoughts and I’m happy to share more. In the meantime, let’s keep our fingers crossed for future comparative studies of user experiences with program transformation tools :)