Выразительный JavaScript (Хавербеке) - страница 121

>specialForms["fun"] = function(args, env) {

>  if (!args.length)

>    throw new SyntaxError("Функции нужно тело");

>  function name(expr) {

>    if (expr.type != "word")

>      throw new SyntaxError("Имена аргументов должны быть типа word");

>    return expr.name;

>  }

>  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

Компиляция

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

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

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

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

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