Skip to content
Rewriting

Rewriting

Rewriting

A rewrite replaces each match with a template rendered at it, or with what a function returns for it, and splices the result into the input. The template language is the rewrite accessors section’s; masking is on the redaction page. The flags and parameters are on the CLI and PowerShell pages.

Templates

${name} renders a register, ${0} the whole match, ${1}, ${2} a register by position, and ${name:accessor} transforms or slices one: upper, lower and trim on any register, the typed slices on a captured atom (${ip:octet1-2}, ${url:host}, ${e:domain}, ${ts:year}), chained with |. $$ is a dollar sign, and \n, \t and \\ are a newline, a tab and a backslash.

$ trex rewrite '\E:e' '[${e:domain}]' --text 'mail bob@x.com and amy@y.org now'
mail [x.com] and [y.org] now

$ trex rewrite '\I:ip' '${ip:octet1-2}.0.0/16' --text 'conn from 192.168.5.9'
conn from 192.168.0.0/16

$ trex rewrite '\W:a "=" \N:b' '${2} := ${1}' --text 'x = 1'
1 := x

A template naming a register the pattern does not bind, or an accessor the register’s kind does not have, is refused before anything is rewritten.

The first matches

let pat = trex::parse(r"\N").expect("valid pattern");
let out = trex::rewrite_n_with(&pat, b"1 2 3 4", 2, |_| "n");
assert_eq!(out, b"n n 3 4");

Computed replacements

A function in place of the template is handed each match and returns its replacement, so a replacement can be computed from a register’s typed value. It reads a register through the same references a template writes.

let pat = trex::parse(r"\E:e").expect("valid pattern");
let mut n = 0;
let out = trex::rewrite_with(&pat, b"bob@x.com, amy@y.org", |m| {
    n += 1;
    format!("{n}:{}", m.get("e:domain").expect("a slice a template could render"))
});
assert_eq!(out, b"1:x.com, 2:y.org");

In Rust the closure receives a Matched, whose get reads a reference such as e:domain, 0:last4 or 1, and whose value reads a typed register where it was built with the pattern’s capture_kinds. In Python the callable receives the Match and returns str for a str input and bytes for bytes. In PowerShell the script block runs with the Trex.Match as $_, and its last output replaces the match.

Files

One input is rewritten to the standard output. A directory or several inputs take --in-place, which writes each file holding a match back in the encoding it was read in, behind its own byte order mark, every byte outside a match as it was, or --dry-run, which prints the unified diff each file would take, with three lines of context or -C N. A directory is walked as scan walks one, and a binary file named alone is refused, by name, unless --binary asks for it.

$ cat src/notes.md
The answer is 42, and legacy_call() is the old name.
See legacy_call in the guide.
$ cat src/main.rs
fn main() {
    legacy_call(42);
}
$ trex rewrite '"legacy_call"' 'current_call' src/ --dry-run
--- src/main.rs
+++ src/main.rs
@@ -1,3 +1,3 @@
 fn main() {
-    legacy_call(42);
+    current_call(42);
 }
--- src/notes.md
+++ src/notes.md
@@ -1,2 +1,2 @@
-The answer is 42, and legacy_call() is the old name.
-See legacy_call in the guide.
+The answer is 42, and current_call() is the old name.
+See current_call in the guide.

$ trex rewrite '"legacy_call"' 'current_call' src/ --in-place
src/main.rs: 1 replacement
src/notes.md: 2 replacements

In PowerShell -InPlace asks ShouldProcess for each file, so -WhatIf names the files it would write and writes none, and -Confirm asks for each.

Review

--interactive (-i) shows each change as its unified diff, with the template of its match - the kinds of the tokens the match spans - and how many later changes share that template, and asks:

AnswerDoes
yapplies this change
nskips it
etakes another replacement, in the editor VISUAL or EDITOR names, or typed at the prompt where neither is set
tapplies this change and every later one of its template
Tskips this change and every later one of its template
aapplies this change and every one after it
qstops, leaving every file as it was

The accepted changes are written once the last one has been answered, so a session that ends early leaves every file as it was. -U (--update-all) applies every change without asking. The answers are read from the standard input, so a review runs from a pipe as well as by hand. How many changes a T passed over is reported; --show-skipped names where each stands. --explain puts under each diff what scan --explain puts under each match.

$ cat levels.log
alpha 10
beta 20
gamma 300
delta 4000
epsilon 5
$ printf 'n\ny\n' | trex rewrite '\N{>=100}' '[${0}]' levels.log --interactive
--- levels.log
+++ levels.log
@@ -1,5 +1,5 @@
 alpha 10
 beta 20
-gamma 300
+gamma [300]
 delta 4000
 epsilon 5
  template: number (1 later change shares it)
[1/2] accept, skip or edit this change, all of this template, none of it, accept all, or quit [y/n/e/t/T/a/q]? --- levels.log
+++ levels.log
@@ -1,5 +1,5 @@
 alpha 10
 beta 20
 gamma 300
-delta 4000
+delta [4000]
 epsilon 5
  template: number (0 later changes share it)
[2/2] accept, skip or edit this change, all of this template, none of it, accept all, or quit [y/n/e/t/T/a/q]? levels.log: 1 replacement

t takes the rest of a template with one answer:

$ cat levels.log
alpha 10
beta 20
gamma 300
delta 4000
epsilon 5
$ printf 't\n' | trex rewrite '\N{>=100}' '[${0}]' levels.log --interactive
--- levels.log
+++ levels.log
@@ -1,5 +1,5 @@
 alpha 10
 beta 20
-gamma 300
+gamma [300]
 delta 4000
 epsilon 5
  template: number (1 later change shares it)
[1/2] accept, skip or edit this change, all of this template, none of it, accept all, or quit [y/n/e/t/T/a/q]? levels.log: 2 replacements

In PowerShell -Interactive puts each change to the host’s prompt with the same choices, Y, N (the default), E, T, S for skipping a template, A and Q; a host that cannot prompt, such as pwsh -NonInteractive, stops at the first question and writes nothing.

Declared atoms

A pattern naming \{name} reads its declaration from a pattern file, --lib FILE on the command line, lib= in Python, Import-TrexAtom or -Library in PowerShell and a ShapeSet in Rust; a declared shape decides the token boundaries the rewrite lexes on, so it replaces what a scan under the same file reports. A rewrite under a declared shape runs on the CPU engines. The pattern files page has the declarations.