Марейн Хавербеке - Выразительный JavaScript Страница 38

Тут можно читать бесплатно Марейн Хавербеке - Выразительный JavaScript. Жанр: Компьютеры и Интернет / Программирование, год неизвестен. Так же Вы можете читать полную версию (весь текст) онлайн без регистрации и SMS на сайте «WorldBooks (МирКниг)» или прочесть краткое содержание, предисловие (аннотацию), описание и ознакомиться с отзывами (комментариями) о произведении.
Марейн Хавербеке - Выразительный JavaScript

Марейн Хавербеке - Выразительный JavaScript краткое содержание

Прочтите описание перед тем, как прочитать онлайн книгу «Марейн Хавербеке - Выразительный JavaScript» бесплатно полную версию:
В процессе чтения вы познакомитесь с основами программирования и, в частности, языка JavaScript, а также выполните несколько небольших проектов. Один из самых интересных проектов — создание своего языка программирования.

Марейн Хавербеке - Выразительный JavaScript читать онлайн бесплатно

Марейн Хавербеке - Выразительный JavaScript - читать книгу онлайн бесплатно, автор Марейн Хавербеке

Хотя JavaScript совершенно не помогает делать модули, его гибкие функции и объекты позволяют сделать достаточно неплохую систему модулей. Область видимости функций используется как внутреннее пространство имён модуля, а объекты используются для хранения наборов переменных.

Есть два популярных подхода к использованию модулей. Один – CommonJS Modules, построенный на функции require, которая вызывает модули по имени и возвращает их интерфейс. Другой – AMD, использующий функцию define, принимающую массив имён модулей и, после их загрузки, исполняющую функцию, аргументами которой являются их интерфейсы.

Упражнения

Названия месяцев

Напишите простой модуль типа weekday, преобразующий номера месяцев (начиная с нуля) в названия и обратно. Выделите ему собственное пространство имён, так как ему потребуется внутренний массив с названиями месяцев, и используйте чистый JavaScript, без системы загрузки модулей.

// Ваш код

console.log(month.name(2));

// → March

console.log(month.number("November"));

// → 10

Вернёмся к электронной жизни

Надеюсь, что глава 7 ещё не стёрлась из вашей памяти. Вернитесь к разработанной там системе и предложите способ разделения кода на модули. Чтобы освежить вам память – вот список функций и типов, по порядку появления:

Vector

Grid

directions

directionNames

randomElement

BouncingCritter

elementFromChar

World

charFromElement

Wall

View

WallFollower

dirPlus

LifelikeWorld

Plant

PlantEater

SmartPlantEater

Tiger

Не надо создавать слишком много модулей. Книга, в которой на каждой странице была бы новая глава, действовала бы вам на нервы (хотя бы потому, что всё место съедали бы заголовки). Не нужно делать десять файлов для одного мелкого проекта. Рассчитывайте на 3-5 модулей.

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

Круговые зависимости

Запутанная тема в управлении зависимостями – круговые зависимости, когда модуль А зависит от Б, а Б зависит от А. Многие системы модулей это просто запрещают. Модули CommonJS допускают ограниченный вариант: это работает, пока модули не заменяют объект exports, существующий по-умолчанию, другим значением, и начинают использовать интерфейсы друг друга только после окончания загрузки.

Можете ли вы придумать способ, который позволил бы воплотить систему поддержки таких зависимостей? Посмотрите на определение require и подумайте, что нужно сделать этой функции для этого.

11. Проект: язык программирования

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

Хэл Абельсон и Жеральд Сасман, «Структура и интерпретация компьютерных программ».

Когда ученик спросил учителя о природе цикла Данных и Контроля, Юань-Ма ответил: «Подумай о компиляторе, компилирующем самого себя».

Мастер Юань-Ма, «Книга программирования»

Создать свой язык программирования удивительно легко (пока вы не ставите запредельных целей) и довольно поучительно.

Главное, что я хочу продемонстрировать в этой главе – в построении языка нет никакой магии. Мне часто казалось, что некоторые человеческие изобретения настолько сложны и заумны, что мне их никогда не понять. Однако после небольшого самообразования и ковыряния такие штуки часто оказываются довольно обыденными.

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

Разбор (parsing)

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

У нашего языка будет простой и однородный синтаксис. В Egg всё будет являться выражением. Выражение может быть переменной, число, строка или приложение. Приложения используются для вызова функций и конструкций типа if или while.

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

Приложения записываются так же, как в JavaScript — при помощи скобок после выражения и с любым количеством аргументов в скобках, разделённых запятыми.

do(define(x, 10),

   if(>(x, 5)),

      print("много"),

      print("мало"))

Однородность языка означает, что то, что в JavaScript является операторами (например, >), применяется так же, как и остальные функции. Так как в синтаксисе нет концепции блоков, нам нужна конструкция do для обозначения нескольких вещей, выполняемых последовательно.

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

  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.

Перейти на страницу:
Вы автор?
Жалоба
Все книги на сайте размещаются его пользователями. Приносим свои глубочайшие извинения, если Ваша книга была опубликована без Вашего на то согласия.
Напишите нам, и мы в срочном порядке примем меры.
Комментарии / Отзывы
    Ничего не найдено.