PHP正则表达式解析-用我自己的语言拆分标记。有更好的办法吗?

PHP正则表达式解析-用我自己的语言拆分标记。有更好的办法吗?,php,string,parsing,Php,String,Parsing,我正在创造我自己的语言 目标是将其“编译”为PHP或Javascript,并最终在同一种语言上解释和运行,使其看起来像一种“中级”语言 现在,我将重点放在用PHP解释并运行它的方面 目前,我正在使用正则表达式拆分字符串并提取多个标记 这是我的正则表达式: /\:((?:cons@(?:\d+(?:\.\d+)?|(?:"(?:(?:\\\\)+"|[^"]|(?:\r\n|\r|\n))*")))|(?:[a-z]+(?:@[a-z]+)?|\^?[\~\&](?:[a-z]+|\d+|

我正在创造我自己的语言

目标是将其“编译”为PHP或Javascript,并最终在同一种语言上解释和运行,使其看起来像一种“中级”语言

现在,我将重点放在用PHP解释并运行它的方面

目前,我正在使用正则表达式拆分字符串并提取多个标记

这是我的正则表达式:

/\:((?:cons@(?:\d+(?:\.\d+)?|(?:"(?:(?:\\\\)+"|[^"]|(?:\r\n|\r|\n))*")))|(?:[a-z]+(?:@[a-z]+)?|\^?[\~\&](?:[a-z]+|\d+|\-1)))/g
这是很难阅读和维护,即使它工作

有更好的方法吗

以下是我的语言代码示例:

:define:&0:factorial
    :param:~0:static
    :case
        :lower@equal:cons@1
    :case:end
    :scope
        :return:cons@1
    :scope:end
    :scope
        :define:~0:static
        :define:~1:static
        :require:static
        :call:static@sub:^~0:~1 :store:~0
        :call:&-1:~0 :store:~1
        :call:static@sum:^~0:~1 :store:~0
        :return:~0
    :scope:end
:define:end
这定义了一个递归函数来计算阶乘(写得不太好,这并不重要)

目标是在
之后获得,包括
@
<代码>:static@sub是一个完整的令牌,保存时不使用

除了令牌
:cons
,它可以在后面取一个值之外,一切都是一样的。该值是一个数值(
整数
浮点
,在语言中分别称为
静态
动态
)或字符串,必须以
开头和结尾,支持类似
\”的转义。不支持多行字符串

变量是具有
~0
的变量,在前面使用
^
将获得上述
:scope
的值

函数类似,使用的是
&0
,而
&-1
指向当前函数(此处不需要
^&-1

这么说,有没有更好的办法来获得代币


在这里,您可以看到它的作用:

[Update]要发布复杂且可维护的模式,您可以使用和拆分它:

请注意,除非在某些情况下(
\n
通常是“安全的”),否则将忽略所有空间


现在,如果你想推销你的lexer和parser,请阅读:

(f)lex[GNU等价于lex]的功能只是让您传递一个regexp列表,并最终传递一个“组”。你也可以试着把工作做完

至于你的要求,我已经在过去做了一个lexer,遵循FLEX的原则。我们的想法是像FLEX一样循环使用regexp:

$regexp = [reg1 => STRING, reg2 => ID, reg3 => WS];
$input = ...;
$tokens = [];
while ($input) {
  $best = null;
  $k = null;
  for ($regexp as $re => $kind) {
    if (preg_match($re, $input, $match)) {
      $best = $match[0];
      $k = $kind;
      break;
    }
  }

  if (null === $best) {
    throw new Exception("could not analyze input, invalid token");
  }

  $tokens[] = ['kind' => $kind, 'value' => $best];

  $input = substr($input, strlen($best)); // move.
}
由于FLEX和Yacc/Bison集成在一起,通常的模式是一直读取到下一个令牌(也就是说,它们不会在解析之前执行读取所有输入的循环)

$regexp
数组可以是任何东西,我希望它是
“regexp”=>“kind”
键/值,但也可以是这样的数组:

$regexp = [['reg' => '...', 'kind' => STRING], ...]

还可以使用组(例如Flex组工作)启用/禁用ReXEP:例如,考虑下面的代码:

class Foobar {
  const FOOBAR = "arg";
  function x() {...}  
}
在需要读取表达式之前,无需激活字符串regexp(此处,表达式是“=”后面的表达式)。当您实际在
类中时,无需激活
标识符

FLEX的组允许使用第一个regexp读取注释,激活一些忽略其他regexp的组,直到完成一些匹配(如“
*/
”)

请注意,这种方法很幼稚:类似lexer的FLEX实际上会生成一个自动机,它使用不同的状态来表示您的需求(regexp本身就是一个自动机)

这使用了一种压缩索引算法或类似的算法(我使用了天真的“for each”,因为我对算法理解不够),这是一种内存和速度高效的算法

正如我所说,这是我过去制作的东西——大约6/7年前

  • 它在窗户上
  • 它不是特别快(因为有两个循环,所以它是O(N²))
  • 我还认为PHP每次都在编译regexp。既然我使用Java,我就使用模式实现来编译regexp一次,并让您重用它。如果已经有一个已编译的regexp,我不知道PHP会通过首先查看regexp缓存来做同样的事情
  • 我使用了带有偏移量的
    preg_match
    ,以避免在末尾执行
    substr($input,…)

您应该尝试使用ANTLR3 PHP代码生成目标,因为ANTLR语法编辑器非常容易使用,并且您将拥有一个更可读/更可维护的代码:)

[Update]要解决模式复杂且可维护的问题,您可以使用,并且:

请注意,除非在某些情况下(
\n
通常是“安全的”),否则将忽略所有空间


现在,如果你想推销你的lexer和parser,请阅读:

(f)lex[GNU等价于lex]的功能只是让您传递一个regexp列表,并最终传递一个“组”。你也可以试着把工作做完

至于你的要求,我已经在过去做了一个lexer,遵循FLEX的原则。我们的想法是像FLEX一样循环使用regexp:

$regexp = [reg1 => STRING, reg2 => ID, reg3 => WS];
$input = ...;
$tokens = [];
while ($input) {
  $best = null;
  $k = null;
  for ($regexp as $re => $kind) {
    if (preg_match($re, $input, $match)) {
      $best = $match[0];
      $k = $kind;
      break;
    }
  }

  if (null === $best) {
    throw new Exception("could not analyze input, invalid token");
  }

  $tokens[] = ['kind' => $kind, 'value' => $best];

  $input = substr($input, strlen($best)); // move.
}
由于FLEX和Yacc/Bison集成在一起,通常的模式是一直读取到下一个令牌(也就是说,它们不会在解析之前执行读取所有输入的循环)

$regexp
数组可以是任何东西,我希望它是
“regexp”=>“kind”
键/值,但也可以是这样的数组:

$regexp = [['reg' => '...', 'kind' => STRING], ...]

还可以使用组(例如Flex组工作)启用/禁用ReXEP:例如,考虑下面的代码:

class Foobar {
  const FOOBAR = "arg";
  function x() {...}  
}
在需要读取表达式之前,无需激活字符串regexp(此处,表达式是“=”后面的表达式)。当您实际在
类中时,无需激活
标识符

FLEX的组允许使用第一个regexp读取注释,激活一些忽略其他regexp的组,直到完成一些匹配(如“
*/
”)

请注意,这种方法很幼稚:类似lexer的FLEX实际上会生成一个自动机,它使用不同的状态