Я написал библиотеку для сопоставления строк с набором шаблонов, и теперь я могу легко встраивать лексические сканеры в программы на C.
Я знаю, что существует много хорошо зарекомендовавших себя инструментов для создания лексических сканеров (lex и re2c, если назвать только первые два, которые приходят на ум) этот вопрос не о лексерах, а о наилучшем подходе к "расширению" синтаксиса C . Пример с лексером — это просто конкретный случай общей проблемы.
Я вижу два возможных решения:
- напишите препроцессор, который преобразует исходный файл со встроенным лексером в простой файл C и, возможно, в набор других файлов, которые будут использоваться при компиляции.
- напишите набор макросов C, чтобы представить лексеры в более удобочитаемой форме.
Я уже сделал и то, и другое, но возникает вопрос: "какой из них вы считаете лучшей практикой в соответствии со следующими критериями?"
- Читаемость. Логика лексера должна быть ясной и легкой для понимания
- Ремонтопригодность. Поиск и исправление ошибки не должно быть кошмаром!
- Вмешательство в процесс сборки. Препроцессору потребуется дополнительный шаг в процессе сборки, препроцессор должен быть в пути и т.д.
Другими словами, если бы вам пришлось поддерживать или писать программу, использующую один из двух подходов, какой из них разочарует вас меньше?
В качестве примера, вот лексер для следующей задачи:
- Суммируйте все числа (может быть в десятичной форме, включая экспоненциальную, например 1.3E-4.2)
- Пропускать строки (двойные и одинарные кавычки)
- списки пропуска (аналогично спискам LISP: (3 4 (0 1)() 3))
- останавливаться при встрече с концом слова (регистр не имеет значения) или в конце буфера
В двух стилях.
/**** SCANNER STYLE 1 (preprocessor) ****/
#include "pmx.h"
t = buffer
while (*t) {
switch pmx(t) { /* the preprocessor will handle this */
case "&q" : /* skip strings */
break;
case "&f<?=eE>&F" : /* sum numbers */
sum += atof(pmx(Start,0));
break;
case "&b()": /* skip lists */
break;
case "&iend" : /* stop processing */
t = "";
break;
case "<.>": /* skip a char and proceed */
break;
}
}
/**** SCANNER STYLE 2 (macros) ****/
#include "pmx.h"
/* There can be up to 128 tokens per scanner with id x80 to xFF */
#define TOK_STRING x81
#define TOK_NUMBER x82
#define TOK_LIST x83
#define TOK_END x84
#define TOK_CHAR x85
pmxScanner( /* pmxScanner() is a pretty complex macro */
buffer
,
pmxTokSet("&q" , TOK_STRING)
pmxTokSet("&f<?=eE>&F" , TOK_NUMBER)
pmxTokSet("&b()" , TOK_LIST)
pmxTokSet("&iend" , TOK_END)
pmxTokSet("<.>" , TOK_CHAR)
,
pmxTokCase(TOK_STRING) : /* skip strings */
continue;
pmxTokCase(TOK_NUMBER) : /* sum numbers */
sum += atof(pmxTokStart(0));
continue;
pmxTokCase(TOK_LIST): /* skip lists */
continue;
pmxTokCase(TOK_END) : /* stop processing */
break;
pmxTokCase(TOK_CHAR) : /* skip a char and proceed */
continue;
);
Если кого-то заинтересует текущая реализация, код находится здесь: http://sites.google.com/site/clibutl .