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

>  else

>    throw new SyntaxError("Неожиданный синтаксис: " + program);


>  return parseApply(expr, program.slice(match[0].length));

>}


>function skipSpace(string) {

>  var first = string.search(/\S/);

>  if (first == -1) return "";

>  return string.slice(first);

>}

Поскольку Egg разрешает любое количество пробелов в элементах, нам надо постоянно вырезать пробелы с начала строки. С этим справляется >skipSpace.

Пропустив начальные пробелы, >parseExpression использует три регулярки для распознавания трёх простых (атомарных) элементов, поддерживаемых языком: строк, чисел и слов. Парсер создаёт разные структуры для разных типов. Если ввод не подходит ни под одну из форм, это не является допустимым выражением, и он выбрасывает ошибку. >SyntaxError – стандартный объект для ошибок, который создаётся при попытке запуска некорректной программы JavaScript.

Мы можем отрезать обработанную часть программы, и передать его, вместе с объектом выражения, в >parseApply, определяющая, не является ли выражение приложением. Если так и есть, он парсит список аргументов в скобках.

>function parseApply(expr, program) {

>  program = skipSpace(program);

>  if (program[0] != "(")

>    return {expr: expr, rest: program};


>  program = skipSpace(program.slice(1));

>  expr = {type: "apply", operator: expr, args: []};

>  while (program[0] != ")") {

>    var arg = parseExpression(program);

>    expr.args.push(arg.expr);

>    program = skipSpace(arg.rest);

>    if (program[0] == ",")

>      program = skipSpace(program.slice(1));

>    else if (program[0] != ")")

>      throw new SyntaxError("Ожидается ',' or ')'");

>  }

>  return parseApply(expr, program.slice(1));

>}

Если следующий символ программы – не открывающая скобка, то это не приложение, и >parseApply просто возвращает данное ей выражение.

В ином случае, она пропускает открывающую скобку и создаёт объект синтаксического дерева для этого выражения. Затем она рекурсивно вызывает >parseExpression для разбора каждого аргумента, пока не встретит закрывающую скобку. Рекурсия непрямая, >parseApply и >parseExpression вызывают друг друга.

Поскольку приложение само по себе может быть выражением (>multiplier(2)(1)), >parseApply должна, после разбора приложения, вызвать себя снова, проверив, не идёт ли далее другая пара скобок.

Вот и всё, что нам нужно для разбора Egg. Мы обернём это в удобную функцию >parse, проверяющую, что она дошла до конца строки после разбора выражения (программа Egg – это одно выражение), и это даст нам структуру данных программы.

>function parse(program) {

>  var result = parseExpression(program);