Преобразование Lisp в C ++ - PullRequest
8 голосов
/ 11 апреля 2011

Я работаю над игрушечным языком, который компилируется в C ++ на основе lisp (очень маленького подмножества схемы), я пытаюсь выяснить, как представить выражение let,

(let ((var 10)
      (test 12))
  (+ 1 1)
  var)

Сначала я подумалвыполнить все выражения, а затем вернуть последний, но возвращение убьет мою способность вкладывать выражения let, каков будет способ представления let?

Кроме того, любые ресурсы по преобразованию источника в источник оцениваются, у меня естьгуглил, но все, что я мог найти, это 90-минутный компилятор схемы.

Ответы [ 3 ]

9 голосов
/ 11 апреля 2011

Один из способов расширения let - это трактовать его как lambda:

((lambda (var test) (+ 1 1) var) 10 12)

Затем преобразуйте это в функцию и соответствующий вызов в C ++:

int lambda_1(int var, int test) {
    1 + 1;
    return var;
}

lambda_1(10, 12);

Итак, в более широком контексте:

(display (let ((var 10)
               (test 12))
           (+ 1 1)
           var))

становится

display(lambda_1(10, 12));

Существует намного больше деталей, например, необходимость доступа к лексическим переменным вне let из let. Поскольку C ++ не имеет лексически вложенных функций (в отличие, например, от Pascal), это потребует дополнительной реализации.

4 голосов
/ 12 апреля 2011

Я попытаюсь объяснить наивный подход к компиляции вложенных lambda с.Поскольку объяснение Грега о расширении let в lambda очень хорошее, я вообще не буду обращаться к let, я предполагаю, что let является производной формой или макросом и разворачивается в lambdaформа, которая вызывается немедленно.

Компиляция функций Lisp или Scheme непосредственно в функции C или C ++ будет сложной из-за проблем, возникших у других авторов.В зависимости от подхода полученный C или C ++ не будет распознаваемым (или даже очень читабельным).

Я написал компилятор Lisp-to-C после завершения Структуры и Интерпретации компьютерных программ (это один изЗаключительные упражнения, и на самом деле я обманул и просто написал переводчик из SICP байт-кода в C).Подмножество C, которое оно излучало, вообще не использовало функции C для обработки функций Lisp.Это связано с тем, что машинный язык регистров в главе 5 SICP действительно ниже уровня C.

Предполагая, что у вас есть какая-то форма окружения, которая связывает имена со значениями, вы можете определить суть вызова функции следующим образом: расширить среду, в которой была определена функция, с помощью формальных параметров, связанных с аргументами, а затем оценить тело функции в этой новой среде.

В компиляторе SICP среда содержится в глобальной переменной.и есть другие глобальные переменные, содержащие список аргументов для вызова функции, а также вызываемый объект процедуры (объект процедуры содержит указатель на среду, в которой он был определен) и метку, к которой можно перейтифункция возвращает.

Имейте в виду, что при компиляции выражения lambda есть два синтаксических компонента, которые вы знаете во время компиляции: формальные параметры и тело lambda.

Когда функция компилируется, EMIКод tted выглядит примерно так: псевдокод:

some-label
*env* = definition env of *proc*
*env* = extend [formals] *argl* *env*
result of compiling [body]
...
jump *continue*

... где *env* и *argl* - глобальные переменные, содержащие список окружения и аргументов, а extend - некоторая функция (это может бытьправильная функция C ++), которая расширяет среду *env* путем сопряжения имен в *argl* со значениями в [formals].

Затем, когда запускается скомпилированный код, и существует вызов этого lambda где-то еще в вашем коде, соглашение о вызовах должно помещать результат оценки списка аргументов в переменную *argl*, помещать метку возврата в переменную *continue*, а затем переходить к some-label.

В случае вложенных lambda с, излучаемый код будет выглядеть примерно так:

some-label
*env* = definition env of *proc*
*env* = extend [formals-outer] *argl* *env*
another-label
*env* = definition env of *proc*
*env* = extend [formals-inner] *argl* *env*
result of compiling [body-inner]
...
jump *continue*
rest of result of compiling [body-outer]
... somewhere in here there might be a jump to another-label
jump *continue*

Это немного сложно объяснить, и я уверен, что я запуталсяработа этого.Я не могу придумать достойного примера, который бы не касался меня в основном небрежно, описывая целую главу 5 SICP.Поскольку я потратил время на написание этого ответа, я собираюсь опубликовать его, но мне очень жаль, если он безнадежно запутывает.

Я настоятельно рекомендую SICP и Lispв Small Pieces .

SICP охватывает метациркулярную интерпретацию для начинающих, а также ряд вариантов интерпретатора и компилятор байтового кода, который мне удалось запутать и покалечить выше.Это только две последние главы, первые 3 главы также хороши.Это прекрасная книга.Абсолютно прочитайте его, если вы еще этого не сделали.

LiSP включает в себя несколько интерпретаторов, написанных на Scheme, компилятор для байтового кода и компилятор для C. Я в середине и могу с уверенностью сказатьЭто глубокая, богатая книга, достойная внимания тех, кто интересуется внедрением Lisp.Это может быть немного устаревшим к этому моменту, но для начинающего, как я, это все еще ценно.Он более продвинутый, чем SICP, поэтому будьте осторожны.Он включает в себя главу посередине о денотационной семантике, которая в основном шла прямо у меня над головой.

Некоторые другие примечания:

Самодостаточный компилятор Lisp to C Дариуса Бэкона

лямбда-лифтинг , это более продвинутая техника, которую, я думаю, использует Марк Фили

0 голосов
/ 11 апреля 2011

Если вам нужны инструменты для перевода из источника в источник, я бы порекомендовал ANTLR . Это самый превосходный.

Однако вам нужно подумать о том, как перевести язык со свободным шрифтом (lisp) на язык со слабым шрифтом (c). Например, на ваш вопрос, какой тип 10? A short? int? A double?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...