Functions

While patterns are used to match against AST nodes, functions are used as replacement values. Thus, functions can only be used on the right hand side of GritQL queries. Specifically:

  • In assignments: $x = function_call()
  • In insertions: $x += 'function_call()
  • In rewrites: $x => function_call()

Function definitions

Just like with patterns, custom functions can be defined using the function keyword. Functions must have a name, and can optionally take parameters.

The body of the function consists of predicates and conditions (like those inside a where clause) that are evaluated in order.

The final line of the function should be return followed by the value to return.

PATTERN
// define a lines function
function lines($string) {
    return split($string, separator=`\n`)
}

// Define a my_todo function
function my_todo($target, $message) {
   if($message <: undefined) {
       $message = "This requires manual intervention."
   },
   $lines = lines(string = $message),
   $lines <: some bubble($result) $x where {
       if ($result <: undefined) {
            $result = `// TODO: $x`
        } else {
            $result += `\n// $x`
        }
   },
   $target_lines = lines(string = $target),
   $target_lines <: some bubble($result) $x where { $result += `\n// $x` },
   return $result,
}

// Use the my_todo function
`module.exports = $_` as $x => my_todo(target=$x, message=`Fix this\nAnd that`)
INPUTOUTPUT
module.exports = {
  king,
  queen: '8',
};
// TODO: Fix this
// And that
// module.exports = {
//   king,
//   queen: '8',
// };

Function parameter names can be omitted as long as arguments are provided in the same order as the parameters are defined. For example, the my_todo function from above could be called like this:

GRIT
`module.exports = $_` as $x => my_todo($x, `Fix this\nAnd that`)

JavaScript functions

Warning: This is an alpha feature in active development, without stability guarantees. Please contact us on Discord or Slack if you have any questions.

Functions are not limited to using GritQL. By including js after the function parameters, you can define a function that is implemented in JavaScript.

PATTERN
language js

function fizzbuzz($x) js {
    // Use $var.text to access the binding's value
    const parsed = parseInt($x.text, 10);
    let output = '';
    if (parsed % 3 === 0) output += 'Fizz';
    if (parsed % 5 === 0) output += 'Buzz';
    return output || parsed;
}

`console.log($x)` => fizzbuzz($x)
INPUTOUTPUT
console.log(3);
console.log(8);
console.log(10);
console.log(15);
Fizz;
8;
Buzz;
FizzBuzz;

Limitations

  • JavaScript functions must return a stringable value. This means that the return value must be a string, or an object with a toString method.
  • Functions are executed in a WebAssembly sandbox. They cannot access the filesystem or make network requests.
  • If the function throws an error, the pattern will error and fail to match.
  • Parameters are accessible only via the $variable.text property. Metavariables without a string representation cannot be accessed. If you need to parse a parameter as a number, you can use parseInt or parseFloat.
  • Foreign functions cannot bind new variables. They can only access the variables that are passed in as parameters.

Built-in functions

GritQL provides several built-in functions that can be used in queries targeting any language.

capitalize

capitalize takes a string and returns a new string with the first character capitalized.

GRIT
capitalize(string = "hello") // "Hello"

trim

trim(s) returns a new string with the whitespace removed from the beginning and end of s.

GRIT
trim(string = "  hello  ", trim_chars = " ") // returns "hello"

join

join(delimiter, strings) concatenates a list of strings with a delimiter to form a single string.

GRIT
join(list = ["a", "b", "c"], separator = "_") // returns "a_b_c"
join(list = ["a", "b", "c"], separator = "") // returns "abc"

todo

In some cases, a transformation cannot be completed fully automatically. You can use the todo function to mark a target snippet as incomplete and add a comment to the generated code. An optional message can be provided to give more information about the incomplete transformation.

PATTERN
or {
  `console.log($msg)` => todo(target=$msg),
  `console.error($msg)` as $log => todo(target=$log, message="Consider a lower error level.")
}
INPUTOUTPUT
console.log("hello");
console.error("This is an error");
// TODO: This requires manual intervention.
// "hello"
// TODO: Consider a lower error level.
// console.error("This is an error")

lowercase

lowercase takes a string and returns a new string with all characters lowercased.

GRIT
lowercase(string = "HELLO") // "hello"
lowercase(string = "Hello") // "hello"
lowercase(string = "hello") // "hello"

length

length(target=$items) returns the number of elements in a target list. It can also be used to get the length of a target string.

GRIT
length(target=[7, 8, 9]) // returns 3
length(target="hello") // returns 5

uppercase

uppercase takes a string and returns a new string with all characters uppercased.

GRIT
uppercase(string = "HELLO") // "HELLO"
uppercase(string = "Hello") // "HELLO"
uppercase(string = "hello") // "HELLO"