4

у меня есть довольно старый код C корпоративный синтаксический анализатор, который был создан из древнего Yacc и использует yyact, yypact, yypgo, yyr1, yyr2, yytoks, yyexca, yychk, yydef столы (но нет yyreds) и исходный источник грамматики теряется. Эта устаревшая часть кода нуждается в обновлении, но я не могу позволить себе перекодировать ее с нуля.извлекать правила грамматики из сгенерированных синтаксического анализа таблиц

Невозможно механически получить/регенерировать правила синтаксического анализа путем вычитания таблиц синтаксического анализа, чтобы восстановить грамматику?

Пример с небольшим количеством выражения синтаксический анализатор, который я могу обработать с той же древней Yacc:

yytabelem yyexca[] ={ 
-1, 1, 
    0, -1, 
    -2, 0, 
-1, 21, 
    261, 0, 
    -2, 8, 
    }; 
yytabelem yyact[]={ 

    13,  9, 10, 11, 12, 23,  8, 22, 13,  9, 
    10, 11, 12,  9, 10, 11, 12,  1,  2, 11, 
    12,  6,  7,  4,  3,  0, 16,  5,  0, 14, 
    15,  0,  0,  0, 17, 18, 19, 20, 21,  0, 
    0, 24 }; 
yytabelem yypact[]={ 

    -248, -1000, -236, -261, -236, -236, -1000, -1000, -248, -236, 
    -236, -236, -236, -236, -253, -1000, -263, -245, -245, -1000, 
-1000, -249, -1000, -248, -1000 }; 
yytabelem yypgo[]={ 

    0, 17, 24 }; 
yytabelem yyr1[]={ 

    0,  1,  1,  1,  2,  2,  2,  2,  2,  2, 
    2,  2,  2 }; 
yytabelem yyr2[]={ 

    0,  8, 12,  0,  6,  6,  6,  6,  6,  6, 
    4,  2,  2 }; 
yytabelem yychk[]={ 

-1000, -1, 266, -2, 259, 263, 257, 258, 267, 262, 
    263, 264, 265, 261, -2, -2, -1, -2, -2, -2, 
    -2, -2, 260, 268, -1 }; 
yytabelem yydef[]={ 

    3, -2,  0,  0,  0,  0, 11, 12,  3,  0, 
    0,  0,  0,  0,  0, 10,  1,  4,  5,  6, 
    7, -2,  9,  3,  2 }; 

yytoktype yytoks[] = 
{ 
    "NAME", 257, 
    "NUMBER", 258, 
    "LPAREN", 259, 
    "RPAREN", 260, 
    "EQUAL", 261, 
    "PLUS", 262, 
    "MINUS", 263, 
    "TIMES", 264, 
    "DIVIDE", 265, 
    "IF", 266, 
    "THEN", 267, 
    "ELSE", 268, 
    "LOW", 269, 
    "UMINUS", 270, 
    "-unknown-", -1 /* ends search */ 
}; 

/* am getting this table in my example, 
but it is not present in the studied parser :^(*/ 
char * yyreds[] = 
{ 
    "-no such reduction-", 
    "stmt : IF exp THEN stmt", 
    "stmt : IF exp THEN stmt ELSE stmt", 
    "stmt : /* empty */", 
    "exp : exp PLUS exp", 
    "exp : exp MINUS exp", 
    "exp : exp TIMES exp", 
    "exp : exp DIVIDE exp", 
    "exp : exp EQUAL exp", 
    "exp : LPAREN exp RPAREN", 
    "exp : MINUS exp", 
    "exp : NAME", 
    "exp : NUMBER", 
}; 

Я ищу, чтобы получить

stmt : IF exp THEN stmt 
     | IF exp THEN stmt ELSE stmt 
     | /*others*/ 
     ; 

exp  : exp PLUS exp 
     | exp MINUS exp 
     | exp TIMES exp 
     | exp DIVIDE exp 
     | exp EQUAL exp 
     | LPAREN exp RPAREN 
     | MINUS exp 
     | NAME 
     | NUMBER 
     ; 

Edit: Я урезанная сгенерированный анализатор моего примера для ясности, но чтобы помочь в анализе, я опубликовал весь сгенерированный код as a gist. Пожалуйста, не то, что по какой-то неизвестной причине нет таблицы yyreds в синтаксическом анализаторе. Я пытаюсь изучить/изменить. Я полагаю, это было бы не весело:^S

+0

Вы спрашиваете об декомпиляции: молотый говяжий назад к корове. Этот вопрос может добавить некоторый разновидности на сайт SE с обратным проектированием. – Kaz

+0

Нет ли файла 'y.output', если это возможно? – Kaz

+0

@ Kaz: если вы чувствуете, что это хороший кандидат, я могу спросить его и в RE. Если такая перекрестная реклама принимается. И нет, у меня есть только «ytab.c». – Seki

ответ

3

Интересная проблема. Как раз от сопоставления таблиц с грамматикой, кажется, что yyr1 и yyr2 дают вам «схему» правил - yyr1 - это символ в левой части каждого правила, а yyr2 - это 2x количество символов с правой стороны , У вас также есть имена всех терминалов в удобной таблице. Но имена нетерминалов теряются.

Чтобы определить, какие символы идут по порядку каждого правила, вам необходимо восстановить конечный автомат из таблиц, что, вероятно, связано с чтением и пониманием кода в файле y.tab.c, который фактически выполняет разбор. Некоторые из таблиц (выглядит как yypact, yychk и yydef) индексируются по номеру штата. Представляется вероятным, что yyact индексируется yypact[state] + token. Но это только догадки. Вам нужно посмотреть на код анализа и понять, как его использование таблиц для кодирования возможных сдвигов, сокращений и gotos.

Как только у вас есть машина состояния, вы можете отступить от состояний, содержащих сокращения определенных правил через состояния, которые имеют сдвиги и переходы этого правила. Переход в состояние сокращения означает, что последний символ в правлении этого правила - это токен, сдвинутый. Переход в состояние сокращения означает, что последний символ на rhs является символом для goto. Второй-последний символ поступает из shift/goto в состояние, которое сдвигает/переходит в состояние сокращения и так далее.

редактировать

Как я предположила, yypact является 'первичным действием' для государства. Если значение равно YYFLAG (-1000), это состояние только для уменьшения (без сдвигов). В противном случае это состояние потенциального сдвига, и yyact[yypact[state] + token] дает вам потенциальное состояние для переключения. Если yypact[state] + token находится за пределами допустимого диапазона для таблицы yyact, или токен не соответствует символу ввода для этого состояния, тогда сдвиг на этом токене отсутствует.

yychk является символом ввода для каждого состояния - положительное число означает, что вы переключитесь на это состояние на этом токене, в то время как отрицательное означает, что вы получаете это состояние на этом нетерминале.

yydef это сокращение для этого состояния - положительное число означает сокращение этого правила, а 0 означает отсутствие сокращения, а -2 означает два или более возможных сокращения. yyexca - таблица сокращений для тех состояний с более чем одной редукцией. Пара -1 state означает следующие данные для данного состояния; следующие пары token rule означают, что для наблюдения token следует уменьшить rule. A -2 для токена - это подстановочный знак (конец списка), в то время как 0 для правила означает отсутствие правила для уменьшения (вместо этого ошибка), а -1 означает принятие ввода.

yypgo Таблица является GOTOS для символа - вы идете заявить yyact[yypgo[sym] + state + 1], если это находится в диапазоне для yyact и yyact[yypgo[sym]] иначе.

Чтобы восстановить правила, просмотрите таблицы yydef и yyexca, чтобы увидеть, какие состояния сокращают каждое правило, и вернуться назад, чтобы узнать, как достигнуто состояние.

Например, правило №1. Из таблиц yyr1 и yyr2 мы знаем его вид S1: X X X X - не терминал № 1 на lhs и 4 символа на rhs. Его уменьшенный в состоянии 16 (из yydef таблицы и вступающий символа состояния 16 (из yychk) -1 Так его:.

S1: ?? ?? ?? S1 

Вы получаете в состояние 16 из yyact[26] и yypgo[1] == 17, так что это означает Гото исходит из состояния 8 (26 == yypgo[1] + 8 + 1 вступающей символ состояния 8, 267 (THEN), так что теперь мы имеем:.

S1: ?? ?? THEN S1 

Вы получаете в состояние 8 из yyact[6], так что предыдущее состояние имеет yypact[state] == -261 который находится в состоянии 3. yychk[3] == -2, так что мы имеем:

S1: ?? S2 THEN S1 

Вы получаете в состояние 3 из yyact[24] и yypgo[2] == 24 поэтому любое государство может Гото 3 здесь. Поэтому мы теперь застряли в этом правиле; чтобы понять, что такое первый символ, нам нужно проложить наш путь вперед из состояния 0 (начальное состояние) для восстановления конечного автомата.

редактировать

Этот код будет декодировать состояние машины из формата таблицы выше и распечатать все сдвиг/свёртка/GOTO действия в каждом государстве:

#define ALEN(A)   (sizeof(A)/sizeof(A[0])) 
for (int state = 0; state < ALEN(yypact); state++) { 
    printf("state %d:\n", state); 
    for (int i = 0; i < ALEN(yyact); i++) { 
     int sym = yychk[yyact[i]]; 
     if (sym > 0 && i == yypact[state] + sym) 
      printf("\ttoken %d shift state %d\n", sym, yyact[i]); 
     if (sym < 0 && -sym < ALEN(yypgo) && 
      (i == yypgo[-sym] || i == yypgo[-sym] + state + 1)) 
      printf("\tsymbol %d goto state %d\n", -sym, yyact[i]); } 
    if (yydef[state] > 0) 
     printf("\tdefault reduce rule %d\n", yydef[state]); 
    if (yydef[state] < 0) { 
     for (int i = 0; i < ALEN(yyexca); i+= 2) { 
      if (yyexca[i] == -1 && yyexca[i+1] == state) { 
       for (int j = i+2; j < ALEN(yyexca) && yyexca[j] != -1; j += 2) { 
        if (yyexca[j] < 0) printf ("\tdefault "); 
        else printf("\ttoken %d ", yyexca[j]); 
        if (yyexca[j+1] < 0) printf("accept\n"); 
        else if(yyexca[j+1] == 0) printf("error\n"); 
        else printf("reduce rule %d\n", yyexca[j+1]); } } } } } 

Он будет производить вывод, как :

state 0: 
     symbol 1 goto state 1 
     token 266 shift state 2 
     symbol 2 goto state 3 
     default reduce rule 3 
state 1: 
     symbol 1 goto state 1 
     symbol 2 goto state 3 
     token 0 accept 
     default error 
state 2: 
     symbol 1 goto state 1 
     token 257 shift state 6 
     token 258 shift state 7 
     token 259 shift state 4 
     symbol 2 goto state 3 
     token 263 shift state 5 
state 3: 
     token 261 shift state 13 
     token 262 shift state 9 
     token 263 shift state 10 
     token 264 shift state 11 
     token 265 shift state 12 
     token 267 shift state 8 
     symbol 1 goto state 1 
     symbol 2 goto state 3 
..etc 

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

+0

Вы даете очень ценный анализ из отдельных таблиц, спасибо! У меня есть некоторый опыт синтаксического анализа (рекурсивный спуск, antlr), но мне никогда не приходилось справляться без грамматики входа. Я добавил свой вопрос, чтобы добавить ссылку на полный код. В моем примере есть таблица, содержащая правила как информация об отладке, но она не существует в синтаксическом анализаторе, который я пытаюсь изменить ... Спасибо в любом случае/ – Seki

+0

Вау! В настоящее время я пересматриваю книгу драконов и некоторые другие материалы, такие как http://www.cs.uic.edu/~spopuri/cparser.html, чтобы лучше понять внутренние элементы yacc и как сгенерированные таблицы структурированы и взаимодействуют, и я планировал прототип чего-то в perl для выполнения ретро-анализа, и вы даже предоставляете некоторый код. Это действительно полезно. Я сожалею, что не смог забрать ваш ответ больше, чем +1: ^) Я отправлю результаты как можно скорее. Большое спасибо! – Seki

+0

@Seki: Вы получили то, что хотите? Я думаю, что это правильно; основанный на том, как построены таблицы. [Я построил такой генератор LALR и хорошо знаю основную структуру таблицы), я ожидаю, что вы сможете извлечь правила (вплоть до изоморфизма по именам маркеров правил, d должен предоставить их). –