This page covers some common techniques for writing GritQL patterns.
List and string accumulation
Within where
clauses, you can assign a value to a metavariable using the =
operator and then use +=
to accumulate values. This is particularly common when combined with some
to iterate over a list of values, such as in this pattern.
Strings and lists can both be accumulated, as shown in this example:
`namedColors = { $colors }` where { $keys = "", $values = [], $colors <: some bubble($keys, $values) `$name: $color` where { // Push the name onto the string $keys += $name, // Push the values onto the list $values += `$color` }, // Join the values together to make a string $new_colors = join(list=$values, separator=", ") } => `$keys = [ $new_colors ]`
const namedColors = { red: '#ff0000', green: '#00ff00', blue: '#0000ff', };
const redgreenblue = [ '#ff0000', '#00ff00', '#0000ff' ];
Creating new files
The $new_files
metavariable allows creating a new file as a side-effect of a pattern.
For example, for following pattern moves all functions which start with test
to a separate file (stuff.test.js
):
`function $functionName($_) {$_}` as $f where { $functionName <: r"test.*", $f => ., $new_file_name = `$functionName.test.js`, $new_files += file(name = $new_file_name, body = $f) }
function doStuff() {} function testStuff() {}
// stuff.js function doStuff() {} // testStuff.test.js function testStuff() {}
Warning: $new_files
does not consider the existing files, so it is possible to overwrite existing files.
Accessing the current file name
The $filename
metavariable always contains the name of the current file the query is processing.
For example, the following pattern we move the matching function to a new file that uses the same name as the original file, but with a .test.js
extension:
`function $functionName($_) {$_}` as $f where { $functionName <: r"test.*", $f => ., $filename <: r"(.*)\.js$"($base_name), $new_file_name = `$base_name.test.js`, $new_files += file(name = $new_file_name, body = $f) }
function doStuff() {} function testStuff() {}
// stuff.js function doStuff() {} // stuff.test.js function testStuff() {}
Comments
Comments are supported in GritQL. Any line starting with //
is considered a comment and ignored by the parser.
`console.log($message)` => . where { // This is a comment $message <: js"'Hello, world!'" }
Specific rewrites
Since GritQL supports nested patterns, it is usually preferable to match a larger pattern and then only rewrite the specific metavariables you want to change inside a where
clause.
This helps in several ways:
- It avoids duplication, as often the right-hand side of the rewrite repeats the left-hand side.
- It helps preserve syntactic and semantic details of the original code.
For example, the following rewrite is not specific and therefore loses the async
keyword and the comment about arguments:
`function foo($args) { $body }` => `function bar($args) {$body} `
async function foo(/* no args */) { console.log('Hello, world!'); }
function bar() { console.log('Hello, world!'); }
The following rewrite is specific and preserves the async
keyword and the comment about arguments:
`function $name($args) { $body }` where { $name <: `foo` => `bar` }
async function foo(/* no args */) { console.log('Hello, world!'); }
async function bar(/* no args */) { console.log('Hello, world!'); }