Walter - Discrepancy between EqualExpression spec and implementation

James Dunne james.jdunne at gmail.com
Mon May 29 16:29:58 PDT 2006


Jarrett Billingsley wrote:
> "Jarrett Billingsley" <kb3ctd2 at yahoo.com> wrote in message 
> news:e5d40i$vp4$1 at digitaldaemon.com...
> 
>>I'm writing a small scripting language with a syntax based on D, and as 
>>such, I'm basing some of the parsing code off the the DMD frontend source. 
>>I've found a discrepancy between the spec and the code, though, and I'm 
>>not sure if it's a bug or if it was changed in the code on purpose and the 
>>spec just hasn't been updated.
> 
> 
> I just realized that most of the parsing functions follow this same pattern 
> (of not following the spec).  The only functions that seem to follow the 
> spec are parseAssignExp() and parseCondExp(). 
> 
> 

I've taken a serious look into this too :)  Here's my answer:

See the while loops implemented in most of those functions?

Grammar rule:
-------------
OrOrExpression:
	AndAndExpression
	OrOrExpression || AndAndExpression

DMD implementation:
-------------------
Expression *Parser::parseOrOrExp()
{   Expression *e;
     Expression *e2;
     Loc loc = this->loc;

     e = parseAndAndExp();
     while (token.value == TOKoror)
     {
	nextToken();
	e2 = parseAndAndExp();
	e = new OrOrExp(loc, e, e2);
     }
     return e;
}

So, on the first iteration we either get an AndAndExpression alone, or 
an OrOrExpression with the left expression being an AndAndExpression and 
the right expression being an AndAndExpression.

The left-recursive descent rule 'OrOrExpression || AndAndExpression' 
means to loop over the left side until a subsequent token is NOT the 
'||' token.  Thus, we can only get an OrOrExpression out of two 
AndAndExpressions next to each other, separated by the '||' token, or 
just an AndAndExpression alone.  So, the code does implement the spec 
correctly.  All the other expression rules follow this pattern.

As to the exceptional case of AssignExpression:

Grammar rule:
-------------
AssignExpression:
	ConditionalExpression
	ConditionalExpression = AssignExpression
	ConditionalExpression += AssignExpression
	ConditionalExpression -= AssignExpression
	ConditionalExpression *= AssignExpression
	ConditionalExpression /= AssignExpression
	ConditionalExpression %= AssignExpression
	ConditionalExpression &= AssignExpression
	ConditionalExpression |= AssignExpression
	ConditionalExpression ^= AssignExpression
	ConditionalExpression ~= AssignExpression
	ConditionalExpression <<= AssignExpression
	ConditionalExpression >>= AssignExpression
	ConditionalExpression >>>= AssignExpression

DMD implementation:
(I've expanded out the #define shortcut for readability)
-------------------
Expression *Parser::parseAssignExp() {
     Expression *e;
     Expression *e2;
     Loc loc;

     e = parseCondExp();
     while (1) {
         loc = this->loc;
         switch (token.value) {
             case TOKassign:  nextToken(); e2 = parseAssignExp(); e = 
new AssignExp(loc,e,e2); continue;
             case TOKaddass:  nextToken(); e2 = parseAssignExp(); e = 
new AddAssignExp(loc,e,e2); continue;
             case TOKminass:  nextToken(); e2 = parseAssignExp(); e = 
new MinAssignExp(loc,e,e2); continue;
             case TOKmulass:  nextToken(); e2 = parseAssignExp(); e = 
new MulAssignExp(loc,e,e2); continue;
             case TOKdivass:  nextToken(); e2 = parseAssignExp(); e = 
new DivAssignExp(loc,e,e2); continue;
             case TOKmodass:  nextToken(); e2 = parseAssignExp(); e = 
new ModAssignExp(loc,e,e2); continue;
             case TOKandass:  nextToken(); e2 = parseAssignExp(); e = 
new AndAssignExp(loc,e,e2); continue;
             case TOKorass:   nextToken(); e2 = parseAssignExp(); e = 
new OrAssignExp(loc,e,e2); continue;
             case TOKxorass:  nextToken(); e2 = parseAssignExp(); e = 
new XorAssignExp(loc,e,e2); continue;
             case TOKshlass:  nextToken(); e2 = parseAssignExp(); e = 
new ShlAssignExp(loc,e,e2); continue;
             case TOKshrass:  nextToken(); e2 = parseAssignExp(); e = 
new ShrAssignExp(loc,e,e2); continue;
             case TOKushrass: nextToken(); e2 = parseAssignExp(); e = 
new UshrAssignExp(loc,e,e2); continue;
             case TOKcatass:  nextToken(); e2 = parseAssignExp(); e = 
new CatAssignExp(loc,e,e2); continue;

             default:
             break;
         }
         break;
     }
     return e;
}

Here, we see that e2 is always evaluated as an AssignExpression, thus 
this rule is right-recursive, not left-recursive as the other pattern. 
Again, note the while loop here.

I'm not sure how to make it clearer, but if you have any questions don't 
hesitate to ask.

P.S. - I've also used this code pattern as a template for my own 
language parser and it does work quite well.

-- 
Regards,
James Dunne



More information about the Digitalmars-d mailing list