Как язык может быть интерпретирован сам по себе (например, Рубиниус)?

Я уже некоторое время программирую на Ruby, используя только стандартную MRI-реализацию Ruby, но мне всегда было интересно узнать о других реализациях, о которых я так много слышал.

На днях я читал о Рубиниусе, переводчике на Ruby, написанном на Ruby. Я пытался найти его в разных местах, но мне было трудно понять, как именно это работает. У меня никогда не было большого опыта в компиляторах или написании языков, но мне действительно интересно это выяснить.

Как именно язык может быть интерпретирован сам по себе? Есть ли базовый шаг в компиляции, который я не понимаю, где это имеет смысл? Может ли кто-нибудь объяснить мне это, как будто я идиот (потому что это не слишком далеко от базы в любом случае)

5 ответов

Решение

Это проще, чем вы думаете.

Rubinius не на 100% написан на Ruby, просто в основном.

С http://rubini.us/

Большой аспект популярных языков, таких как C и Java, заключается в том, что большая часть функциональности, доступной программисту, написана на самом языке. Цель Rubinius - добавить Ruby в этот список. Рубиисты могут легче добавлять функции в язык, исправлять ошибки и узнавать, как работает язык. Везде, где возможно, Рубинус пишется на Ruby. Там, где это невозможно (пока), это C++.

Концепция, которую вы ищете, это начальная загрузка компилятора.

По сути, начальная загрузка означает написание компилятора (или интерпретатора) для языка x на языке x. Это делается либо путем написания базового компилятора на более низком уровне вручную (т.е. написания компилятора C в ассемблере), либо с использованием другого языка высокого уровня.

Узнайте больше о начальной загрузке в Википедии. Ответ Грега относительно мета-циркулярных оценщиков также настоятельно рекомендуется, включая соответствующую главу в SICP.

В случае Rubinius виртуальная машина написана на C++ и работает со всеми низкоуровневыми (связанными с операционной системой) вещами и базовыми операциями. У виртуальной машины есть собственный формат байт-кода (как и у JVM), и когда Rubinius запускается, он запускает виртуальную машину, которая выполняет байт-код. Однако большая часть стандартной библиотеки Rubinius (которая является частью языка Ruby) реализована на Ruby по сравнению с C (MRI) или Java (JRuby). Кроме того, компилятор байт-кода Rubinius также написан на Ruby. Так что да, в какой-то момент в начале они должны были использовать стандартный интерпретатор Ruby (MRI) для начальной загрузки Rubinius. Но это больше не должно иметь место (хотя я не уверен, что он вам все еще может понадобиться, поскольку его система сборки использует rake).

Предположим, что вы работаете с языком, скажем Lisp, хотя это не имеет значения. (Может быть C++, Java, Ruby, что угодно.)

Ну, у вас есть реализация Lisp. Назовите эту реализацию Imp (только некоторые выдуманные имена, сокращенно от IMPlementation). Поскольку Imp сама по себе является программой, ваш компьютер может ее запустить. Теперь вы пишете свою собственную реализацию для Lisp, написанную на Lisp, и называете ее Circ. Circ - это просто программа, скомпилированная (или интерпретированная, если хотите) из кода на Лиспе. Ваш код написан так, что он читает в файле, анализирует его (обрабатывает его в значимые данные) и что-то делает с данными. Что это такое? В случае с Circ он выполняет данные.

Но как это сделать?

Хорошо предположим, что в простом случае код, который читает и анализирует Circ, представляет собой нечто простое, например, выполнение математических операций и вывод результата. Circ обрабатывает код в виде простых в использовании данных (хорошо для такого языка, как Lisp, с которого легко начать, но это не главное) и сохраняет его. Что ж, в Lisp вы можете написать код для сокращения чисел, поэтому код, написанный для Circ, может сделать то же самое, потому что он написан на Lisp. Таким образом, обработанные данные подключаются к некоторому коду дополнительной обработки... и вуаля! У вас есть числовой результат! Затем ваша программа Circ выводит результат.

То же самое можно сделать с более сложными вещами, чем простая математика. На самом деле вы можете компилировать / интерпретировать другие аспекты языка. Напишите достаточно этих "других аспектов" и склейте их вместе, вы получите компилятор для Lisp, написанный на Lisp.

Поскольку компилятор скомпилирован Imp, он может быть запущен на вашей машине и presto! Вы сделали.

Этот метод обычно называется метациклическим оценщиком и был впервые введен несколько десятилетий назад в контексте Lisp.

Хорошее описание методики можно найти в Структуре и интерпретации компьютерных программ, глава 4.

Другие вопросы по тегам