我正在尝试匹配SCSS字符串中使用的函数或mixin,所以我可能会将其删除但是我遇到了一些麻烦。
对于那些不熟悉SCSS的人来说,这是我想要匹配的事情的一个例子(来自bootstrap 4)。
@mixin _assert-ascending($map, $map-name) {
$prev-key: null;
$prev-num: null;
@each $key, $num in $map {
@if $prev-num == null {
// Do nothing
} @else if not comparable($prev-num, $num) {
@warn "Potentially invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} whose unit makes it incomparable to #{$prev-num}, the value of the previous key '#{$prev-key}' !";
} @else if $prev-num >= $num {
@warn "Invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} which isn't greater than #{$prev-num}, the value of the previous key '#{$prev-key}' !";
}
$prev-key: $key;
$prev-num: $num;
}
}
一个小功能:
@function str-replace($string, $search, $replace: "") {
$index: str-index($string, $search);
@if $index {
@return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
}
@return $string;
}
到目前为止,我有以下正则表达式:
@(function|mixin)\s?[[:print:]]+\n?([^\}]+)
然而,它只匹配它找到的第一个}
使它失败的原因,这是因为它需要找到结束大括号的最后一次出现。
我的想法是,可以调整能够匹配函数定义的正则表达式,但是使用我的Google foo找不到好的正则表达式!
提前致谢!
答案 0 :(得分:0)
我不建议使用正则表达式,因为正则表达式无法处理递归,在这种情况下您可能需要。
For Instance:
@mixin test {
body {
}
}
此处包含两个»级别«范围({{ }}
),因此您的正则表达式应该能够在打开和关闭时计算括号,以匹配mixin或函数的结尾。但是使用正则表达式是不可能的。
这个正则表达式
/@mixin(.|\s)*\}/gm
将匹配整个mixin,但如果输入是这样的:
@mixin foo { … }
body { … }
它将匹配最后}
的所有内容,其中包括正文的样式定义。这是因为正则表达式无法知道哪个}
关闭了mixin。
看看this答案,它解释了或多或少相同的事情,但基于匹配的html元素。
相反,您应该使用解析器将整个样式表解析为语法树,而不是删除不需要的函数,而不是再次将其写入字符串。
答案 1 :(得分:0)
事实上,正如@philipp所说,正则表达式无法像编译器那样取代语法分析。
但是这里有一个sed命令,它有点难看,但可以解决问题:
sed -r -e ':a' -e 'N' -e '$!ba' -e 's/\n//g' -e 's/}\s*@(function|mixin)/}\n@\1/g' -e 's/^@(function|mixin)\s*str-replace(\s|\()+.*}$//gm' <your file>
-e ':a' -e 'N' -e '$!ba' -e 's/\n//g'
:读取循环中的所有文件并删除新行(有关详细信息,请参阅https://stackoverflow.com/a/1252191/7990687)-e 's/}\s*@(function|mixin)/}\n@\1/g'
:将每个@mixin
或@function
语句设为新行的开头,并将前一行}
作为前一行的最后一个字符's/^@(function|mixin)\s*str-replace(\s|\()+.*}$//gm'
:删除与@function str-replace
或@mixin str-replace
声明相对应的行但是它会导致输出松动缩进,所以在此之后你必须重新加注它。
我在一个文件上尝试过,我复制/粘贴了你提供的示例代码多次,所以你必须在你的文件上尝试它,因为可能会出现正则表达式匹配的元素多于想要的情况。如果是这种情况,请向我们提供一个测试文件,以尝试解决这些问题。
答案 2 :(得分:0)
经过多次头痛后,这是我的问题的答案!
源需要逐行拆分并读取,保持打开/关闭括号的计数以确定索引何时为0。
$pattern = '/(?<remove>@(function|mixin)\s?[\w-]+[$,:"\'()\s\w\d]+)/';
$subject = file_get_contents('vendor/twbs/bootstrap/scss/_variables.scss'); // just a regular SCSS file containing what I've already posted.
$lines = explode("\n",$subject);
$total_lines = count($lines);
foreach($lines as $line_no=>$line) {
if(preg_match($pattern,$line,$matches)) {
$match = $matches['remove'];
$counter = 0;
$open_braces = $closed_braces = 0;
for($i=$line_no;$i<$total_lines;$i++) {
$current = $lines[$i];
$open_braces = substr_count($current,"{");
$closed_braces = substr_count($current,"}");
$counter += ($open_braces - $closed_braces);
if($counter==0) {
$start = $line_no;
$end = $i;
foreach(range($start,$end) as $a) {
unset($lines[$a]);
} // end foreach(range)
break; // break out of this if!
} // end for loop
} // end preg_match
} // endforeach
我们有一个$lines
数组,没有任何函数或mixins。
有一种更优雅的方式可以做到这一点,但我没有时间或愿意为SCSS编写AST解析器
然而,这可以非常容易地适应制作被黑的一个!