2021-02-18 20:13:55 +00:00
|
|
|
|
## forth
|
|
|
|
|
|
|
|
|
|
В этой задаче нужно написать интерпретатор [языка программирования Forth](https://en.wikipedia.org/wiki/Forth_(programming_language)).
|
|
|
|
|
|
|
|
|
|
Forth - это стек-ориентированный, конкатенативный (конкатенация двух фрагментов кода выражает их композицию) язык программирования.
|
|
|
|
|
|
|
|
|
|
В нашем примитивном случае язык состоит из стека, хранящего целые числа, и словаря известных операций.
|
|
|
|
|
Каждая операция вынимает некоторое (возможно нулевое) количество чисел со стека и кладёт некоторое (возможно нулевое) количество чисел на стек.
|
|
|
|
|
|
|
|
|
|
Полноценный Forth, с ячейками памяти, циклами, условными переходами и множеством стандартных операций
|
|
|
|
|
позволяет писать довольно сложные программы и используется в микропроцессорах.
|
|
|
|
|
|
|
|
|
|
#### Примеры программ
|
|
|
|
|
|
|
|
|
|
В каждом примере на последней строке приведено результирующее значение стека.
|
|
|
|
|
|
|
|
|
|
Положить 3 числа на стек:
|
|
|
|
|
```
|
|
|
|
|
1 2 3
|
|
|
|
|
Stack: 1, 2, 3
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Положить 2 числа на стек, исполнить операцию `+` (достать 2 числа со стека и положить на стек их сумму)
|
|
|
|
|
```
|
|
|
|
|
1 2 +
|
|
|
|
|
Stack: 3
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Положить число на стек и выполнить операцию `drop` (достать верхнее значение из стека)
|
|
|
|
|
```
|
|
|
|
|
1 drop
|
|
|
|
|
Stack:
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Определить операцию ("слово" в терминологии Forth) `drop2` для удаления двух верхних значений со стека,
|
|
|
|
|
положить 2 числа на стек и воспользоваться `drop2`:
|
|
|
|
|
```
|
|
|
|
|
: drop2 drop drop ;
|
|
|
|
|
1 2 drop2
|
|
|
|
|
Stack:
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Грамматика
|
|
|
|
|
|
|
|
|
|
У языка нет официальной грамматики.
|
|
|
|
|
Она определяется простым алгоритмом.
|
|
|
|
|
Интерпретатор считывает строку ввода,
|
|
|
|
|
разбивает на части, используя пробелы в качестве разделителя,
|
|
|
|
|
анализирует каждую часть на принадлежность к лексемам языка ("словам").
|
|
|
|
|
|
|
|
|
|
Все известные языку слова хранятся в словаре.
|
|
|
|
|
Если алгоритм натыкается на знакомое слово, он исполняет связанный с ним исходный код.
|
|
|
|
|
Незнакомые слова интерпретируются как числа и добавляются в стек.
|
|
|
|
|
|
|
|
|
|
В случае успеха интерпретатор продолжает разбор входного потока.
|
|
|
|
|
Если и поиск, и преобразование чисел завершаются неудачно,
|
|
|
|
|
интерпретатор печатает слово, за которым следует сообщение об ошибке, указывающее, что слово не распознано,
|
|
|
|
|
сбрасывает входной поток и ждет нового пользовательского ввода.
|
|
|
|
|
|
|
|
|
|
### Что нужно сделать?
|
|
|
|
|
|
|
|
|
|
Нужно написать реализацию функций `NewEvaluator` и `Evaluator.Process`.
|
|
|
|
|
Первая создаёт объект исполнителя Forth кода, а вторая парсит и исполняет.
|
|
|
|
|
|
|
|
|
|
Требуется поддержать следующие слова:
|
|
|
|
|
* `+`, `-`, `*`, `/` удаляют со стека два верхних значения и кладут результат соответствующей целочисленной арифметической операции
|
|
|
|
|
* `dup` кладёт на стек верхнее значение
|
|
|
|
|
* `over` кладёт на стек второе сверху значение
|
|
|
|
|
* `drop` удаляет верхнее значение со стека
|
|
|
|
|
* `swap` меняет два верхних значения стека местами
|
|
|
|
|
|
|
|
|
|
При нехватке аргументов `Process` должна возвращать ошибки.
|
|
|
|
|
|
|
|
|
|
Также нужно поддержать определение новых слов.
|
|
|
|
|
```
|
|
|
|
|
: word definition ;
|
|
|
|
|
```
|
|
|
|
|
где `word` -- это case-insensitive имя новой операции,
|
2021-02-28 10:46:34 +00:00
|
|
|
|
а `definition` состоит из известных слов и чисел, разделённых пробелами.
|
2021-02-18 20:13:55 +00:00
|
|
|
|
Слова можно переопределять.
|
|
|
|
|
|
2024-06-05 17:36:34 +00:00
|
|
|
|
При реализации механизма определений обратите внимание на юнит-тест
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
{
|
|
|
|
|
description: "no redefinition",
|
|
|
|
|
input: []string{": foo 5 ;", ": bar foo ;", ": foo 6 ;", "bar foo"},
|
|
|
|
|
expected: []int{5, 6},
|
|
|
|
|
},
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
То есть семантика слова вычисляется непосредственно при определении и не меняется при переопределении "зависимостей".
|
|
|
|
|
|
2021-02-18 20:13:55 +00:00
|
|
|
|
### Проверка решения
|
|
|
|
|
|
|
|
|
|
Для запуска тестов нужно выполнить следующую команду:
|
|
|
|
|
```
|
|
|
|
|
go test -v ./forth/...
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### Интерактивная среда
|
|
|
|
|
|
|
|
|
|
В [main.go](./main.go) написана небольшая обёртка вашей реализации,
|
|
|
|
|
позволяющая интерактивно взаимодействовать с интерпретатором.
|
|
|
|
|
```
|
|
|
|
|
go build . && ./forth
|
|
|
|
|
Welcome to Forth evaluator! To exit type "bye".
|
|
|
|
|
>1 2 +
|
|
|
|
|
Stack: 3
|
|
|
|
|
>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Ссылки
|
|
|
|
|
|
|
|
|
|
* https://en.wikipedia.org/wiki/Forth_(programming_language)
|
|
|
|
|
* https://golang.org/pkg/strings/
|