C ++ 我无法弄清楚如何为以下语法编写递归下降解析器:
<Datalog Program> -> Schemes : <Scheme List>
Facts : <Fact List>
Rules : <Rule List>
Queries : <Query List>
<EOF>
<Scheme List> -> <Scheme> <Scheme List Tail>
<Scheme List Tail> -> <Scheme List> | ε
<Scheme> -> <Identifier> ( <Identifier List> )
<Identifier List> -> <Identifier> <Identifier List Tail>
<Identifier List Tail>-> , <Identifier List> | ε
<Fact List> -> <Fact> <Fact List> | ε
<Fact> -> <Identifier> ( <Constant List> ) .
<Constant List> -> <String> <Constant List Tail>
<Constant List Tail> -> , <Constant List> | ε
<Rule List> -> <Rule> <Rule List> | ε
<Rule> -> <Head Predicate> :- <Predicate List> .
<Head Predicate> -> <Identifier> ( <Identifier List> )
<Predicate List> -> <Predicate> <Predicate List Tail>
<Predicate List Tail> -> , <Predicate List> | ε
<Predicate> -> <Identifier> ( <Parameter List> )
<Parameter List> -> <Parameter> <Parameter List Tail>
<Parameter List Tail> -> , <Parameter List> | ε
<Parameter> -> <String> | <Identifier> | <Expression>
<Expression> -> ( <Parameter> <Operator> <Parameter> )
<Operator> -> + | *
<Query List> -> <Query> <Query List Tail>
<Query List Tail> -> <Query List> | ε
<Query> -> <Predicate> ?
这是一个简单的类似数据目录的语法。我在尝试编写解析器时完全迷失了。我已经编写了一个词法分析器,可以输出带有所有标记的向量。我知道我需要为每个生产编写方法,但我无法弄清楚如何将标记连接到解析树(因此我可以在树完成后运行toString()函数)。我需要指出正确的方向。感谢。
答案 0 :(得分:2)
鉴于你已经写过的内容,它主要是从EBNF到C ++源代码的机械翻译,所以(例如),如下规则:
<Scheme> -> <Identifier> ( <Identifier List> )
<Identifier List> -> <Identifier> <Identifier List Tail>
<Identifier List Tail>-> , <Identifier List> | ε
翻译成这个一般顺序的东西(虽然要小心 - 我只是在我头顶输入它,所以它根本没有经过测试;把它想象成类似C的伪代码比任何东西都要按原样编译。
bool scheme() {
return identifier() &&
check_input('(') &&
identifier_list() &&
check_input(')');
}
bool identifier_list() {
if (identifier()) {
if (check_input(','))
return identifier_list();
else
return true;
}
return false;
}
bool identifier() {
// you haven't said what's a legal identifier, so for the moment I'm assuming
// rules roughly like C's.
int ch;
std::string id;
if (!((ch=getinput()) == '_' || isalapha(ch))
return false;
do {
id += ch;
ch = getinput();
} while (isalpha(ch) || isdigit(ch) || ch == '_');
putback(ch);
return true;
}
一旦你正确识别输入(并构建标识符等,就像我上面所做的那样,即使我没有做任何事情)你可以开始构建一个解析树(或其他)添加东西你认出它们到树上。如果你在C中这样做,通常会使用联合。在C ++中,您可能希望使用类层次结构(尽管它将是一个非常短的,浓密的层次结构 - 实际上,很容易就是所有其他直接从基类派生的。)
答案 1 :(得分:1)
要查看语法中的内容并不容易,至少如果一个人不了解Datalog,或者不管语言是什么。
如果您使用不同的Markup for Terminals and Productions,那么不仅可以阅读不知情的内容,而且还可以自己阅读。有可能通过狩猎和向后找出哪一个是哪个,但它似乎让我有点头晕。
看看你的语法,你似乎不会得到一个相当复杂的树。因为所有物品都是订购的。
我的建议是根据您的Grammmar建模树,即数据结构: DatalogProgram Schmema事实规则和查询以及Expression HeadPredicate和Predicate
通过这种方式,您需要做的就是使您的Parser工作看起来像
DatalogProgram parse_datalog_progrem (pos){
DatalogProgram program = new
while ( Scheme s = parse_scheme () )
program.append_scheme (s);
while ( Fact f = parse_fact () )
program.append_scheme (f);
while ( Rule r = parse_rule () )
program.append_rule (r);
while ( Query q = parse_query () )
program.append_query (q);
return program;
}
Scheme parse_scheme () {
Scheme s = new ...
s.id = parse_identifier ();
parse ('(')
while (Identifier i = parse_identifier) {
s.append_id_list (i);
if (lookahead != ',')
break;
parse (',');
}
parse (')');
return s;
}
....
......等等。我想你明白了。
似乎还有其中一种可以解决,但我认为这是最好的方法。你例如可能想考虑在像
这样的数据结构中使用联合struct Parameter {
enum ParamType type;
union {
String str;
Identifier id;
Expression expr;
}
}
距离这里还有很长的路要走,但我希望你喜欢这个方向。
..哦和DatalogProgram类似:
struct DatalogProgram {
struct Scheme *schemes;
struct Fact *facts;
struct Rule *rules;
struct Query *queries;
}
表示解析树的根目录。