-
-
Notifications
You must be signed in to change notification settings - Fork 15
Advanced queries via Go code #32
Description
Simple queries can be done as a pipeline, such as finding all unnecessary uses of [:]
on strings:
$ gogrep -x '$x[:]' -a 'type(string)'
However, this quickly breaks down if any non-linear logic is required. For example, what if we wanted to catch all unnecessary uses of [:]
, including both strings and slices? You might imagine a more complex syntax and language, like:
$ gogrep -x '$x[:]' -a 'type(string) OR is(slice)'
However, this would still be a limited subset of all the logic that you might want in your query. Another common example is variations on -g
, such as contains at least 2 of X pattern
, as opposed to contains at least 1 of X pattern
.
Instead of complicating the query language further, we should leverage the Go language, which all gogrep users should already be familiar with. Our unnecessary [:]
example above could be something like:
stmts := gogrep.FindAll(input, `$x[:]`)
stmts = gogrep.Filter(stmts, gogrep.AnyOf(
gogrep.Type("string"),
gogrep.Kind("slice"),
))
gogrep.Output(stmts, "should drop [:] on strings and slices")
All the features that are available via the command line would be available via this API, as the command line would be implemented using the API after all. For example, to make the code perform a rewrite instead of giving a warning, we could have:
stmts := gogrep.FindAll(input, `$x[:]`)
stmts = gogrep.Filter(stmts, gogrep.AnyOf(
gogrep.Type("string"),
gogrep.Kind("slice"),
))
stmts = gogrep.Substitute(stmts, "$x")
gogrep.WriteBack(stmts)
One of the important features of using Go code is the ability to compose commands in more interesting ways, such as our use of gogrep.AnyOf
above. But it would also be possible to perform changes and filters directly, as one can range over the stmts list of type []ast.Stmt
.
We could even add ways to simplify the writing of simpler pipe-like queries in Go code, such as:
gogrep.Pipe(input,
gogrep.FindAll("$x[:]"),
gogrep.Filter(gogrep.AnyOf(gogrep.Type("string"), gogrep.Kind("slice"))),
gogrep.Substitute("$x"),
gogrep.WriteBack,
)
The first step is to move the logic out of the main package, so that it can be imported as a Go package. I would also like to come up with a package name that is shorter than gogrep
, as one will have to write it everywhere (assuming dot-imports are to be avoided). Perhaps gg
for its initials, or gr
for "grep".