Как реализовать ленивую оценку if ()

10

В настоящее время я реализую оценщик выражений (однострочные выражения, например формулы), основанный на следующем:

  • введенное выражение токенизируется для разделения буквенных логических значений, целых чисел, десятичных дробей, строк, функций, идентификаторов (переменных)
  • Я реализовал алгоритм Shunting-yard (слегка измененный для обработки функций с переменным числом аргументов), чтобы избавиться от скобок и упорядочить операторы с приличным приоритетом в постфиксном порядке.
  • мой маневровый двор просто создает (смоделированную) очередь токенов (с помощью массива мой язык Powerbuilder Classic может определять объекты, но иметь только динамические массивы как собственное хранилище - не истинный список, не словарь), которые я оцениваю последовательно с помощью простая стековая машина

Мой оценщик работает хорошо, но мне все еще не хватает if()и мне интересно, как поступить.

С моей оценкой постфикса и стекового шунтирующего двора, если я добавлю if()как другую функцию с истинными и ложными частями, одно if(true, msgbox("ok"), msgbox("not ok"))сообщение покажет оба сообщения, в то время как я хотел бы показать только одно. Это потому, что когда мне нужно оценить функцию, все ее аргументы уже были оценены и помещены в стек.

Не могли бы вы дать мне способ реализовать if()это ленивым образом?

Я хотя и рассматривал их как своего рода макрос, но на ранних этапах еще не оценил состояние. Возможно, мне нужно использовать другой вид структуры, кроме очереди, чтобы отдельно хранить условие и выражения «истина / ложь»? На данный момент выражение анализируется перед оценкой, но я также планирую сохранить промежуточное представление как вид предварительно скомпилированного выражения для будущей оценки.

Изменить : после некоторого, хотя по проблеме, я думаю, что я мог бы построить представление моего выражения в виде дерева (AST вместо линейного потока токенов), из которого я мог бы легко игнорировать ту или иную ветвь своего if().

Секи
источник

Ответы:

9

Здесь есть два варианта.

1) Не реализуйте ifкак функцию. Сделайте это языковой особенностью со специальной семантикой. Легко сделать, но менее «чисто», если вы хотите, чтобы все было функцией.

2) Реализовать семантику «вызова по имени», которая намного сложнее, но позволяет магии компилятора позаботиться о проблеме ленивых вычислений, сохраняя ее ifкак функцию вместо элемента языка. Это выглядит так:

ifэто функция, которая принимает два параметра, каждый из которых объявлен как «по имени». Когда компилятор видит, что передает что-то параметру по имени, он изменяет код, который будет сгенерирован. Вместо оценки выражения и передачи значения он создает замыкание, которое оценивает выражение, и вместо этого передает его. А при вызове параметра внутри имени функции компилятор генерирует код для оценки замыкания.

Мейсон Уилер
источник
Я не уверен, но должно ли быть "закрытие" быть "толчком"? Хм, может и нет; только что посмотрел на страницу википедии: "Thunk - закрытие без параметров".
Когда вы говорите «по имени», вы имеете в виду глобально? В качестве альтернативы глобальному вызову по имени просто реализуется тип замыкания, тогда функция if просто берет 3 замыкания и оценивает два (условие и затем или другое), но не все должно распознаваться как замыкание, такое как полный вызов по семантика имени
Джимми Хоффа
@Matt: термин «thunk» может означать несколько других вещей в контексте программирования, и «закрытие без параметров» - не первое, о чем я думаю, когда слышу это. «Закрытие» гораздо более однозначно.
Мейсон Уилер
1
@JimmyHoffa: Когда я говорю «вызов по имени», я имею в виду определенный стиль настройки аргумента функции, который должен быть необязательным. Как и во многих существующих языках, вы можете выбрать передачу параметра по значению или по ссылке, для этого сценария вам нужно выбрать передачу по имени.
Мейсон Уилер
Хотя ваше предложение о семантике «вызова по имени» показало мне некоторые интересные моменты, для моего оценщика это немного излишне, поскольку он не является полным компилятором, поскольку мои вызовы функций являются однострочными (подумайте о формулах MS-Excel). Я думаю, что мог бы добавить шаг после очереди токенов, выполнив псевдо-оценку стека, чтобы вывести дерево вызовов. Должно быть легче узнать по дереву ветви, от которых нужно отказаться.
Секи
3

Вместо функции, имеющей подпись:

object if(bool, object, object)

Дайте ему подпись:

object if(bool, object function(), object function())

Тогда ваша ifфункция будет вызывать соответствующую функцию на основе условия, оценивая только одну из них.

Hand-E-Food
источник
1

Это довольно легко, если вы все компилируете лениво. У вас должны быть какие-то средства, чтобы увидеть, оценивается ли уже значение или требуется дополнительная оценка.

Затем вы можете сделать следующее: Если это литерал или переменная (у вас есть такие ?, т.е. имена функций?), Поместите ее в стек. Если это приложение функции, скомпилируйте ее отдельно и поместите точку входа в стек.

Таким образом, выполнение программы просто зацикливается до тех пор, пока не будет вычислена вершина стека, а не функция. Если он не оценен или не является функцией, вызовите код, на который указывает вершина стека.

Инго
источник