Posted 9 years ago by David
Avatar
Hi, I'm trying to solve a ambiguity issue with 2 non-terminals... 'VariableDeclaration' and 'FunctionDeclaration'

VariableDeclaration >>> 'Var' 'Identifier' 'SemiColon'
FunctionDeclaration >>> 'Var' 'Identifier' 'OpenParenthesis' 'CloseParenthesis' 'SemiColon'

As you can see they both start with the same tokens. How do I handle this?

I've tried...

<% 
if (IsNonTerminal("FunctionDeclaration")) 
{
%>
{ "FunctionDeclaration" }
<% 
}
else if (IsNonTerminal("VariableDeclaration")) 
{
%>
{ "VariableDeclaration" }
<% 
}
else { // error! }
%>
...but it only wants to accept one or the other and can't differentiate between the two.

Is this the right way?

Thanks.

[Modified at 08/27/2010 04:19 AM]

Comments (1)

Posted 9 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi David,

There are two ways to do this sort of thing. The easier version is to combine the common pieces into one non-terminal that will call the others.

So you make a non-terminal that will consume the 'Var' and 'Identifier'. Then you do an alternation between the real VariableDeclaration and FunctionDeclaration non-terminals. You pass in the identifier value that was read since you've already consumed it. In the VariableDeclaration, you'd start looking for the 'SemiColon' and would create the appropriate AST for it in there.

The alternate harder version is to change the match conditions for the non-terminal.

In the C# add-on language we do this for our ImplicitlyTypedLambdaParameterList non-terminal:
<NonTerminal Key="ImplicitlyTypedLambdaParameterList" Parameters="out AstNodeList parameterList">
    <AdditionalConditions>
        <ClearFirstSet />
        <ExpressionCondition>this.IsImplicitlyTypedLambdaParameterList()</ExpressionCondition>
    </AdditionalConditions>
    <Production><![CDATA[
        <%
            parameterList = new AstNodeList(null);                
            ParameterDeclaration parameter;
        %>
        "ImplicitlyTypedLambdaParameter<@ out parameter @><+ parameterList.Add(parameter); +>"
        {
            'Comma'
            "ImplicitlyTypedLambdaParameter<@ out parameter @><+ parameterList.Add(parameter); +>"
        }
    ]]></Production>
</NonTerminal>
The AdditionalConditions lets you specify additional conditions for the non-terminal to match. Here we clear the "first set" of tokens (meaning in your case you'd remove the 'var' condition) and will replace it with a method call to IsImplicitlyTypedLambdaParameterList().

We declare IsImplicitlyTypedLambdaParameterList() above in the Declarations section like this:
/// <summary>
/// Returns whether the current <see cref="IToken"/> is an implicitly typed lambda parameter list.
/// </summary>
/// <returns>
/// <c>true</c> if the current <see cref="IToken"/> is an implicitly typed lambda parameter list; otherwise, <c>false</c>.
/// </returns>
private bool IsImplicitlyTypedLambdaParameterList() {
    if (this.IsIdentifier(this.LookAheadToken)) {
        IToken targetToken = this.GetLookAheadToken(2);
        return ((this.TokenIs(targetToken, CSharpTokenID.Comma)) || (this.TokenIs(targetToken, CSharpTokenID.CloseParenthesis)));
    }
    else
        return false;
}
Thus we have replaced any ambiguity with a method call that can do look-aheads. You'd need to do three token look-ahead to see that the first two tokens are 'Var' and 'Identifier'. Then for VariableDeclaration you'd need to see if it's a 'SemiColon'. For FunctionDeclaration, you'd need to see if it's an 'OpenParenthesis'.

Hope that helps!


Actipro Software Support

The latest build of this product (v2018.1 build 0342) was released 11 days ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.