目前我正在开发一个源代码到源代码的编译器,我已经编写了一个bison解析器,可以正确地为输入创建AST。我现在需要对语法树进行几个转换,因此我需要在树中插入许多节点。
我可以手动创建我想添加到语法树中的所有结构/联合,但这似乎是非常繁重的工作。
对我来说,创建一个字符串会容易得多,我希望这个字符串能被我已有的解析器解析。然后,解析器应该返回这个字符串的树,我可以将其插入到我的原始语法树中。
不幸的是,字符串不能用我的解析器的start规则解析,因为它必须由子规则解析(例如,我的解析器解析包含语句的函数列表,字符串是单个语句)。
如何让bison解析字符串,从一个不同于开始规则的规则开始?
提前谢谢!
假代币并不能完全解决问题。当你想要解析某个语言的子规则时,你可能还需要重复调用该规则(多次调用yyparse
)。
这就要求你用伪令牌“初始化”解析器,这会使它在每次调用时返回到有趣的规则,并保持实际非伪令牌流的正确状态。另外,您需要一种方法来检测yyparse
调用是否遇到了EOF。
此外,理想情况下,您希望能够将调用与流上的yyparse
和其他操作混合在一起,这意味着对Flex-Yacc组合执行的先行操作有精确的控制。
我在TXR语言的解析器中解决了所有这些问题。在这种语言中,有一些有趣的子语言:Lisp语法和regex语法。
问题是提供一个Lisp读取函数,它从流中提取单个对象,并使流处于合理的状态(关于前瞻)。例如,假设流包含以下内容:
(a b c d) (e f g h)
我们使用到达Lisp子语法所需的伪标记初始化解析器。然后调用yyparse
。当yyparse
完成时,它将消耗到这里的所有内容:
(a b c d) (e f g h)
^ stream pointer is here
^ the lookahead token is the parenthesis
在这个调用之后,如果有人调用函数从流中获取字符,他们将获得e
,而不是(
括号,很遗憾)。
总之,我们调用了yyparse
,得到了(abcd)
Lisp对象,流指针位于e
,而先行标记是(
)。
下一次调用yyparse
,它将忽略这个先行标记,我们将得到一个错误消息。我们不仅必须使用伪伪标记来初始化解析器,使其解析Lisp表达式,还必须让它在(
先行标记)处开始解析。
这样做的方法是将此令牌插入启动流。
在TXR解析器中,我实现了一个token stream对象,它最多可以占用四个pushback令牌。当调用yylex
时,将从该回推中提取令牌,并且只有当其为空时才执行真正的lexing。
这在prime_解析器
函数中使用:
void prime_parser(parser_t *p, val name, enum prime_parser prim)
{
struct yy_token sec_tok = { 0 };
switch (prim) {
case prime_lisp:
sec_tok.yy_char = SECRET_ESCAPE_E;
break;
case prime_interactive:
sec_tok.yy_char = SECRET_ESCAPE_I;
break;
case prime_regex:
sec_tok.yy_char = SECRET_ESCAPE_R;
break;
}
if (p->recent_tok.yy_char)
pushback_token(p, &p->recent_tok);
pushback_token(p, &sec_tok);
prime_scanner(p->scanner, prim);
set(mkloc(p->name, p->parser), name);
}
解析器的recent_-tok
成员跟踪最近看到的令牌,这使我们能够从最近的解析中访问前瞻令牌。
为了控制yylex
,我在解析器中实现了这个黑客程序。l
:
/* Near top of file */
#define YY_DECL \
static int yylex_impl(YYSTYPE *yylval_param, yyscan_t yyscanner)
/* Later */
int yylex(YYSTYPE *yylval_param, yyscan_t yyscanner)
{
struct yyguts_t * yyg = convert(struct yyguts_t *, yyscanner);
int yy_char;
if (yyextra->tok_idx > 0) {
struct yy_token *tok = &yyextra->tok_pushback[--yyextra->tok_idx];
yyextra->recent_tok = *tok;
*yylval_param = tok->yy_lval;
return tok->yy_char;
}
yy_char = yyextra->recent_tok.yy_char = yylex_impl(yylval_param, yyscanner);
yyextra->recent_tok.yy_lval = *yylval_param;
return yy_char;
如果token pushback索引为非零,我们将弹出封装的token并将其返回给Yacc。否则我们称之为真正的lexeryylex_impl
。
注意,当我们这样做的时候,我们还可以窥视lexer返回的内容,并将其保存在最近的_-tok中。yy_char和最近的_tok。yy_lval
。
(如果yy_lval
是一个堆分配的对象类型呢?幸好我们在这个项目中有垃圾收集功能!)
在匹配这些子语言的规则中,我必须使用yyapt
。请注意,byacc_fool
business:这是让黑客与伯克利Yacc合作所需要的。(T.E.Dickey维护的版本,支持Bison可重入解析器方案。)
spec : clauses_opt { parser->syntax_tree = $1; }
| SECRET_ESCAPE_R regexpr { parser->syntax_tree = $2; end_of_regex(scnr);
| SECRET_ESCAPE_E n_expr { parser->syntax_tree = $2; YYACCEPT; }
byacc_fool { internal_error("notreached"); }
| SECRET_ESCAPE_I i_expr { parser->syntax_tree = $2; YYACCEPT; }
byacc_fool { internal_error("notreached"); }
| SECRET_ESCAPE_E { if (yychar == YYEOF) {
parser->syntax_tree = nao;
YYACCEPT;
} else {
yybadtok(yychar, nil);
parser->syntax_tree = nil;
} }
| SECRET_ESCAPE_I { if (yychar == YYEOF) {
parser->syntax_tree = nao;
YYACCEPT;
} else {
yybadtok(yychar, nil);
parser->syntax_tree = nil;
} }
| error '\n' { parser->syntax_tree = nil;
if (parser->errors >= 8)
YYABORT;
yyerrok;
yybadtok(yychar, nil); }
;
}
为什么要接受?我不记得了;好在我们有详细的变更记录信息:
* parser.y (spec): Use YYACCEPT in the SECRET_ESCAPE_E clause for
pulling a single expression out of the token stream. YYACCEPT
is a trick for not invoking the $accept : spec . $end production
which is implicitly built into the grammar, and which causes
a token of lookahead to occur. This allows us to read a full
expression without stealing any further token: but only if the
grammar is structured right.
我认为这一评论略带误导性。隐式的$end
生产导致了一个问题,这个问题不仅仅是不必要的前瞻性问题:它是前瞻性的,因为它实际上想要匹配EOF。我似乎记得,yyaccpt
是一种退出解析器的方法,这样当下一个标记不是$end
标记时,它就不会抛出语法错误,后者是EOF的内置表示。
不管怎样,Yacc最终还是展望未来;我们不希望它产生语法错误,因为前瞻并不像规则所预期的那样是文件的结尾。当我们有
(a b c d) (e f g h)
我们有一个简单的语法规则来匹配一个表达式,(ef g h)
东西看起来像是散乱的材料,这是一个语法错误!解析器获得第一个)
标记后,它再次调用yylex
,并得到(
,这是一个语法错误;它希望yylex
在该点指示EOF。yyaccpt
是一种解决方法。我们让Yacc调用yylex
,并取出第二个(
,并记下它,以便我们可以在下一个yyparse
调用中将其推回;但我们防止Yacc对该令牌产生不适。
考虑到你似乎愿意做的事情,你可能会从这篇论文中找到有趣的想法。
它提供了从C代码支持具体语法的方法,比如(这里,从Bison解析器本身进行去语法):
exp: exp "&" exp
{
// Sub−ASTs are used as−is (they are not printed, then parsed,
// as in the previous example). Spaces are no longer critical.
$$ = parse(Tweast() <<
"if" << $1 << "then" << $3 << "<> 0 else 0");
};
有一个简单的黑客,这在野牛FAQ中有描述。。
基本上,对于您希望能够使用的每个非终端,您创建一个伪令牌,然后创建一个“元开始”非终端,选择您想要使用的终端:
%token START_PROGRAM
%token START_STATEMENT
%token START_EXPRESSION
%start meta_start
%%
meta_start: START_PROGRAM program
| START_STATEMENT statement
| START_EXPRESSION expression
;
(在每个产品的操作中,您可以将2美元
的价值保存在调用者可以获得的地方。)
现在您只需要安排您的lexer来传递正确的开始标记。您可以通过使用纯解析器和纯lexer并通过共享数据结构传递消息来做到这一点。这将是最好的方法,但是为了这个答案的目的,我将展示如何使用全局变量来实现它,因为原理是相同的:
extern int start_token;
// If all your start non-terminals produce a value of the same type, possibly a union
// type, you could arrange to return it instead of the error report.
int yyparse(int start) {
// I left out the code to cause the lexer to scan a given string.
start_token = start;
return real_yyparse();
}
int yylex() {
int rv = start_token;
if (start_token)
start_token = 0;
else
rv = real_yylex();
return rv;
}
我想在Esper中编写一条规则,当过去15分钟内的步数为0且心率高于120时触发。我想出了以下规则: 我的心率类别有以下字段: 我的Steps类有以下字段: 我面临的问题是,我只想在过去15分钟内没有采取任何措施的情况下启动规则。现在,当两个步骤事件的步骤数相同时,它将激发。我知道我可能必须使用计时器。但是我不知道如何写这个规则。谁能帮帮我吗?
我最近开始使用Esper编程,我有一个智能可穿戴设备,可以将计步器数据发送到我的笔记本电脑。然后,我使用esper处理这些数据。但假设我有多个智能穿戴设备,每个都有一个唯一的MAC地址。我使用时间窗口,我的问题是如何更改规则文件,使规则仅对具有相同MAC地址的事件触发,并基于此MAC地址采取适当的操作。我的初始化和规则是: “我的步骤”类具有以下字段: 以下是我插入事件的方式: 这是我的输出: 为
我想达到的,大致可以概括为一个简单的或者有条件的。 虽然使用Fluent验证支持其他属性上的条件属性验证(When/Unless ),但似乎没有一种方法支持同一属性上的条件规则。还是我错过了什么? 本质上我想实现: 唯一的替代方法是将规则组合在一个自定义规则中? 然而,这紧密地耦合了规则逻辑,实际上它们是完全独立的条件,我可能想在其他对象/字段上使用。 有人建议在 FluentValidation
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
Drools Guvnor有自己的版本控制系统,在生产使用中允许应用程序的用户修改规则和决策表,以适应业务的变化。然而,同样的资产仍然存在于开发版本控制系统中,在该系统中开发了应用程序的新功能。 这篇文章是为了寻找在使用Drools rules和Guvnor时关于规则开发和部署的见解/想法/经验。 下面是一些我一直困惑的关键概念。 首先,将drl文件和决策表部署到正式生产环境的最佳方式是什么?只需