Статьи

Что делает рубиновый тик

OLYMPUS DIGITAL CAMERA Когда я впервые начал работать с компьютерами, меня поразила одна вещь. Вы можете буквально распечатать код для чего-то вроде червя Морриса (вредоносный фрагмент кода, который сеет хаос), и это было довольно безопасно.

Но при правильной подаче на компьютер он неожиданно превратился в нечто совершенно иное.

Как это работает с рубином? Как ваш код превращается из «просто текста» во что-то, что может подключаться к HTTP-серверу или запускать некоторые SQL-запросы?

О каком рубине мы говорим?

А вот и шокер: на самом деле существует более одного рубина.

На самом деле, мы не знаем, сколько на самом деле рубинов. Мы все знаем только один, вы знаете, тот, который вы можете получить от apt-get или rvm или чего-то еще. Но на самом деле это только одна из реализаций языка программирования Ruby. Ruby (язык) — это спецификация, которую компиляторы / интерпретаторы создаются для реализации.

Сообщество Perl придумало хорошее соглашение для этого. «Perl» (с заглавной «P») обозначает сам язык, где «perl» обозначает реализацию языка. Итак, сказать pass perl -f флаг нормально, но говорить, что с «Perl» не имеет никакого смысла.

На самом деле, существует реализация Rubyus языка программирования Ruby, которая предлагает несколько приятных преимуществ по сравнению со стандартной реализацией.

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

Виртуальная машина

Важной частью основного интерпретатора ruby ​​является виртуальная машина.

Короче говоря: виртуальная машина — это просто своего рода виртуальный компьютер, который выполняет некоторые заданные команды. На обычных компьютерах есть так называемый «ассемблер», который представляет собой короткие команды, которые они умеют выполнять (эти команды напрямую преобразуются в двоичный код, который компьютеры могут успешно обрабатывать).

Виртуальная машина — это некое программное обеспечение, которое работает на компьютере и эмулирует подобные вещи, за исключением того факта, что тот, кто написал это программное обеспечение, решает, какие «короткие команды» будет понимать эта машина. Эти короткие команды называются инструкциями.

Эти инструкции, как правило, фундаментальные вещи, которые не могут быть получены из других вещей (немного похоже на аксиомы в математике / геометрии). Например, виртуальная машина Java имеет отдельные инструкции для добавления, сохранения в памяти и преобразования типов. Набор этих инструкций, собранных во что-то связное (то есть что-то, что может запустить виртуальная машина), называется байт-кодом .

Как эта виртуальная машина вступает в игру с Ruby? Что ж, когда мы набираем «ruby hello.rb», hello.rb запускается не сразу, а конвертируется в байт-код. Затем этот байт-код интерпретируется и программа запускается.

Почему так непрямо?

В любом случае, зачем нам вся эта виртуальная машина? Почему мы не можем просто запустить код «напрямую»?

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

Во-вторых, написание прямого интерпретатора приводит к всевозможным безумиям, фактически «превращая» код Ruby во что-то, что можно запустить немедленно.

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

Фактически, виртуальная машина Java была очагом разработки языка программирования. JRuby, JPython, Clojure, Scala и все другие интересные языки использовали преимущества переносимости виртуальной машины Java и встроенных языков, которые можно превратить в байт-код JVM.

Рубиновая виртуальная машина

Ruby представляет очень интересный случай для рассмотрения виртуальных машин, поскольку в последнее время он претерпел значительные изменения.

Ruby (или MRI) от Matz теперь использует в качестве базовой виртуальной машины нечто, называемое YARV (Yet Another Ruby VM).

Переход на YARV произошел в версии 1.9, потому что у старой виртуальной машины были некоторые проблемы с оптимизацией. Изменив базовую виртуальную машину, Ruby быстро теряет свой образ «слишком медленного для любого реального использования».

Весьма похвально, что команда разработчиков ядра Ruby смогла разделить виртуальную машину за один цикл выпуска. Это говорит не только о работе команды над проблемой, но и об их способности разделять вещи. Я все еще получаю воспоминания о времени, проведенном с базой данных Perl.

До сих пор мы сталкивались с тем фактом, что MRI берет ваш код ruby, превращает его в байт-код для YARV, а затем YARV выполняет этот байт-код.

Но как происходит это преобразование в байт-код? Ну, есть две части.

Tokenizer

Построение компилятора / интерпретатора является очень интересной, но иногда очень теоретической частью компьютерной науки. Я постараюсь свести теорию (и, следовательно, строгость) к минимуму, чтобы обеспечить хороший обзор того, что происходит.

МРТ просматривает ваш код Ruby и разбивает его на куски. Эти куски называются токенами, а часть, которая делает это, называется «лексером». Рассматривать код Ruby в этом примере было бы слишком сложно, поэтому давайте попробуем связать это с чем-то вроде «языка калькулятора». Строка на этом языке может выглядеть так (без кавычек):

(4 + 5)/2

Лексер возьмет это и превратит в:

OPENPAREN NUMBER OPERATOR NUMBER CLOSEPAREN OPERATOR NUMBER

Для нас это не выглядит большим изменением, но мы только что полностью изменили проблемную область. Мы взяли ввод (просто последовательность символов и символов) к чему-то, что дает нам представление о том, какой тип ввода мы обрабатываем. Для лексера 496 и 46 составляют НОМЕР, дающий общую структуру строки.

Теперь, что мы делаем с этой недавно полученной информацией?

анализ

Парсер в основном берет токены, полученные от лексера, и превращает их в байт-код.

Давайте продолжим с нашим примером языка калькулятора.

Синтаксический анализатор распознает, что «NUMBER OPERATOR NUMBER» является шаблоном, который приводит к некоторому определенному байт-коду (то есть добавлению или разделению), и он может взять эти токены и преобразовать их в байт-код, который работает на базовой виртуальной машине.

MRI делает нечто очень похожее (хотя и гораздо более сложное), где ищет конкретные шаблоны в токенах, а затем превращает их в байт-код.

Ruby — большой язык, поэтому он не может быть таким простым. К сожалению, это не так. Есть еще несколько конструкций, которые находятся под и между огромными куполами лексизма и разбора (например, деревья AST). Тем не менее, общая идея довольно проста: лексер вычисляет токены, анализатор просматривает шаблоны в этих токенах, чтобы произвести байт-код.

Вывод

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

Создание компилятора — это потрясающий предмет, который сочетает в себе теорию и практику (что мне кажется очень интересным) — это руководство должно было дать вам очень приблизительный обзор того, как создаются вещи.

Спасибо за прочтение!