var argNames = args.slice(0, args.length - 1).map(name);

  var body = args[args.length - 1];

  return function() {

    if (arguments.length != argNames.length)

      throw new TypeError("Неверное количество аргументов");

    var localEnv = Object.create(env);

    for (var i = 0; i < arguments.length; i++)

      localEnv[argNames[i]] = arguments[i];

    return evaluate(body, localEnv);

  };

};

У функций в Egg своё локальное окружение, как и в JavaScript. Мы используем Object.create для создания нового объекта, имеющего доступ к переменным во внешнем окружении (своего прототипа), но он также может содержать новые переменные, не меняя внешней области видимости.

Функция, созданная формой fun, создаёт своё локальное окружение и добавляет к нему переменные-аргументы. Затем она интерпретирует тело в этом окружении и возвращает результат.

run("do(define(plusOne, fun(a, +(a, 1))),",

    "   print(plusOne(10)))");

// → 11

run("do(define(pow, fun(base, exp,",

    "     if(==(exp, 0),",

    "        1,",

    "        *(base, pow(base, -(exp, 1)))))),",

    "   print(pow(2, 10)))");

// → 1024

<p>Компиляция</p>

Мы с вами построили интерпретатор. Во время интерпретации он работает с представлением программы, созданным парсером.

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

По традиции компиляция также превращает программу в машинный код – сырой формат, пригодный для исполнения процессором. Но каждый процесс превращения программы в другой вид, по сути, является компиляцией.

Можно было бы создать другой интерпретатор Egg, который сначала превращает программу в программу на языке JavaScript, использует new Function для вызова компилятора JavaScript и возвращает результат. При правильной реализации Egg выполнялся бы очень быстро при относительно простой реализации.

Если вам это интересно, и вы хотите потратить на это время, я поощряю вас попробовать сделать такой компилятор в качестве упражнения.

<p>Мошенничество</p>

Когда мы определяли if и while, вы могли заметить, что они представляли собой простые обёртки вокруг if и while в JavaScript. Значения в Egg – также обычные значения JavaScript.

Сравнивая реализацию Egg, построенную на JavaScript, с объёмом работы, необходимой для создания языка программирования непосредственно на машинном языке, то разница становится огромной. Тем не менее, этот пример, надеюсь, даёт вам представление о работе языков программирования.

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

Такой язык не обязан напоминать обычный ЯП. Если бы JavaScript не содержал регулярных выражений, вы могли бы написать свои парсер и интерпретатор для такого суб-языка.

Или представьте, что вы строите гигантского робота-динозавра и вам нужно запрограммировать его поведение. JavaScript – не самый эффективный способ сделать это. Можно вместо этого выбрать язык примерно такого свойства:

behavior walk

  perform when

    destination ahead

  actions

    move left-foot

    move right-foot

behavior attack

  perform when

    Godzilla in-view

  actions

    fire laser-eyes

    launch arm-rockets

Обычно это называют языком для выбранной области (domain-specific language) – язык, специально предназначенный для работы в узком направлении. Такой язык может быть более выразительным, чем язык общего назначения, потому что он разработан для выражения именно тех вещей, которые надо выразить в этой области – и больше ничего.

<p>Упражнения</p><p>Массивы</p>

Добавьте поддержку массивов в Egg. Для этого добавьте три функции в основную область видимости: array(...) для создания массива, содержащего значения аргументов, length(array) для возврата длины массива и element(array, n) для возврата n-ного элемента.

Перейти на страницу:

Похожие книги