Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem handing escaped identifiers in the Repl. #2577

Open
pvillads opened this issue Aug 5, 2024 · 2 comments
Open

Problem handing escaped identifiers in the Repl. #2577

pvillads opened this issue Aug 5, 2024 · 2 comments

Comments

@pvillads
Copy link

pvillads commented Aug 5, 2024

Please consider this set of actions in the REPL where a variable of name '$id' is defined. This variable name is escaped with the single quotes to cater for the otherwise illegal $ character.

a = 2
a: 2

2 + a
4

'$id' = 2
'$id': 2

From there it would appear that there are three variables defined: (a, b, '$id'). However, when I attempt to get the value of the '$id' variable I get into trouble:

'$id'
Error 0-1: Unexpected characters....
... many more messages ...

However, you can get the variable by escaping the escapes:

'''$id'''
2

@pvillads
Copy link
Author

pvillads commented Aug 6, 2024

Here is a simpler repro in C#, that strips away a lot of the overhead:

namespace PowerFxQuotedNamesProblem
{
using Microsoft.PowerFx;
using Microsoft.PowerFx.Types;
using System.Diagnostics;

internal class Program
{
    static void Main(string[] args)
    {
        var engine = new RecalcEngine();

        // Put some variables into the engine scope.
        engine.UpdateVariable("a", 1);
        engine.UpdateVariable("b", 2);

        // Now check that the engine can evaluate expressions with these names.
        var result = engine.Eval("a + b");
        Debug.Assert(result.AsDouble() == 3.0, "Incorrect result when using normal variables");

        // Now add an escaped variable
        engine.UpdateVariable("'$id'", 3);

        // All the names are accounted for.
        Debug.Assert(engine.EngineSymbols.SymbolNames.Count() == 3, "Wrong number of variables");
        Debug.Assert(engine.EngineSymbols.SymbolNames.Select(n => n.Name).ToArray()[0].ToString() == "a", "Incorrect variable name");
        Debug.Assert(engine.EngineSymbols.SymbolNames.Select(n => n.Name).ToArray()[1].ToString() == "b", "Incorrect variable name");
        Debug.Assert(engine.EngineSymbols.SymbolNames.Select(n => n.Name).ToArray()[2].ToString() == "'$id'", "Incorrect variable name");

        // The 3rd variable (with the escaped name) is not found.
        try
        {
            result = engine.Eval("'$id'");
            Debug.Assert(result.AsDouble() == 3.0, "Incorrect result when using escaped variable");
        }
        catch(Exception e)
        {
            Debug.Assert(false, e.Message);
        }

        // There is no variable with this name, yet it returns a result.
        try
        {
            result = engine.Eval("'''$id'''");
            Debug.Assert(false, "Reference to unknown variable yielded a value");
        }
        catch (Exception e)
        {
        }
    }
}

}

It seems that the issue is that when a string (i.e. '$id') is parsed (through the Eval call) to be an identifier, the start and end quotes are stripped. Look in LexIdent (TexlLexer @ 1361). Here the string designating the variable is returned as $id, not '$id'. This is what causes all the mayhem going forward. There is special code to handle the '''$id'' case:

                    if (IsIdentDelimiter(PeekChar(1)))
                    {
                        // Escaped delimiter.
                        _sb.Append(CurrentChar);
                        NextChar();
                        NextChar();
                    }

which causes the name to be left as '$id' and then everything snaps into place. But the name should not be escaped. I want the name to be '$id', I do not want to have understand the name as containing quotes.

@MikeStall
Copy link
Contributor

Note that the single quotes ' are not part of the identifier's name - they're just lexical hints. Much like the quotes in "hello" are not part of the string literal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants