Ещё одна основная часть языка – do
specialForms["do"] = function(args, env) {
var value = false;
args.forEach(function(arg) {
value = evaluate(arg, env);
});
return value;
};
Чтобы создавать переменные и давать им значения, мы создаём форму define
word в качестве первого аргумента, и выражение, производящее значение, которое надо присвоить этому слову в качестве второго. define, как и всё, является выражением, поэтому оно должно возвращать значение. Пусть оно возвращает присвоенное значение (прямо как оператор = в JavaScript).specialForms["define"] = function(args, env) {
if (args.length != 2 || args[0].type != "word")
throw new SyntaxError("Bad use of define");
var value = evaluate(args[1], env);
env[args[0].name] = value;
return value;
};
Окружение
Окружение, принимаемое интерпретатором — это объект со свойствами, чьи имена соответствуют именам переменных, а значения – значениям этих переменных. Давайте определим объект окружения, представляющий глобальную область видимости.
Для использования конструкции if
true и false.var topEnv = Object.create(null);
topEnv["true"] = true;
topEnv["false"] = false;
Теперь мы можем вычислить простое выражение, меняющее булевское значение на обратное.
var prog = parse("if(true, false, true)");
console.log(evaluate(prog, topEnv)); // → false
Для поддержки простых арифметических операторов и сравнения мы добавим несколько функций в окружение. Для упрощения кода мы будем использовать new Function
["+", "-", "*", "/", "==", "<", ">"].forEach(function(op) {
topEnv[op] = new Function("a, b", "return a " + op + " b;");
});
Также пригодится способ вывода значений, так что мы обернём console.log
print.topEnv["print"] = function(value) {
console.log(value);
return value;
};
Это даёт нам достаточно элементарных инструментов для написания простых программ. Следующая функция run
function run() {
var env = Object.create(topEnv);
var program = Array.prototype.slice
.call(arguments, 0).join("\n");
return evaluate(parse(program), env);
}
Использование Array.prototype.slice.call
join. Она принимает все аргументы, переданные в run, и считает, что все они – строчки программы.run("do(define(total, 0),",
" define(count, 1),",
" while(<(count, 11),",
" do(define(total, +(total, count)),",
" define(count, +(count, 1)))),",
" print(total))");
// → 55
Эту программу мы видели уже несколько раз – она подсчитывает сумму чисел от 1 до 10 на языке Egg. Она уродливее эквивалентной программы на JavaScript, но не так уж и плоха для языка, заданного менее чем 150 строчками кода.
Функции
Язык программирования без функций – плохой язык.
К счастью, несложно добавить конструкцию fun
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;
}