我正在创建自己的语言。
目标是将其“编译”为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
之外,所有内容都是相同的,后者可以取值。该值是一个数值(integer
或float
,分别在语言中称为static
或dynamic
)或字符串,必须以{{1开头和结尾},支持像"
这样的转义。不支持多行字符串。
变量是\"
的变量,之前使用~0
将获得上述^
的值。
函数类似,使用:scope
而&0
指向当前函数(此处不需要&-1
)。
说这个,有没有更好的方法来获得令牌?
您可以在此处看到它:http://regex101.com/r/nF7oF9/2
答案 0 :(得分:3)
[更新]要发布模式复杂且可维护性,您可以使用PCRE_EXTENDED和comments对其进行拆分:
preg_match('/
# read constant (?)
\:((?:cons@(?:\d+(?:\.\d+)?|
# read a string (?)
(?:"(?:(?:\\\\)+"|[^"]|(?:\r\n|\r|\n))*")))|
# read an identifier (?)
(?:[a-z]+(?:@[a-z]+)?|
# read whatever
\^?[\~\&](?:[a-z]+|\d+|\-1)))
/gx
', $input)
请注意忽略所有空格,除非在某些条件下(\n
通常是“安全的”)。
现在,如果你想让皮条客和解析器皮条客,那么请阅读:
(f)lex [GNU等效于LEX]只是让你传递一个regexp列表,最后是一个“group”。您还可以尝试使用ANTLR和PHP Target Runtime来完成工作。
至于你的要求,我在过去遵循FLEX的原则制作了词法分析器。我的想法是像FLEX那样循环使用正则表达式:
$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], ...]
您还可以使用组启用/禁用regexp(例如FLEX组工作):例如,请考虑以下代码:
class Foobar {
const FOOBAR = "arg";
function x() {...}
}
在您需要读取表达式之前,不需要激活字符串regexp(此处,表达式是“=”之后的表达式)。当您实际在class
时,无需激活class
标识符。
FLEX的小组允许使用第一个正则表达式读取注释,激活一些忽略其他正则表达式的组,直到某些匹配完成(如“*/
”)。
请注意,这种方法是一种天真的方法:像FLEX这样的词法分析器实际上会生成一个自动机,它使用不同的状态来表示你的需要(正则表达式本身就是一个自动机)。
这使用了打包索引或类似的算法(我使用了每个“天真”,因为我不太了解算法),这是内存和速度效率。
正如我所说,这是我过去做的事情 - 就像6/7年前一样。
preg_match
,以避免在结尾处执行substr($input, ...)
。 您应该尝试使用ANTLR3 PHP代码生成目标,因为ANTLR语法编辑器非常易于使用,并且您将拥有更易读/可维护的代码:)