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

для обозначения нескольких вещей, выполняемых последовательно.

Структура данных, описывающая программу, будет состоять из объектов выражений, у каждого из которых будет свойство >type, отражающее тип этого выражения и другие свойства, описывающие содержимое.

Выражения типа >“value” представляют строки или числа. Их свойство >value содержит строку или число, которое они представляют. Выражения типа >“word” используются для идентификаторов (имён). У таких объектов есть свойство >name, содержащее имя идентификатора в виде строки. И наконец, выражения >“apply” представляют приложения. У них есть свойство >operator, ссылающееся на применяемое выражение, и свойство >args с массивом аргументов.

Часть >>(x, 5) будет представлена так:

>{

>  type: "apply",

>  operator: {type: "word", name: ">"},

>  args: [

>    {type: "word", name: "x"},

>    {type: "value", value: 5}

>  ]

>}

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


Структура синтаксического дерева

Сравните это с парсером, написанным нами для файла настроек в главе 9, у которого была простая структура: он делил ввод на строки и обрабатывал их одну за другой. Там было всего несколько форм, которые разрешено принимать строке.

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

Мы определяем функцию >parseExpression, принимающую строку на вход и возвращающую объект, содержащий структуру данных для выражения с начала строки, вместе с частью строки, оставшейся после парсинга. При разборе подвыражений (таких, как аргумент приложения), эта функция снова вызывается, возвращая выражение аргумента вместе с оставшимся текстом. Тот текст может, в свою очередь, содержать ещё аргументы, или же быть закрывающей скобкой, завершающей список аргументов.

Первая часть парсера:

>function parseExpression(program) {

>  program = skipSpace(program);

>  var match, expr;

>  if (match = /^"([^"]*)"/.exec(program))

>    expr = {type: "value", value: match[1]};

>  else if (match = /^\d+\b/.exec(program))

>    expr = {type: "value", value: Number(match[0])};

>  else if (match = /^[^\s(),"]+/.exec(program))

>    expr = {type: "word", name: match[0]};