PHP正则表达式解析-用我自己的语言拆分标记。有更好的办法吗?
我正在创造我自己的语言 目标是将其“编译”为PHP或Javascript,并最终在同一种语言上解释和运行,使其看起来像一种“中级”语言 现在,我将重点放在用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+|
/\:((?: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实际上会生成一个自动机,它使用不同的状态