#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
метод, в основном:
- ReadPrefix (но более менее игнорируйте, был ли найден префикс или нет)
- ReadBody (завершается ошибкой, если тело не было прочитано)
- 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)