题
我的日常工作包括合作开发一类Pascal编译器。我一直都沿着优化和代码生成。
我也想开始学习打造为同一种语言的简单语法分析器。不过我,真的不知道如何去了解这一点。 Flex和野牛似乎是选择。但是,是不是可以用C ++或C#写一个解析器?我有点爬行以C
的Yacc ++支持C#,但它是一个授权的一个。我在寻找的一切,我可以在这方面找到帮助。建议,将不胜感激。
解决方案
我相信你可以使用ANTLR用C#。我从来没有尝试过自己(还),但有一个教程的此处可能指向您在正确的方向。
其他提示
我个人推出自己词法分析器和解析器(LL)。这里是一个非常,缩写的例子。这是C ++,但希望你能适应它。它利用一个宏PARSE_HIGHER,使其容易插入在不同的优先级级别的运营商没有太多的代码改变。
// routine to scan over whitespace/comments
void ScanWhite(const char* &pc){
while(true){
if(0);
else if (WHITESPACE(*pc)) pc++;
else if (pc[0]=='/' && pc[1]=='/'){
while(*pc && *pc++ != '\n');
}
else break;
}
}
// routine to lex an identifier
bool SeeId(const char* &pc, string sId){
ScanWhite(pc);
const char* pc0 = pc;
if (alpha(*pc)){
sId = "";
while(alphanum(*pc)) sId += (*pc++);
return true;
}
pc = pc0;
return false;
}
// routine to lex a number
bool SeeNum(const char* &pc, double &dNum){
ScanWhite(pc);
const char* pc0 = pc;
if (digit(*pc)){
dNum = 0;
while(digit(*pc)) dNum = dNum * 10 + (*pc++ - '0');
if (*pc == '.'){
double divisor = 1, frac = 0;
while(digit(*pc)){
divisor *= 0.1;
frac += (*pc++ - '0') * divisor;
}
dNum += frac;
}
return true;
}
pc = pc0;
return false;
}
// routine to lex some constant word
bool SeeWord(const char* &pc, const char* sWord){
ScanWhite(pc);
const char* pc0 = pc;
int len = strlen(sWord);
if (strncmp(pc, sWord, len)==0 && !alphanum(pc[len])){
pc += len;
return true;
}
pc = pc0;
return false;
}
// routine to lex a single character like an operator
bool SeeChar(const char* &pc, const char c){
ScanWhite(pc);
const char* pc0 = pc;
if (*pc == c){
pc++;
return true;
}
pc = pc0;
return false;
}
// primitive expression parser
void ParsePrimitiveExpr(const char* &pc, CNode* &p){
double dNum;
char sId[100];
if (0);
else if (SeeNum(pc, dNum)){
p = new CNode(dNum);
}
else if (SeeId(pc, sId)){
// see if its a function call
if (SeeChar(pc, '(')){
p = MakeNewFunctionCallNode(sId);
while(!SeeChar(pc, ')')){
CNode* p1 = null;
ParseExpression(pc, p1);
AddArgumentExprToFunctionCallNode(p, p1);
SeeChar(pc, ','); /* optional comma separator */
}
}
// otherwise its just a variable reference
else {
p = new CNode(sId);
}
}
// handle embedded expressions
else if (SeeChar(pc, '(')){
ParseExpression(pc, p);
if (!SeeChar(pc, ')')) /* deal with syntax error */
}
}
#define PARSE_HIGHER ParsePrimitiveExpr
// product parser
void ParseProduct(const char* &pc, CNode* &p){
PARSE_HIGHER(pc, p);
while(true){
if (0);
else if (SeeChar(pc, '*')){
CNode p1 = null;
PARSE_HIGHER(pc, p1);
p = new CNode('*', p, p1);
}
else if (SeeChar(pc, '/')){
CNode p1 = null;
PARSE_HIGHER(pc, p1);
p = new CNode('/', p, p1);
}
else break;
}
}
#undef PARSE_HIGHER
#define PARSE_HIGHER ParseProduct
// sum parser
void ParseSum(const char* &pc, CNode* &p){
PARSE_HIGHER(pc, p);
while(true){
if (0);
else if (SeeChar(pc, '+')){
CNode p1 = null;
PARSE_HIGHER(pc, p1);
p = new CNode('+', p, p1);
}
else if (SeeChar(pc, '-')){
CNode p1 = null;
PARSE_HIGHER(pc, p1);
p = new CNode('-', p, p1);
}
else break;
}
}
#undef PARSE_HIGHER
// can insert more routines like the above
// to handle move operators
#define PARSE_HIGHER ParseSum
// overall expression parser
void ParseExpression(const char* &pc, CNode* &p){
PARSE_HIGHER(pc, p);
}
增加了一些帕斯卡式的语句的语法:
void ParseStatement(const char* &pc){
char sId[100];
if(0);
else if (SeeWord(pc, "begin")){
while(!SeeWord(pc, "end")){
ParseStatement(pc);
SeeChar(pc, ';');
}
}
else if (SeeWord(pc, "while")){
CNode* p1 = null;
ParseExpression(pc, p1);
ParseStatement(pc);
/* semantics for while statement */
}
else if (SeeWord(pc, "if")){
CNode* p1 = null;
ParseExpression(pc, p1);
ParseStatement(pc);
if (SeeWord(pc, "else")){
ParseStatement(pc);
}
/* semantics for if statement */
}
else if (SeeWord(pc, "for")){
/* you do it */
}
// handle assignments and subroutine calls
else if (SeeId(pc, sId)){
if(0);
else if (SeeChar(pc, '=')){
CNode* p1 = null;
ParseExpression(pc, p1);
/* semantics for assignment statement */
}
else if (SeeChar(pc, '(')){
CNode* p = MakeNewFunctionCallNode(sId);
while(!SeeChar(pc, ')')){
CNode* p1 = null;
ParseExpression(pc, p1);
AddArgumentExprToFunctionCallNode(p, p1);
SeeChar(pc, ','); /* optional comma separator */
}
}
else {
/* we have a 1-word statement, which is OK in pascal */
}
}
else {
/* syntax error */
}
}
它仍然需要语法数组索引,变量声明和函数定义,但我希望它是清楚如何做到这一点。
在他的经典编程文本,算法+数据结构=程序,尼克劳斯·维尔特开发一种简单Pascal0状语言整个递归下降语法分析器(以帕斯卡)。
如果你在使用Java编写它,我建议ANTLR。这是一个不错LL(*)分析器发电机用Java编写的。有一个了不起的书,它在亚马逊了。
野牛&挠曲是规范解析器生成器。如果你有兴趣在C ++中,我发现提振精神有用。我从来没有使用过它的任何东西一样复杂的编译器虽然。我相信其他人将有其他语言如C有趣的建议#...
您可以实际使用挠曲&野牛与C ++。在本教程,例如,你可以看到,第5节是致力于这一问题。只是谷歌它,我敢肯定,你会发现很多的例子。
我已经写有挠曲和野牛的XSLT解析器。更多最近我正在做使用ANTLR的一个项目,但:
是JFig语言语法(比Spring框架的XML DSL和更好的)有效和明确?
我喜欢在ANTLR工作程度远远超过了Flex和野牛。 ANTLR使您在抽象的,在某些方面上一个台阶。词法定义和语法分析器都可以去在一个文件中。 (ANTLR将生成令牌文件。)
其中一个关键项目是定义树语法的能力。基本上你在输入语言的语法解析和有复写高度优化AST树输出(这仍然为关联数据结构在内存中)的行动。然后,您可以将此树传递给在单独的树分析器文件中定义的另一个解析器。树分析器是你做你想做的行动项目的实际工作。
这是一个很好的方法,因为你可以保持AST的形式,并多次根据需要重新处理 - 剥离特定的子树节点基于后者的行动,打击处理等想想有点像语言解释器。代替从底层向上一遍一遍进入一个for循环和处理语言,可以只处理通过它的AST表示。
在我来说,我已经设计了一个bean工厂做的IoC依赖注入。我的bean工厂保持一个bean描述符的AST在周围运行。当需要作出(或检索)一个新的bean实例,我只是通过这个bean描述AST树到我的树分析器 - 结果是所需的bean实例(也有很多的因素去确定如何实例化请求的豆,包括制造被引用和/或经由元属性应用其他特殊行为)任何其他豆类。
最后,我目前的bean工厂的目标是Java的,但我想针对的ActionScript3和C#.NET。 ANTLR具有用于这样做支持。
如前所述,泰伦斯帕尔写了一本书关于如何使用ANTLR。这是针对工作需要做一些实事ANTLR的(而不是主题的学术处理)。
程序员如果您想要C#按本问题尝试花园点GPPG和GPLEX。
当您使用Lex和Yacc你实际上并没有写任何东西在C 莱克斯是它自己的语言,是Yacc的。所以,你写在莱克斯词法分析器和的Yacc 一个解析器>。然而,对于帕斯卡,Lex和Yacc输入是已经可用。
将所得的解析器和词法分析器具有I2C接口,这是真的。然而,大多数语言,包括C ++,简单的方法来调用(或包裹)C接口。
我不是这方面的专家,但我敢肯定,以上所有的无二ANTLR为好。
如果你问做,在“纯C ++”(知道是什么意思),考虑使用升压精神。我实在不明白在理论纯度点,如果它会导致一吨多的工作,虽然。
手工编写自己的词法分析器和解析器实际上是有点乐趣。词法分析器是的,你可以同时使用的转到和预处理证明极少数的情况之一。不过,我不会建议它像帕斯卡尔一个成熟的语言,如果你能避免它。这将是大量的工作。我说的是人 - 年。