Я не верю, что этот подход работоспособен.
Как примечание, вам придется обнаружить ошибку, прежде чем произойдет какое-либо сокращение.Таким образом, для вставки точки с запятой в конце оператора необходимо добавить вывод ошибки к stmt
, а не stmt_list
.Таким образом, вы получите что-то вроде этого:
stmt_list
: %empty
| stmt_list stmt
stmt: value ';' { handle_value_stmt(); }
| value error { handle_value_stmt(); }
| [other types of statements...]
Это не вставляет точку с запятой;он просто делает вид, что точка с запятой была вставлена.(Если точка с запятой не может быть вставлена, будет вызвана другая ошибка.)
Но так как она не включает лексер, произойдет, была ли пропущенная точка с запятой в конце строки., что слишком восторженно.Таким образом, идеальным решением было бы как-то сказать лексеру генерировать токен с запятой в качестве следующего токена.Но в тот момент, когда ошибка обнаружена, лексер уже создал токен lookahead, и анализатор знает, что такое токен lookahead.И он будет использовать свой записанный жетон предпросмотра для продолжения анализа.
Существует также вопрос о том, как можно общаться с лексером на этом этапе, поскольку действия Mid-Rule не очень хорошо работают салгоритм исправления ошибок.Теоретически, вы можете использовать тот факт, что yyerror
будет вызываться для сообщения об ошибке, но это означает, что yyerror
должен иметь возможность сделать вывод, что это не "настоящая" ошибка, а это значит, что она должна бытьтыкать в yyparse
в кишки.(Я уверен, что это возможно, но я не знаю, как это сделать на макушке головы, и мне это не рекомендуется).
Теоретически это так.можно сказать синтаксическому анализатору отбросить токен lookahead, а лексеру - сгенерировать точку с запятой с последующим повторением только что отправленного токена.Так что едва ли возможно, что, взломав взлом на хак, вы могли бы сделать эту работу, если вы достаточно упрямы.Но в итоге вы получите что-то очень сложное в обслуживании, проверке и тестировании.(И убедиться, что это работает во всех угловых случаях, также будет проблемой.)
И это без учета других случаев, когда точки с запятой могут быть вставлены.
Мой подход к ASI заключался в том, чтобыпросто проанализируйте грамматику, выяснив, какие пары последовательных токенов возможны.(Это легко сделать; вам просто нужно создать наборы FIRST и LAST, а затем прочитать все произведения, просматривая последовательные символы.) Затем, если ввод состоит из токена A, за которым следует один или несколько символов новой строки, за которым следует токен B, и онневозможно, чтобы за A следовал B в грамматике, тогда это кандидат на вставку точки с запятой.Вставка точки с запятой может произойти сбой, но это приведет к синтаксической ошибке, поэтому вы не можете получить ложное срабатывание.(Возможно, вам придется исправить сообщение об ошибке синтаксиса, но в этот момент вы хотя бы знаете, что вставили точку с запятой.)
Доказать, что этот алгоритм работает, сложнее, поскольку теоретически это может бытьЗа A
может следовать B
в некотором контексте, но это невозможно в текущем контексте, в то время как A ; B
будет возможно в текущем контексте.В этом случае вы можете пропустить возможную вставку точки с запятой.Я не рассматривал подробно последние версии JS, но давно, когда я написал лексер JS, мне удалось доказать себе, что таких случаев нет.
Примечание: поскольку вопрос был поднят в комментарии, я добавлю немного помахивания руками, хотя я действительно не рекомендую следовать этому подходу.
Без погружения вУ бизона хватит сил, на самом деле невозможно «отменить» токен, включая токен error
(который является настоящим токеном, более или менее).К тому моменту, когда токен error
был смещен, анализ фактически фиксируется для создания ошибки.Поэтому, если вы хотите аннулировать ошибку, вы должны принять этот факт и обойти его.
После смещения токена error
парсер будет пропускать токены до тех пор, пока не встретится токен смещения.Поэтому, если вам удалось вставить автоматическую точку с запятой в поток токенов, вы можете использовать этот токен в качестве защиты:
stmt: value ';' { handle_value_stmt(); }
| value error ';' { handle_value_stmt(); }
Однако, возможно, вам не удалось вставить автоматическую точку с запятой, вв каком случае вам действительно необходимо сообщить об ошибке синтаксиса (и, возможно, попытаться выполнить повторную синхронизацию).Приведенные выше правила просто молча сбрасывают токены до следующей точки с запятой, что, безусловно, неправильно.Таким образом, первое приближение будет для вашего устройства вставки ASI всегда вставить что-то, что может быть использовано в качестве защиты при выдаче ошибок:
stmt: value ';' { handle_value_stmt(); }
| value error ';' { handle_value_stmt(); }
| value error NO_ASI { handle_real_error(); }
Этого достаточно для "сброса при ошибке"обработки, но если вы хотите выполнить восстановление после ошибок, вам нужно сделать еще несколько хакерских атак.
Как я уже сказал, я действительно не рекомендую идти по этому пути.Конечный результат не будет красивым, даже если он работает (и вы все равно можете обнаружить, что код, который, по вашему мнению, работал, не работает при реальном вводе пользователем, в случае, если вы его не рассматривали)