для обозначения нескольких вещей, выполняемых последовательно.
Структура данных, описывающая программу, будет состоять из объектов выражений, у каждого из которых будет свойство >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]};