Ниже исходный код — мое решение упражнения 1-24 K&R.
Упражнение 1-24. Напишите программу для проверки программы на языке C на предмет элементарных синтаксических ошибок, таких как несбалансированные круглые скобки, скобки и фигурные скобки. Не забывайте о кавычках, как одинарных, так и двойных, escape-последовательностях и комментариях. (Эта программа сложна, если вы делаете ее в общих чертах.)
В моей программе используются два типа переменных состояния. Скобки(( ), { }, [ ]) и пунктуация (», «», / * * /). Круглые скобки сохраняются в nest[]
и знаки препинания сохраняются в state
. И условия оператора if в основном используют эти две переменные.
Меня не волновал случай фигурной скобки после открытия скобок и скобок.
Я не уверен, что есть лучшее именование переменных, логика, стиль и т. Д. Я не знаю, можно ли мой код написать короче с той же логикой. Любое предложение будет оценено по достоинству.
#include <stdio.h>
/* This program checks if there is unbalanced parentheses, curly braces or brackets.
* This program doesn't check if there is a newline charcter between a single quote or a double quote.
* This program doesn't check if there is non-terminated comment, single quote or double quote.
*/
#define NOTHING 0
#define COMMENT 1
#define SINGLE_QUOTE 2
#define DOUBLE_QUOTE 3
#define PARENTHESIS 1
#define CURLY_BRACE 2
#define BRACKET 3
#define MAX_NESTING_LEVEL 100
void check_paren(void);
void main(void)
{
check_paren();
}
void check_paren(void)
{
int c;
int line;
int state;
int nest[MAX_NESTING_LEVEL] = {NOTHING};
int n;
n = 0;
line = 1;
state = NOTHING;
while ((c = getchar()) != EOF) {
if ( n > MAX_NESTING_LEVEL -1) {
printf("error : line %d : exceed MAX_NESTING_LEVELn", line);
return;
} else if (c == 'n') {
++line;
} else if (state != COMMENT) {
if (c == "https://codereview.stackexchange.com/")
if ((c = getchar()) == '*')
state = COMMENT;
}
if (state == NOTHING && n == 0) {
if (c == ''')
state = SINGLE_QUOTE;
else if (c == '"')
state = DOUBLE_QUOTE;
else if (c == '(')
nest[n++] = PARENTHESIS;
else if (c == '{')
nest[n++] = CURLY_BRACE;
else if (c == '[')
nest[n++] = BRACKET;
else if (c == ')') {
printf("error : line %d : unmatched )n", line);
return;
} else if (c == '}'){
printf("error : line %d : unmatched }n", line);
return;
} else if (c == ']'){
printf("error : line %d : unmatched ]n", line);
return;
}
} else if (state == NOTHING && nest[n-1] == PARENTHESIS) {
if (c == ')')
nest[--n] = NOTHING;
else if (c == '(')
nest[n++] = PARENTHESIS;
else if (c == '[')
nest[n++] = BRACKET;
else if (c == ''')
state = SINGLE_QUOTE;
else if (c == '"')
state = DOUBLE_QUOTE;
else if (c == '}') {
printf("error : line %d : ( + }n", line);
return;
}
else if (c == ']') {
printf("error : line %d : ( + ]n", line);
return;
}
} else if (state == NOTHING && nest[n-1] == CURLY_BRACE) {
if (c == '}')
nest[--n] = NOTHING;
else if (c == '(')
nest[n++] = PARENTHESIS;
else if (c == '{')
nest[n++] = CURLY_BRACE;
else if (c == '[')
nest[n++] = BRACKET;
else if (c == ''')
state = SINGLE_QUOTE;
else if (c == '"')
state = DOUBLE_QUOTE;
else if (c == ')') {
printf("error : line %d : { + )n", line);
return;
}
else if (c == ']') {
printf("error : line %d : { + ]n", line);
return;
}
} else if (state == NOTHING && nest[n-1] == BRACKET) {
if (c == ']')
nest[--n] = NOTHING;
else if (c == '(')
nest[n++] = PARENTHESIS;
else if (c == ''')
state = SINGLE_QUOTE;
else if (c == ')') {
printf("error : line %d : [ + )n", line);
return;
}
else if (c == '}') {
printf("error : line %d : [ + }n", line);
return;
}
} else {
if (state == COMMENT && c == '*') {
if ((c = getchar()) == "https://codereview.stackexchange.com/")
state = NOTHING;
} else if (c == '\')
getchar();
else if (state == SINGLE_QUOTE && c == ''')
state = NOTHING;
else if (state == DOUBLE_QUOTE && c == '"')
state = NOTHING;
}
}
}
1 ответ
Вы можете присвоить номер state
вместо того, чтобы просто держать его в char
. Вы все еще можете использовать значение символа EOF / ноль как «магию». Это значительно упростило бы отладку, и вы можете просто назначить символ чтения для nest
напрямую. Это удалило бы много кода.
Я бы также создал метод, позволяющий избавляться от любых строк и комментариев всякий раз, когда они встречаются. Скобки и прочее в строках в любом случае следует пропускать. Просто продолжайте читать символами, пока не встретите конец (а если он не встретился: что ж, в этом ваша проблема).
char
и int
также может использоваться в switch
заявления вместо if
заявления. Когда вы думаете о «машине состояний», то, вероятно, сначала на ум приходит цикл с переключателем (в любом случае, когда не выполняется функциональное программирование).
Например
switch(c):
case '(':
case '[':
...
nest[n++] = c;
break;
...
В конце концов, основной цикл должен быть максимально коротким и интуитивно понятным.
Вы имеете в виду переменную
c
бесполезно, а затемwhile ((c = getchar()) != EOF)
может быть переписанwhile ((state = getchar()) != EOF)
?— на ол
Тот факт, что у вас могут быть escape-последовательности и два разных двухсимвольных комментария, может потребовать некоторых обходных решений. Но это случай с твоим текущим
int
как я полагаю.— Маартен Бодевес
Нет, поцарапайте это, такие строки, как
else if (state == SINGLE_QUOTE && c == ''')
не будет работать; в конце концов, состояние зависит от прошлого. Но во многих случаи ты мог бы просто выполнитьstate = c
…— Маартен Бодевес
Не знаю, я на 100% понимаю ваш комментарий. Я думаю ты говоришь
else if (c == '"') state = DOUBLE_QUOTE;
возможноelse if (c == '"') state = c
иelse if (c == '(') nest[n++] = PARENTHESIS;
возможноelse if (c == '(') nest[n++] = c;
Если моя мысль верна. Фактически вы имеете в виду, что мое определение макроса можно заменить символьной переменной (c
). Это будет вопрос стиля. Длина кода не изменится.— на ол
Добавлен пример … меньше конверсии всегда означает меньше кода. Во-первых, вы можете избавиться от большинства
#define
заявления.— Маартен Бодевес