Ирония: как запретить пробел между 2 токенами?

#c# #grammar #irony

#c# #грамматика #ирония

Вопрос:

Я пытаюсь определить переменные в стиле PHP в Irony следующим образом:

 variable.Rule = "$"   identifier;
  

Отлично работает, за исключением того, что вам разрешено ставить пробелы между $ и identifier . Я хочу предотвратить это. Как?

Должен ли я создавать новый настроенный терминал? Если да, смогу ли я по-прежнему воспользоваться IdentifierTerminal магией?


Покопавшись IdentifierTerminal , я вижу, что на самом деле есть флаг для «NameIncludesPrefix», но он используется только в одном месте. Похоже, что префикс хранится в этом CompoundTokenDetails объекте… который я не уверен, как использовать. Редактировать: Неважно, это был тупик. Эти флаги предназначены для добавления модификаторов к поведению переменной.


Это вроде как работает…

 class VariableTerminal : Terminal
{
    public VariableTerminal(string name) : base(name)
    {
    }

    public override IList<string> GetFirsts()
    {
        return new[] { "$" };
    }

    public override Token TryMatch(ParsingContext context, ISourceStream source)
    {
        if (source.PreviewChar != '$') return null;
        do
        {
            source.PreviewPosition  ;
        } while (!source.EOF() amp;amp; char.IsLetter(source.PreviewChar));

        var token = source.CreateToken(OutputTerminal);
        return token;
    }
}
  

Хотя я не совсем уверен, что OuputTerminal такое.. Я предполагаю, что это какое-то динамическое свойство, основанное на текущей позиции предварительного просмотра? Способ синтаксического анализа, выполняемый в Irony, мне кажется немного странным…

В любом случае, проблема с этим заключается в том, что когда я использую это VariableTerminal , вместо того, как я делал это раньше с "$" IdentifierTerminal" , возникает синтаксическая ошибка, например, в этом коде:

 p cat
  

Терминал идентификатора обычно говорил

Ожидаемая синтаксическая ошибка: { реальная строка $ true false …

Но вместо этого переменная выдает мне эту ошибку:

Недопустимый символ: ‘c’

Я думаю, что предыдущая ошибка была более полезной. Я действительно не понимаю, почему он выдает другую ошибку … как я могу заставить его сказать это вместо этого?

Комментарии:

1. (Я пытался придумать шутку «Какая ирония!» с тех пор, как увидел это, но не придумал ни одной …)

Ответ №1:

для меня кажется очевидным, что то, что вы хотите, в настоящее время не поддерживается (проверено в источниках). Смотрите также обсуждение символа pascal (того самого botoom), который идентифицируется как ‘#number’, не допускающий пробел между ними.

Я не верю, что использовать нетерминальный способ. Грамматики работают так, что между токенами могут быть пробелы. Итак, что вам действительно нужно, так это следовать рекомендациям, приведенным в project wiki — разделе Пользовательские терминалы внизу страницы, и расширить класс Terminal в соответствии с вашими потребностями.

Или самым простым вариантом было бы ввести флаг, который может сделать префикс обязательным. Расширение IdentifierTerminal класса и переопределение TryMatch метода.

Если вы посмотрите на этот метод в CompoundTerminalBase классе, что делает TryMatch метод, в основном:

  1. ReadPrefix (но более менее игнорируйте, был ли найден префикс или нет)
  2. ReadBody (завершается ошибкой, если тело не было прочитано)
  3. ReadSuffix

ReadPrefix Метод устанавливает details.Prefix флаг, если найден префикс. Итак, после вызова ReadPrefix вы можете захотеть проверить свой недавно введенный флаг на наличие обязательного префикса, и если он установлен, вы можете проверить, установлен ли также details.Prefix флаг, в противном случае вы выдаете ошибку.

Удачи 🙂

Комментарии:

1. Я еще не пробовал это, но я дам вам 250 до истечения срока его действия. Выглядит неплохо.

2. Дайте мне знать, как все прошло, особенно, если вы не разобрались в этом.

3. Приближаемся… смотрите обновление моего вопроса? Может быть, у вас есть другое предложение?

Ответ №2:

Я не знаю, какую версию Irony вы используете, но с текущей версией я смог заставить это работать, используя AllFirstChars:

         var localVariable = new IdentifierTerminal(NodeType.LocalVariable);
        localVariable.AllFirstChars = "$";
  

Надеюсь, это поможет

Комментарии:

1. Хм … может быть, это что-то новенькое. Не помню, чтобы видел это. Придется попробовать это, если я когда-нибудь вернусь к этому проекту.

Ответ №3:

Не уверен, что это может помочь:

http://irony.codeplex.com/discussions/70460

Итак, разделяем его на 2 строки:

   var identifier = new IdentifierTerminal("Identifier", IdFlags.NameIncludesPrefix);
  identifier.AddPrefix(Strings.AllLatinLetters, IdFlags.None);   //[a-zA-Z]([a-zA-Z0-9])
  

Я думаю, вы не будете использовать их точно так же, но, возможно, что-то похожее.

Комментарии:

1. Нет. Префикс по-прежнему необязателен. Я хочу, чтобы это было обязательным.

Ответ №4:

 var identifier = new IdentifierTerminal("identifier", IdFlags.NameIncludesPrefix);
identifier.AddPrefix("$", IdFlags.None);
  

должно сработать.

Комментарии:

1. Это почти буква в букву то, что сказал Мохаммед … это не делает того, на что похоже.

Ответ №5:

Я согласен с Яном, что это должно обрабатываться в сканере, а не в анализаторе.

Делает ли включение ‘$’ в extraFirstChars то, что вы хотите?

 public IdentifierTerminal(string name, string extraChars, string extraFirstChars)