Каждый автомат представлен соответствующей процедурой (функцией), осуществляющей имитацию переходов от начального состояния к одному из заключительных. Разметка диаграмм Вирта, соответствующая состояниям конечного автомата, была описана ранее. Принципы программной реализации в общем случае тоже достаточно просты:
– каждому состоянию ставится в соответствие метка;
– анализируемые альтернативы проверяются условными операторами (они подходят лучше переключателей из-за того, что проверяться могут значения символов и их классы);
– если результат проверки является истиной, проводится обработка контекста, берется следующий символ и осуществляется переход на новую метку (в новое состояние);
– процесс повторяется до тех пор, пока не произойдет переход в одно заключительных состояний.
Данная реализация активно использует оператор безусловного перехода goto вследствие того, что диаграмма Вирта в общем случае является неструктурированным графом. А такие графы невозможно представить без goto, если только не осуществить соответствующие преобразования. Применение же этих преобразований может снизить наглядность исходных диаграмм и привести к их избыточности.
Общая структура непрямого лексического анализатора
В завершение всего можно привести объединенную диаграмму Вирта, определяющую взаимосвязи между отдельными, ранее представленными автоматами (см. Рисунок 5.3). На ней отражается альтернативный порядок следования разбираемых лексем с учетом приоритетов, определяемых схемой арбитража.
Рисунок 7.3 Объединение диаграмм Вирта для непрямого лексического анализа в соответствии с приоритетом
Рисунок 7.3 Объединение диаграмм Вирта для непрямого лексического анализа в соответствии с приоритетом (продолжение)
Рисунок 7.3 Объединение диаграмм Вирта для непрямого лексического анализа в соответствии с приоритетом (продолжение)
Рисунок 7.3 Объединение диаграмм Вирта для непрямого лексического анализа в соответствии с приоритетом (окончание)
В основной программе данной диаграмме соответствует функция nxl(), порождающая лексемы.
//----------------------------------------------------------------
// Функция, формирующая следующую лексему
// Вызывается синтаксическим анализатором
//----------------------------------------------------------------
void nxl(void) {
do {
i_lv = -1;
lv[0] = '\0';
// Фиксируем начальную позицию
oldpoz=ftell(infil)-1;
oldline=line; oldcolumn=column;
// Процесс пошел
if(si == EOF) {lc = lexEof; return;}
// Игнорируемую лексему не возвращаем
if(isSkip(si)) {nxsi(); lc = lexSkip; continue; /*return;*/}
if(id_etc()) {return;} unset();
if(string_const()) {return;} unset();
if(float1()) {return;} unset();
if(float2()) {return;} unset();
if(float3()) {return;} unset();
if(float4()) {return;} unset();
if(float5()) {return;} unset();
if(binary()) {return;} unset();
if(octal()) {return;} unset();
if(hex()) {return;} unset();
if(pdecimal()) {return;} unset();
if(decimal()) {return;} unset();
// Игнорируемую лексему не возвращаем
if(comment()) {continue; /*return;*/} unset();
// Игнорируемую лексему не возвращаем
if(isIgnore(si)) {nxsi();lc = lexIgnore;continue;/*return;*/}
if(si=='/') {nxsi(); lc = lexSlash;return;}
if(si == ';') {nxsi(); lc = lexSemicolon; return;}
if(si == ',') {nxsi(); lc = lexComma; return;}
if(si == ':') {
nxsi();
if(si == '=') {nxsi(); lc = lexAssign; return;}
} unset();
if(si==':') {nxsi(); lc = lexColon; return;}
if(si == '(') {nxsi(); lc = lexLftRndBr; return;}
if(si == ')') {nxsi(); lc = lexRghRndBr; return;}
if(si == '[') {nxsi(); lc = lexLftSqBr; return;}
if(si == ']') {nxsi(); lc = lexRghSqBr; return;}
if(si == '*') {nxsi(); lc = lexStar; return;}
if(si == '%') {nxsi(); lc = lexPercent; return;}
if(si == '+') {nxsi(); lc = lexPlus; return;}
if(si == '-') {
nxsi();
if(si == '>') {nxsi(); lc = lexArrow; return;}
} unset();
if(si=='-') {nxsi(); lc=lexMinus; return;}
if(si == '=') {nxsi(); lc = lexEQ; return;}
if(si == '!') {
nxsi();
if(si == '=') {nxsi(); lc = lexNE; return;}
} unset();
if(si == '>') {
nxsi();
if(si == '=') {nxsi(); lc = lexGE; return;}
} unset();
if(si=='>') {nxsi(); lc=lexGT; return;}
if(si == '<') {
nxsi();
if(si == '=') {nxsi(); lc = lexLE; return;}
} unset();
if(si=='<') {nxsi(); lc=lexLT; return;}
lc = lexError; er(0); nxsi();
} while (lc == lexComment || lc == lexSkip || lc == lexIgnore);
}
Остановимся на ряде моментов. В сканер введен цикл, который отфильтровывает лексемы, не обрабатываемые распознавателем. Это комментарии, пропуски, игнорируемые символы.
Для выполнения отката, в начале обработки лексемы запоминаются текущие позиции в файле, строке и столбце. При неудаче они восстанавливаются и происходит анализ следующей диаграммы. Функция отката unset() реализована следующим образом:
// откат назад при неудачной попытке распознать лексему
static void unset() {
fseek(infil, oldpoz, 0);
nxsi();
i_lv=-1;
lv[0]='\0';
poz = oldpoz;
line=oldline;
column=oldcolumn;
}
С реализацией отдельных диаграмм, процедурой обработки ошибок и тестовыми процедурами можно ознакомиться по представленным к конспекту лекций примерам.