我有一个简单的文件格式,我想用jison解析器生成器解析。这个文件可以由任意顺序和数量的多个表达式组成。这是解析器的jison文件:
/* lexical grammar */
%lex
%%
\s+ /* skip whitespace */
\"(\\.|[^"])*\" return 'STRING'
File\s*Version\s*\: return 'FILEVERSION'
[0-9]+("."[0-9]+)?\b return 'NUMBER'
<<EOF>> return 'EOF'
. return 'INVALID'
/lex
%start expressions
%% /* language grammar */
expressions
: EOF
| e expressions EOF
;
e
: STRING
| FILEID
;
FILEID
: FILEVERSION NUMBER { return $1 + $2; }
;
为了简单起见,我将文件缩短为只有字符串和文件id表达式。
我的问题是,如果第二个表达式只包含一个类似令牌的字符串,那么生成的解析器似乎只能识别一个或两个完整的表达式。例如:
文件版本: 1.0
将被解析,或者
文件版本:1.0“我的字符串”
也将被解析,但对于
文件版本:1.0"我的字符串""未解析的字符串"
最后一个字符串不会被解析。
我用jison调试器和jison页面本身尝试了这段代码,但两个页面都显示了相同的结果。
我对这个问题的建议是:
我不是ebnf解析器大师,所以请尽可能简单地回答。
直接的问题是,您从FILEID
产品中返回return
返回,因此解析将以返回的值终止。通常,语义规则应该通过分配给变量$
来提供结果。(对于右边只有一个符号的“单元规则”来说,这是不必要的;在执行操作之前,解析器会执行$=$1
,因此如果这是您想要的,您可以像在两个FILEID
规则中一样将操作省略。)
此外,你的
表达式
产品对$2
没有任何作用,因此即使你解决了第一个问题,你仍然只能在结果中看到一个e
。
您的
表达式
产品也不正确,因为除了基本案例中的EOF外,每个e
都需要一个EOF
标记。考虑产品是如何工作的:
expressions -> e expressions EOF
-> e e expressions EOF EOF
-> e e e expressions EOF EOF EOF
-> e e e EOF EOF EOF EOF
就个人而言,我建议使用左递归而不是右递归。像jison这样的自底向上的解析器更喜欢左递归,它通常会导致更自然的语义规则,如本例所示。
最后,当您实际到达输入的末尾时,需要返回最终值。在jison中,这通常需要一个明确的开始规则,其语义动作是
return
。
因此,考虑到所有这些,让我们尝试一下:(我更改了一些非终端的名称,并使用小写的
FILEID
,因为传统上,非终端使用小写,终端使用大写)
%start prog
%%
prog : exprs EOF { return $1; }
;
exprs : { $$ = []; }
| exprs expr { $$.push($2); }
;
expr : file_id
| STRING
;
file_id: FILEVERSION NUMBER { $$ = $1 + $2; }
;
关于匹配字符串的正则表达式,请注意:
\"(\\.|[^"])*\" return 'STRING'
虽然它显然在javascript中工作(主要是;见下文),但它在flex(或与Posix兼容的正则表达式库)中会出现错误。它主要在javascript中工作,因为javascript正则表达式替换操作符
|
是有序的;如果第一个备选方案匹配,则永远不会尝试第二个备选方案,除非模式的其余部分无法匹配(在这种情况下,将触发错误)。
但在(f)lex中,交替操作符会注意到所有匹配的替代项,并最终选择可能最长的匹配项。结果是当匹配
“\\”时。。。“
,flex将使用[^”]
匹配第一个\,然后使用\\,匹配令牌直到第三个引号
以匹配\“。这样它就可以继续寻找收盘报价。
编写正则表达式很容易,这样它就可以使用任意一种语义,我强烈建议您这样做,以防您想要迁移到另一个解析器生成器,只需确保\与
[^]
不匹配即可:
\"(\\.|[^\\"])*\" return 'STRING'
此更改还将修复一个微妙的错误,即使在javascript中,
“\”
如果是输入中的最后一个字符串,则被视为有效的字符串标记。在本例中,javascript将首先使用\\
匹配\“,但一旦匹配,它将找不到任何收盘报价。然后,它将回溯并尝试与[^”]
匹配,后者将匹配\,从而允许该报价被识别为收盘报价。
CloudGate解析规则可以直接导入使用,不需要任何额外的操作,非常方便! 规则列表 规则名称 下载地址 Surge https://async.be/Rule/Basic/Hosts Shadowrocket https://async.be/Rule/Basic/Hosts 解析规则 简要概述:通过实时同步Hosts信息源达到自动更新,同时使用解析模板进行生成。 无需任何其他操作,导入即可使
template.defaults.rules art-template 可以自定义模板解析规则,默认配置了原始语法与标准语法。 修改界定符 // 原始语法的界定符规则 template.defaults.rules[0].test = /<%(#?)((?:==|=#|[=-])?)[ \t]*([\w\W]*?)[ \t]*(-?)%>/; // 标准语法的界定符规则 template.def
我们在ANTLR中遇到的问题是,我们有这样一个语法: 请记住“鲍勃。”第一行是动态的,可以是任何东西。其中之一就是“鲍勃”。“Bob Offset”行不是动态的,它存在于我们正在解析的每一个类型的文件中。 理想的解决方案是,如果ANTLR有某种方法来指定上下文或解析器规则特定的lexer规则。这样,“Bob offset:”标记在语法中的其他地方就不会出错了。 对此问题的任何想法都将不胜感激。
我有Antlr4中的语法,用来解析和验证定制语言。在其他方面,我的语法应该认为以下是“有效的” //将字符串值赋给变量 //带有字符串参数的函数 //带有特定格式(日期)的函数 下面是我语法中的相关部分
phpGrace url 解析规则,如下: http://www.xxx.com/ = http://www.xxx.com/index/index -------域名-------- --------域名------控制器-方法 -- http://www.xxx.com/index/test --------域名------控制器-方法-- http://www.xxx.com/a
我对是否允许以下情况感到困惑: UPDATE:我知道当我在for循环中提供正确的声明类型时,它就会工作。问题是如果我不这样做会发生什么?