Читаем Программирование полностью

Теперь необходимо написать функцию declaration(). Что следует сделать? Нужно убедиться, что после ключевого слова let следует Имя, а за ним — символ = и Выражение. Именно это утверждает грамматика. Что делать с членом name? Мы должны добавить в вектор var_table типа vector объект класса Variable c заданными строкой name и значением выражения. После этого мы сможем извлекать значения с помощью функции get_value() и изменять их с помощью функции set_value(). Однако сначала надо решить, что случится, если мы определим переменную дважды. Рассмотрим пример.

let v1 = 7;

let v1 = 8;

Мы решили, что повторное определение является ошибкой. Обычно это просто синтаксическая ошибка. Вероятно, мы имели в виду не то, что написали, а следующие инструкции:

let v1 = 7;

let v2 = 8;

Определение объекта класса Variable с именем var и значением val состоит из двух логических частей.

1. Проверяем, существует ли в векторе var_table объект класса Variable с именем var.

2. Добавляем пару (var, val) в вектор var_table.

Мы не должны использовать неинициализированные переменные, поэтому определили функции is_declared() и define_name(), представляющие эти две операции.

bool is_declared(string var)

  // есть ли переменная var в векторе var_table?

{

  for (int i = 0; i

  if (var_table[i].name == var) return true;

  return false;

}

double define_name(string var, double val)

  // добавляем пару (var,val) в вектор var_table

{

  if (is_declared(var)) error(var,"declared twice");

  var_table.push_back(Variable(var,val));

  return val;

}

Добавить новый объект класса Variable в вектор типа vector легко; эту операцию выполняет функция-член вектора push_back().

var_table.push_back(Variable(var,val));

Вызов конструктора Variable(var,val) создает соответствующий объект класса Variable, а затем функция push_back() добавляет этот объект в конец вектора var_table. В этих условиях и с учетом лексем let и name функция declaration() становится вполне очевидной.

double declaration()

  // предполагается, что мы можем выделить ключевое слово "let"

  // обработка: name = выражение

  // объявляется переменная с именем "name" с начальным значением,

  // заданным "выражением"

{

  Token t = ts.get();

  if (t.kind != name) error ("в объявлении ожидается переменная name");

  string var_name = t.name;

  Token t2 = ts.get();

  if (t2.kind != '=') error("в объявлении пропущен символ =",

  var_name);

  double d = expression();

  define_name(var_name,d);

  return d;

}

Обратите внимание на то, что мы возвращаем значение, хранящееся в новой переменной. Это полезно, когда инициализирующее выражение является нетривиальным. Рассмотрим пример.

let v = d/(t2–t1);

Это объявление определяет переменную v и выводит ее значение. Кроме того, печать переменной упрощает код функции calculate(), поскольку при каждом вызове функция statement() возвращает значение. Как правило, общие правила позволяют сохранить простоту кода, а специальные варианты приводят к усложнениям.

Описанный механизм отслеживания переменных часто называют таблицей символов (symbol tables). Его можно радикально упростить с помощью стандартной библиотеки map (см. раздел 21.6.1).

<p id="AutBody_Root129"><strong>7.8.2. Использование имен</strong></p>

Все это очень хорошо, но, к сожалению, не работает. Это не должно было стать для нас сюрпризом. Первый вариант никогда — почти никогда — не работает. В данном случае мы даже не закончили программу — она даже не скомпилируется. У нас нет лексемы '=', но это легко исправить, добавив дополнительный раздел case в функцию Token_stream::get() (см. раздел 7.6.3). А как представить ключевые слова let и name в виде лексем? Очевидно, для того чтобы распознавать эти лексемы, необходимо модифицировать функцию get(). Как? Вот один из способов.

const char name = 'a';        // лексема name

const char let = 'L';         // лексема let

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже