bmstu-iu9 / bmstu-iu9/refal-5-lambda
Обработка исключений
- Dominant language
- C++
- Stars
- 97
- Forks
- 40
- PR merge metrics
- No merged PRs in 30d
Description
# Мотивация
Встроенные функции ввода-вывода Рефала-5 не могут сообщить об ошибке, собственно, ввода-вывода. Если файла не существует, то `Open` аварийно остановит программу. Та же проблема будет при попытке писать файл, если на диске закончилось место. И т.д.
Рефал-5 — экспериментальный язык, язык для исследований в сфере преобразования программ на Рефале (и как входной язык для таких преобразователей, и как язык реализации). Поэтому и библиотека встроенных функций ограничена некоторым достаточным минимумом, и сами функции имеют ограниченную семантику.
Рефал-5λ позиционируется как промышленный язык. А в промышленных языках ошибочные ситуации в операциях ввода-вывода нужно перехватывать и корректно обрабатывать. Аварийные падения допустимы для внутренних ошибок (вроде SEGFAULT’ов или деления на нуль), но не для нештатных ситуаций взаимодействия со внешней средой.
Хорошо известны два способа обработки подобных внештатных ситуаций — возврат кода ошибки и выбрасывание исключения (которое можно затем перехватить и обработать).
Возврат кода ошибки довольно просто и естественно реализуется в Рефале. Например, та же функция открытия файла (назовём её `SafeOpen`) могла бы иметь следующий формат:
```
== Success
== Fails e.ErrorMessage
== Success s.FileNo
== Fails e.ErrorMessage
```
Но у этого подхода есть два недостатка:
* Вводится новая несовместимая функция. Это значит, что унаследованный код на Рефале-5 остаётся без контроля ошибок.
* У неё сложный формат — после вызова нужно производить сопоставление с образцом и предусматривать разумную реакцию на ошибочную ситуацию. Либо тащить сообщение об ошибке вверх по стеку.
Поэтому предлагается реализовать исключения.
# Библиотечные средства для работы с исключениями
Никакой новый синтаксис не вводится, поскольку для работы с исключениями в минимальном варианте достаточно ввести три новых функции (разумеется, они потребуют правок рантайма).
Первая функция — это генерация исключения
```
== нет возврата
t.FuncCall ::= (s.FUNCTION e.Arg)
e.Arg, e.ErrorMessage ::= e.ANY-EXPR
```
**Семантика.** Если исключение не перехвачено (см. далее), то функция выводит в файл дампа `e.ErrorMessage` (в том же виде, что и `Prout`), заменяет свой вызов на `t.FuncCall` (но с угловыми скобками) и возвращает `refalrts::cRecognitionImpossible`. Таким образом, останов будет выглядеть, как будто упал вызов `t.FuncCall`, но с дополнительным текстом ошибки.
**Пример.**
```
```
Напечатается `FileNotFound`, вызов `` заменится на `` и программа остановится с выдачей аварийного дампа.
Вторая функция — это конструкция перехвата исключения:
```
== e.Result
t.Callable ::= s.SingleFunction | (s.ArgFunction e.Arg)
s.SingleFunction, s.ArgFunction ::= s.FUNCTION
== e.Result
== e.Result
== e.Result
```
**Семантика.** Первый аргумент функции `t.Callable` — вызываемая функция, которая может упасть. Второй аргумент — обработчик исключения. Если функция, заданная в `t.Callable`, завершилась без исключений, то её возвращаемое значение совпадает с возвращаемым значением функции `Try`. Если исключение произошло (была где-то вызвана функция `Raise`), то вызывается `s.CatchFunction` с аргументом вызова `Raise`. Возвращаемое значение обработчика становится возвращаемым значением функции `Try`.
**Пример.** Немножко надуманный, но иллюстрирует.
```
;
}
Catch
{
(&FOpen 'r' e.FileName^) FileNotFound = Fails FileNotFound e.FileName;
(&FOpen 'r' e.FileName^) PermissionDenied = Fails PermissionDenied e.FileName;
t.Callable e.ErrorMessage = ;
}
>
```
В примере встретилась третья функция — `ReRaise`, которая служит для продолжения обработки исключения внешним перехватчиком.
```
```
**Семантика.** Может быть вызвана только из `s.CatchFunction`. Передаёт управление наружному обработчику исключения, как если бы предыдущего обработчика `Try` не было. Если больше нет обработчиков, программа выводит аварийный дамп, если есть — исключение перехватывается в следующем из них.
Преимущества выбранного подхода:
* Можно перехватывать ошибки встроенных функций ввода-вывода, в форматах самих этих функций ничего не меняется.
* Подход полностью библиотечный, синтаксис менять не надо (но надо дорабатывать рантайм).
* Можно выдавать разумные исключения, если функция написана на Рефале. Например, если упадёт функция `Putout`, то в дампе будет виден ошибочный вызов `Putout`, а не `Autoopen` или `Putout-Aux`.
# Какие ошибки перехватывать?
Очевидно, нужно описывать исключениями ошибки функций ввода-вывода (см. мотивацию).
Технически можно генерировать исключение даже для ошибок невозможности отождествления или недостатка памяти (хотя здесь засада в том, что как программа будет продолжать работу, если память закончилась), но надо ли? На мой взгляд — скорее, не надо. Внутренние ошибки должны приводить к падениям, скрывать их нельзя. Но, с другой стороны, операционные системы и языки программирования позволяют перехватывать и ошибки доступа к памяти, и ошибки деления на нуль, значит, это зачем-то может быть нужно.
С делением на нуль тоже не очевидно. Функции длинной арифметики написаны на Рефале, поэтому сейчас сообщение об ошибке деления на нуль в дампе не очевидно. В дампе будет вызов не `
Возможно, для ошибок в вызове встроенных функций (не только деление на нуль, но и любые другие нарушения формата) следует добавить неперехватываемый аналог `Raise`.
Всё это надо обдумать.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with the runtime implementations of built-in I/O and the proposed Raise, Try, and ReRaise entry points. Review the existing seven-comment discussion before deciding the exception semantics and which errors are in scope. Done means an agreed design is implemented in the runtime with coverage for successful calls, caught exceptions, reraising, and uncaught failures.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100