#regex #sed #tool #command-line #command-line-tool #source-file #file-path

app spidior

处理类似 sed 的替换任务的工具,当源代码语义成为障碍时尤为有用

2 个版本

0.2.2 2022年2月22日
0.2.1 2022年2月22日

开发工具 中排名第 2248

GPL-3.0 许可证

77KB
1.5K SLoC

spidior

spidior 是一个命令行工具,用于在源代码文件上执行类似 sed 的替换,旨在通过从其操作的代码中解析出的语义信息来增强正则表达式。

状态

Build Status

构建中

安装最新的稳定版 rust,克隆此仓库,然后运行 cargo build

运行

以下是 spidior 的 --help 输出,显示了如何运行它。

spidior 0.1.1
John Westhoff <johnjwesthoff@gmail.com>

USAGE:
    spidior [FLAGS] [OPTIONS]

FLAGS:
    -d, --dump        Whether we should just dump info without replacing
    -h, --help        Prints help information
    -i, --in-place    Whether we should edit files in place or print to stdout
    -I, --interactive Whether we are are interactively replacing things or not
    -n, --nfa         Whether we should print info about the regex nfa
    -r, --recursive   Whether we should search recursively
    -V, --version     Prints version information

OPTIONS:
    -p, --path <path>    The path to the files we are reading [default: .]
    -q, --query <query>  The query string for find/replace for each file we find in the input, required if `dump` is not set

如果使用了 --dump 参数,则 spidior 将不会进行任何替换,而只是打印出从指定路径中的文件运行其轻量级解析的结果。否则,必须使用 -q 或 --query 指定一个查询。

查询

查询非常类似于 sed s 命令,格式为 ${LOCATION}${COMMAND}/${FIND}/${REPLACE}/${END}。其中 ${LOCATION} 是替换应该允许发生的位置(下面有更多说明),${COMMAND} 总是 s,${FIND} 是一个正则表达式,${REPLACE} 是一个替换,而 ${END} 可以是空或字母 'g',以允许在同一行上进行多次替换。

位置

位置可以是以下几种之一

  • % - 在任何包含路径指定符的文件中的任何位置
  • <path_suffix> - 在任何以 path_suffix 结尾的文件中的任何位置
  • {function} - 在任何名为 function 的函数中的任何文件中的任何位置
  • cA-B - 在任何文件的A字符(含)到B字符(不含)之间
  • lA-B - 在任何文件的A行(含)到B行(不含)之间

位置也可以通过括号分组,使用 | 进行并集,使用 & 进行交集,使用 ^ 进行取反。为什么用 ^ 而不是 !?因为我觉得在大多数正则表达式解释器中,集合使用 ^ 进行取反,所以在这里也很有意义。

正则表达式

正则表达式遵循类似于 sed 的语法,并支持以下操作

  • 基本正则表达式操作(连接、合取和星号[以及加号])
  • 使用括号进行分组
  • 集合和负集合,但仅限于范围和显式字符(例如 [a-z] 或 [^xyz],但不能是 \w 或 [[:upper:]])
  • 最重要的是,对输入程序中的标识符的特殊查询
    • 目前,这些查询被放在双大括号之间,用逗号分隔的列表表示条件
      • 支持的准则包括 name=$NAME,其中 $NAME 是你正在搜索的标识符的名称,type=$TYPE,其中 $TYPE 是你正在搜索的标识符的类型,以及 pos=$POS:$LEN,其中 $POS 是匹配长度的起始位置,$LEN。

替换

替换是一个字符串字面量,可以包含对组的后向引用,使用反斜杠后跟一个数字。

示例

考虑以下输入文件,identifiers.java

public class LightningOvercharge extends Lightning {
    int charge = 0;
    public LightningOvercharge() {
        charge = 0;
    }

    double number;
    @Override
    public void onSpawn(Session me) {
        number = 1;
        me.x = 0;
    }
}

onSpawn 方法中,Session 输入参数的名称不应为 me,因此让我们将其更改为 sess。我们可以运行以下命令:spidior -p identifiers.java '%s/[[type=Session]]/sess/g

此命令的结果将是

public class LightningOvercharge extends Lightning {
    int charge = 0;
    public LightningOvercharge() {
        charge = 0;
    }

    double number;
    @Override
    public void onSpawn(Session sess) {
        number = 1;
        sess.x = 0;
    }
}

同样,我们可以运行:spidior -.java '%s/[[type=double,name=number]]/spawnFlag/g 将前面的结果更改为

public class LightningOvercharge extends Lightning {
    int charge = 0;
    public LightningOvercharge() {
        charge = 0;
    }

    double spawnFlag;
    @Override
    public void onSpawn(Session sess) {
        spawnFlag = 1;
        sess.x = 0;
    }
}

请注意,这更改了变量的声明和用法 number

轻量级解析器

spidior 提供动力的是一组特定语言的轻量级解析器。目前,spidior 需要能够解析函数声明和标识符声明 及其 使用,以支持操作语言。目前,只编写了一个类似于 "C-like" 的解析器,它非常过于热情 - 它将许多不是标识符的东西识别为标识符。在实践中,这最终是可行的,因为它的错误包括关键字作为名称的类型或标识符的名称,所以实际的替换操作不会因为这种过分的热情而受阻。

以下是一个示例,运行 spidior --dump -p identifiers.java 的结果。

[{"filename":"identifiers.java","functions":[{"name":"LightningOvercharge","start":507,"end":534},{"name":"onSpawn","start":605,"end":671}],"identifiers":[{"name":"com","type_name":"static","start":67,"end":70},{"name":"com","type_name":"static","start":232,"end":235},{"name":"com","type_name":"static","start":273,"end":276},{"name":"com","type_name":"static","start":316,"end":319},{"name":"com","type_name":"static","start":361,"end":364},{"name":"LightningOvercharge","type_name":"class","start":414,"end":433},{"name":"charge","type_name":"int","start":462,"end":468},{"name":"charge","type_name":"int","start":517,"end":523},{"name":"number","type_name":"double","start":547,"end":553},{"name":"me","type_name":"Session","start":601,"end":603},{"name":"number","type_name":"double","start":615,"end":621},{"name":"me","type_name":"Session","start":635,"end":637},{"name":"me","type_name":"Session","start":635,"end":637}]}]

它正确地识别了源文件中的两个函数,但它找到的变量比实际的多得多——它找到了很多“变量” com 的“类型” static 的使用。在现实中,你永远不会试图替换类型为 static 的标识符,因为这根本不是一个类型,所以这不是一个直接的问题。

依赖项

~5–15MB
~176K SLoC