Общее объяснение сообщения об ошибке
===SORRY!=== Error while compiling ...
Когда вы видите SORRY!
, то вы знаете, что компилятор говорит вам о проблеме, которая произошла во время компиляции, даже до того, как была предпринята попыткачтобы запустить ваш код.
Two terms in a row
Это сводная информация компилятора на английском языке о том, что остановило компиляцию вашего кода.Мы вернемся к этому позже.
------>
- это способ сказать компилятору, что он был сбит с толку тем, что вы написали, и он будет отображать часть вашего кода после ------>
.
⏏
, символ с именем Unicode EJECT SYMBOL
, вставляется на экран вашего кода.Вставленная точка должна помочь в интерпретации сообщения об ошибке.
В этом случае оно указывает между .say
и "VERBOSE..."
.Компилятор считает, что это два термина подряд.
Что такое термин?
Рассмотрим следующий код:
start-time + 42
sum(start-time, 42)
Термины похожи на терминыпо математике .Оба примера выражения включают в себя термины start-time
и 42
.Общее выражение start-time + 42
также является термином.Выражение sum(start-time, 42)
можно назвать термином sum()
или sum(...)
.
Термины также в некоторой степени похожи на существительные или словосочетания в естественном языке .start-time
и 42
подобны существительным, отсюда и термины.start-time + 42
и sum(...)
подобны словосочетаниям, каждая из которых также является термином.
(Кстати, термины, в смысле, относящемся к этому вопросу, не относятся к парсингу«терминалы», которые иногда называют «терминами».)
Теперь вы можете догадаться, что произойдет, если попытаться скомпилировать пример кода, с которого начинался этот раздел:
Two terms in a row across lines (missing semicolon or comma?)
Первая строка (start-time + 42
) - это термин.sum(start-time, 42)
это другой термин.И между ними нет ничего, кроме конца строки.В Perl 6 явно не нравятся два термина подряд без чего-то, что не является термином между ними, а пробелы и концы строк не учитываются.
Как можно избежать ошибки «Два члена подряд»?
Операторы, такие как infix +
, postcircumfix ()
и infix ,
, которые я использовал в приведенных выше примерах, могут использоваться в позициях операторов (до, после, между или вокруг терминов) формировать выражения.(И общие выражения сами по себе являются терминами, как описано выше.)
Ключевые слова, такие как for
или last
, используемые в позиции ключевого слова, также являются , а не терминами (если вы не сошли с умадостаточно переопределить их как термины, и в этом случае вы, вероятно, получите странные ошибки компиляции, которых вы заслуживаете. :)) Но, как и операторы, они должны быть расположены в правильном положении, иначе компилятор может подумать, что они термины.Если вы напишите last
в неправильном месте, компилятор может подумать, что last
- это термин.
Проблема с вашим кодом
Компилятор считает .say
(обратите внимание на пробел вконец) быть термином, эквивалентным .say()
.Таким образом, он интерпретирует то, что вы написали как .say() "VERBOSE..."
, что является двумя терминами подряд.
(Я рекомендую вам просто согласиться с тем, что это так, но если вы хотите углубиться в мелочи синтаксиса вызова методов, чтобы полностью понять, почему invocant.foo ( arrgh, arrgh, ... ) ;
также означает «Два члена подряд», см. мой ответ, охватывающий различные синтаксисы, связанные с обычными вызовами .)
Давайте исправим ваш код, изменив .say
на say
(без .
):
say "VERBOSE \"$_ is the string\"" for $*IN.lines() last when "";
Компилятор возвращает еще одну ошибку «Два члена в строке», но теперь он указывает между $*IN.lines()
и last
.
Ключевое слово for
и его аргумент итерации должны быть либо в началезаявление или в конце заявления.Вы использовали их в конце.
Но это означает, что то, что следует после for
, является термином (его аргумент итерации).
$*IN.lines()
может работать как термин.
Вы можете даже сделать это частью выражения, например.for flat $*IN.lines(), $*FOO.lines()
для циклического перебора как строк ввода, так и строк из некоторого другого дескриптора.(flat
создает единый список для for
, который можно зациклить, сгладив два отдельных списка, один из $*IN.lines()
, другой из $*FOO.lines()
.)
Но вы не построили выражение, вы просто сразу последовали за $*IN.lines()
с last
.
Поскольку нет инфиксного оператора last
и last
должно быть первым словомв операторе для того, чтобы это было ключевое слово last
, компилятор интерпретирует last
как термин - и поэтому он видит «Два термина в ряд».
Вам нужно last
, чтобыбыть ключевым словом оператора, и оно должно быть в контексте цикла for
.Но у вас уже есть утверждение в контексте цикла for
, а именно выражение / термин say ...
.Вам нужно несколько скобок или аналогичных, чтобы позволить вам написать несколько утверждений.Вот один из способов:
{ last when ""; say "VERBOSE \"$_ is the string\"" } for $*IN.lines();
Теперь ваш код работает.
Финальные настройки
Я мог бы также добавить пару финальных настроек:
( last when ''; say qq[VERBOSE "$_ is the string"]; $i++ ) for lines ;
Я переключился с {...}
на (...)
.Он не более компактен, но показывает, что вы можете использовать вместо символов скобок скобки, когда for
записывается как модификатор оператора (т.е. в конце оператора, а не в начале).{...}
создает лексическую область видимости, тогда как (...)
- нет;Есть моменты, когда один или другой - именно то, что вам нужно.
Вам не нужен $*IN.
, потому что есть подпункт lines
, который эквивалентен $*IN.lines()
.
Я удалил ()
после lines
, потому что они не нужны, если нет аргументов после lines
(или $*IN.lines
) и до концазаявление.
Я использовал ''
вместо ""
, потому что я думаю, что это хорошая привычка использовать не интерполирующие кавычки, если вам не нужны интерполяционные.
Я использовал qq[...]
, потому что это означает, что вам не нужно экранировать "
в строке.
У меня естьиспользуется $i++
вместо $i=$i+1
, потому что он достигает того же эффекта, и я думаю, что он читается лучше.