Первое, что нужно сделать, когда дерево разбора кажется обрезанным, - это проверить, есть ли какие-либо синтаксические ошибки, так как это будет наиболее распространенной причиной. Поскольку вы вообще не связывались с обработкой ошибок в своем коде, это означает, что любые синтаксические ошибки должны быть напечатаны в stderr. Так как их нет, по-видимому, не было никаких синтаксических ошибок.
Но давайте пока не отказываться от идеи о том, что синтаксическая ошибка только что возникла. Одна распространенная ошибка, когда дело доходит до синтаксических ошибок в ANTLR, это если ваше правило запуска не заканчивается на EOF
. Если это так, ANTLR просто попытается найти префикс ввода, который синтаксически допустим, и проигнорирует все остальное. То есть он остановится на первой синтаксической ошибке без фактического создания сообщения об ошибке (при условии, что существует допустимая программа, приводящая к этой ошибке - поскольку многие грамматики принимают пустые программы, что очень часто имеет место). И конечно же: если мы посмотрим на Scala.g4
, то в грамматике нигде нет EOF (во всяком случае, на момент написания этой статьи). Итак, давайте добавим EOF
в конце правила compilationUnit
. Теперь, если мы все перекомпилируем и снова запустим ваш код, мы, наконец, получим синтаксическую ошибку:
line 1:20 mismatched input 'Foo' expecting {<EOF>, '.', ',', 'implicit', 'lazy', 'case', '@', 'override', 'abstract', 'final', 'sealed', 'private', 'protected', 'import', 'class', 'object', 'trait', 'package'}
Теперь есть две вещи, которые могут показаться вам любопытными:
- Почему ANTLR обнаруживать синтаксическую ошибку при запуске из вашего кода, но не из TestRig GUI (даже после добавления
EOF
, GUI все равно покажет правильное дерево). - Почему появляется сообщение об ошибке утверждаете, что
Foo
появляется в столбце 20 строки 1, когда он фактически находится в строке 3?
Ответ на оба эти вопроса один и тот же: вводимые вами ANTLR данные не соответствуют в вашем тестовом файле. Чтобы убедиться в этом, попробуйте напечатать fileContents
после того, как прочитаете его. Вы увидите, что весь ввод находится в одной строке, начиная с import Thing._class Foo
, что явно не соответствует синтаксису.
Причина этого в том, что getLines
дает вам список строк без окончаний строк, а mkString
объединяет их без разделителя. Быстрое решение состоит в том, чтобы просто передать "\n"
в качестве разделителя в mkString
, но лучшее решение - вообще не читать файл.
Вместо этого вы можете заставить ANTLR делать это, создавая свой ввод поток с использованием CharStreams.fromFileName
. Это также избавит от предупреждения об устаревании ANTLRInputStream
.