Regex 正则表达式匹配scss函数/混合

Regex 正则表达式匹配scss函数/混合,regex,twitter-bootstrap,Regex,Twitter Bootstrap,我正在尝试匹配SCSS字符串中使用的函数或mixin,因此我可能会删除它,但我遇到了一点麻烦 对于那些不熟悉SCS的人来说,这是我试图匹配的一个例子(来自Bootstrap4) 还有一个小功能: @function str-replace($string, $search, $replace: "") { $index: str-index($string, $search); @if $index { @return str-slice($string, 1, $index -

我正在尝试匹配SCSS字符串中使用的函数或mixin,因此我可能会删除它,但我遇到了一点麻烦

对于那些不熟悉SCS的人来说,这是我试图匹配的一个例子(来自Bootstrap4)

还有一个小功能:

@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?([^\}]+)
但是,它只匹配它找到的第一个
}
,这使得它失败,这是因为它需要找到最后一次出现的右大括号

我的想法是,一个能够匹配函数定义的正则表达式可以被修改,但是我用我的GoogleFoo找不到一个好的正则表达式


提前谢谢

我不建议为此使用正则表达式,因为正则表达式不能处理递归,在这种情况下您可能需要什么

例如:

@mixin test {
  body {
  }
}
这里包括两个»级别«(
{{}
),因此您的正则表达式应该能够在括号打开和关闭时计数括号,以匹配mixin或函数的结尾。但这在正则表达式中是不可能的

这个正则表达式

/@mixin(.|\s)*\}/gm
将匹配整个mixin,但如果输入如下:

@mixin foo { … }

body { … }
它将匹配到最后一个
}
的所有内容,包括主体的样式定义。这是因为正则表达式无法知道哪个
}
关闭mixin

看看答案,它或多或少地解释了相同的事情,但基于匹配的html元素


相反,您应该使用解析器,将整个样式表解析为语法树,然后删除不需要的函数并再次将其写入字符串。

事实上,正如@philipp所说,regex不能像编译器那样取代语法分析

但这里有一个sed命令,虽然有点难看,但它可能会起作用:

sed-r-e:a'-e'N'-e'$!ba'-e's/\n//g'-e's/}\s*@(函数| mixin)/}\n@\1/g'-e's/^@(函数| mixin)\s*str replace(\s | \()+.}$//gm'

  • -e':a'-e'N'-e'$!ba'-e's/\N//g'
    :读取循环中的所有文件并删除新行(有关详细信息,请参阅)
  • -e's/}\s*@(function|mixin)/}\n@\1/g'
    :使每个
    @mixin
    @function
    语句成为新行的开始,前面的
    }
    成为前一行的最后一个字符
  • 's/^@(function|mixin)\s*str replace(\s| \()++.*}$///gm'
    :删除与
    @function str replace
    @mixin str replace
    声明相对应的行
但这会导致输出的缩进松动,因此您必须在缩进后重新缩进


我在一个文件中尝试了它,在这个文件中,我复制/粘贴了您提供的示例代码的多次,因此您必须在您的文件中尝试,因为可能有一些情况下正则表达式将匹配比所需更多的元素。如果是这样的情况,请为我们提供一个测试文件来尝试解决这些问题。

在经历了许多头痛之后,这里是我问题的答案

源代码需要逐行拆分并读取,维护打开/关闭大括号的计数以确定索引何时为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
$pattern='/(?@(函数| mixin)\s?[\w-]+[$,:“\'()\s\w\d]+)/”;
$subject=file_get_contents('vendor/twbs/bootstrap/scss/_variables.scss');//只是一个普通的scss文件,包含我已经发布的内容。
$lines=分解(“\n”,$subject);
$total_line=计数($line);
foreach($line作为$line\u no=>$line的行){
if(预匹配($pattern,$line,$matches)){
$match=$matches['remove'];
$counter=0;
$open_brates=$closed_brates=0;

对于($i=$line_no;$iThanks。不幸的是,在运行时不知道函数或mixin的名称是什么。主要的问题是,这些函数/mixin中有一些我不想匹配的变量,所以我的想法是在进行变量匹配之前去掉它们。正如我所说的,一个正则表达式是不够的。cong谢天谢地,奖牌就在岗位上。
$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