cerr << e.what << endl; // выводим сообщение об ошибке
clean_up_mess;
}
}
Теперь необходимо написать функцию
declaration
. Что следует сделать? Нужно убедиться, что после ключевого слова
let
следует Имя, а за ним — символ = и Выражение. Именно это утверждает грамматика. Что делать с членом
name
? Мы должны добавить в вектор
var_table
типа
vector<Variable>
объект класса
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<var_table.size; ++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<Variable>
легко; эту операцию выполняет функция-член вектора
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). Его можно радикально упростить с помощью стандартной библиотеки