Хрестоматия по программированию на Си в Unix
#define TOKEN(v) (*v)->token
#define TYPE(v) (*v)->type
#define eq(str1, str2) (!strcmp(str1, str2))
jmp_buf breakpoint;
#define ERR(msg,val) { printf("%s\n", msg);longjmp(breakpoint, val+1);}
typedef struct {
char *name; /* Имя переменной */
int value; /* Значение переменной */
int isset; /* Получила ли значение ? */
} Var;
#define MAXV 40
Var vars[MAXV];
/* Получить значение переменной */
int getVar(char *name){ Var *ptr;
for(ptr=vars; ptr->name; ptr++)
if(eq(name, ptr->name)){
if(ptr->isset) return ptr->value;
printf("%s: ", name); ERR("variable is unbound yet", 0);
}
printf("%s: ", name); ERR("undefined variable", 0);
}
/* Создать новую переменную */
Var *internVar(char *name){ Var *ptr;
for(ptr=vars; ptr->name; ptr++)
if(eq(name, ptr->name)) return ptr;
ptr->name = strdup(name);
ptr->isset = 0; ptr->value = 0; return ptr;
}
/* Установить значение переменной */
void setVar(Var *ptr, int val){ ptr->isset = 1; ptr->value = val; }
/* Распечатать значения переменных */
void printVars(){ Var *ptr;
for(ptr=vars; ptr->name; ++ptr)
printf("\t%s %s %d\n", ptr->isset ? "BOUND ":"UNBOUND",
ptr->name, ptr->value);
}
А. Богатырев, 1992-95 - 339 - Си в UNIX
/* Синтаксический разбор и одновременное вычисление ----------- */
/* Вычисление встроенных функций */
int apply(char *name, int args[], int nargs){
if(eq(name, "power2")){
if(nargs != 1) ERR("power2: wrong argument count", 0);
return (1 << args[0]);
} else if(eq(name, "min")){
if(nargs != 2) ERR("min: wrong argument count", 0);
return (args[0] < args[1] ? args[0] : args[1]);
} else if(eq(name, "max")){
if(nargs != 2) ERR("max: wrong argument count", 0);
return (args[0] < args[1] ? args[1] : args[0]);
} else if(eq(name, "sum")){ register i, sum;
for(i=0, sum=0; i < nargs; sum += args[i++]);
return sum;
} else if(eq(name, "rand")){
switch(nargs){
case 0: return rand();
case 1: return rand() % args[0];
case 2: return args[0] + rand() % (args[1] - args[0] + 1);
default: ERR("rand: wrong argument count", 0);
}
}
ERR("Unknown function", args[0]);
}
/* Вычислить выражение из списка лексем. */
/* Синтаксис задан праворекурсивной грамматикой */
int expr(Token *t){ int val = 0;
if(val = setjmp(breakpoint)) return val - 1;
val = expression(&t);
if(t){ printlex(NULL, t); ERR("Extra tokens", val); }
return val;
}
/* = |
";" */
int expression(Token **v){ int arg = expass(v);
if(*v && TYPE(v) == SMC ){
NEXT(v); return expression(v);
} else return arg;
}
/* = <ПЕРЕМЕННАЯ> "=" |
*/
int expass(Token **v){ int arg;
if(*v && (*v)->next && (*v)->next->type == OP &&
eq((*v)->next->token, "=")){ Var *ptr;
/* присваивание (assignment) */
if( TYPE(v) != ID ) /* слева нужна переменная */
ERR("Lvalue needed", 0);
ptr = internVar(TOKEN(v));
NEXT(v); NEXT(v); setVar(ptr, arg = expass(v)); return arg;
}
return exp0(v);
}
А. Богатырев, 1992-95 - 340 - Си в UNIX
/* = | "||" */
int exp0(Token **v){ int arg = exp1(v);
if(*v && TYPE(v) == OP && eq(TOKEN(v), "||")){
NEXT(v); return(exp0(v) || arg );
/* помещаем arg ВТОРЫМ, чтобы второй операнд вычислялся
* ВСЕГДА (иначе не будет исчерпан список токенов и
* возникнет ошибка в expr(); Это не совсем по правилам Си.
*/
} else return arg;
}
/* = | "&&" */
int exp1(Token **v){ int arg = exp2(v);
if(*v && TYPE(v) == OP && eq(TOKEN(v), "&&")){
NEXT(v); return(exp1(v) && arg);
} else return arg;
}
/* = | "|" */
int exp2(Token **v){ int arg = exp2a(v);
if(*v && TYPE(v) == OP && eq(TOKEN(v), "|")){
NEXT(v); return( arg | exp2(v));
} else return arg;
}
/* = | "^" */
int exp2a(Token **v){ int arg = exp2b(v);
if(*v && TYPE(v) == OP && eq(TOKEN(v), "^")){
NEXT(v); return( arg ^ exp2a(v));
} else return arg;
}
/* = | "&" */
int exp2b(Token **v){ int arg = exp2c(v);
if(*v && TYPE(v) == OP && eq(TOKEN(v), "&")){
NEXT(v); return( arg & exp2b(v));
} else return arg;
}
/* = | "=="
| "!=" */
int exp2c(Token **v){ int arg = exp3(v);
if(*v && TYPE(v) == OP && eq(TOKEN(v), "==")){
NEXT(v); return( arg == exp3(v));
} else if(*v && TYPE(v) == OP && eq(TOKEN(v), "!=")){
NEXT(v); return( arg != exp3(v));
} else return arg;
}
А. Богатырев, 1992-95 - 341 - Си в UNIX
/* = | ">"
| "<"
| ">="
| "<=" */
int exp3(Token **v){ int arg = exp3a(v);
if(*v && TYPE(v) == OP && eq(TOKEN(v), ">")){
NEXT(v); return( arg && exp3(v));
}else if(*v && TYPE(v) == OP && eq(TOKEN(v), "<")){
NEXT(v); return( arg && exp3(v));
}else if(*v && TYPE(v) == OP && eq(TOKEN(v), ">=")){
NEXT(v); return( arg && exp3(v));
}else if(*v && TYPE(v) == OP && eq(TOKEN(v), "<=")){
NEXT(v); return( arg && exp3(v));
} else return arg;
}
/* = | "<<"
| ">>" */
int exp3a(Token **v){ int arg = exp4(v);
if(*v && TYPE(v) == OP && eq(TOKEN(v), "<<")){
NEXT(v); return( arg << exp3a(v));
}else if(*v && TYPE(v) == OP && eq(TOKEN(v), ">>")){
NEXT(v); return( arg && exp3a(v));
} else return arg;
}
/* = | "+"
| "-" */
int exp4(Token **v){ int arg = exp5(v);
if(*v && TYPE(v) == OP && eq(TOKEN(v), "+")){
NEXT(v); return( arg + exp4(v));
}else if(*v && TYPE(v) == OP && eq(TOKEN(v), "-")){
NEXT(v); return( arg - exp4(v));
} else return arg;
}
/* = | "*"
| "/"
| "%" */
int exp5(Token **v){ int arg = exp6(v), arg1;
if(*v && TYPE(v) == OP && eq(TOKEN(v), "*")){
NEXT(v); return( arg * exp5(v));
}else if(*v && TYPE(v) == OP && eq(TOKEN(v), "/")){
NEXT(v); if((arg1 = exp5(v)) == 0) ERR("Zero divide", arg);
return( arg / arg1);
}else if(*v && TYPE(v) == OP && eq(TOKEN(v), "%")){
NEXT(v); if((arg1 = exp5(v)) == 0) ERR("Zero module", arg);
return( arg % arg1);
} else return arg;
}
А. Богатырев, 1992-95 - 342 - Си в UNIX
/* = "!" | "~" | "-"
А. Богатырев, 1992-95 - 280 - Си в UNIX
| <ИМЯФУНКЦИИ> "(" [ [ "," ]... ] ")"
| <ЧИСЛО>
| */
int exp6(Token **v){ int arg;
if( !*v) ERR("Lost token", 0);
if(TYPE(v) == OP && eq(TOKEN(v), "!")){
NEXT(v); return !exp6(v);
}
if(TYPE(v) == OP && eq(TOKEN(v), "~")){
NEXT(v); return ~exp6(v);
}
if(TYPE(v) == OP && eq(TOKEN(v), "-")){
NEXT(v); return -exp6(v); /* унарный минус */
}
if(TYPE(v) == OPEN){
NEXT(v); arg = expression(v);
if( !*v || TYPE(v) != CLOSE) ERR("Lost ')'", arg);
NEXT(v); return arg;
}
if(TYPE(v) == NUM){ /* изображение числа */
arg = atoi(TOKEN(v)); NEXT(v); return arg;
}
if(TYPE(v) == ID){
char *name = (*v)->token; int args[20], nargs = 0;
NEXT(v);
if(! (*v && TYPE(v) == OPEN)){ /* Переменная */
return expvar(v, name);
}
/* Функция */
args[0] = 0;
do{ NEXT(v);
if( *v && TYPE(v) == CLOSE ) break; /* f() */
args[nargs++] = expression(v);
} while( *v && TYPE(v) == COMMA);
if(! (*v && TYPE(v) == CLOSE)) ERR("Error in '()'", args[0]);
NEXT(v);
return apply(name, args, nargs);
}
printlex(TOKEN(v), *v); ERR("Unknown token type", 0);
}
/* = <ПЕРЕМЕННАЯ> |
<ПЕРЕМЕННАЯ> "++" |
<ПЕРЕМЕННАЯ> "--"
Наши операции ++ и -- соответствуют ++x и --x из Си */
int expvar(Token **v, char *name){
int arg = getVar(name); Var *ptr = internVar(name);
if(*v && TYPE(v) == OP){
if(eq(TOKEN(v), "++")){ NEXT(v); setVar(ptr, ++arg); return arg; }
if(eq(TOKEN(v), "--")){ NEXT(v); setVar(ptr, --arg); return arg; }
}
return arg;
}
А. Богатырев, 1992-95 - 343 - Си в UNIX
/* Головная функция ------------------------------------------- */
char input[256];
Token *head, *tail;
void main(){
do{ printf(": "); fflush(stdout);
if( !gets(input)) break;
if(!*input){ printVars(); continue; }
if(eq(input, "!!")) ; /* ничего не делать, т.е. повторить */
else{ if(head) freelex(&head); lex(&head, &tail, input); }
printf("Result: %d\n", expr(head));
} while(1); putchar('\n');
}
7.69. Напишите программу, выделяющую n-ое поле из каждой строки файла. Поля разделя-
ются двоеточиями. Предусмотрите задание символа-разделителя из аргументов программы.
Используйте эту программу для выделения поля "домашний каталог" из файла /etc/passwd.
Для выделения очередного поля можно использовать следующую процедуру:
main(){
char c, *next, *strchr(); int nfield;
char *s = "11111:222222222:333333:444444";
for(nfield=0;;nfield++){
if(next = strchr(s, ':')){
c= *next; *next= '\0';
}
printf( "Поле #%d: '%s'\n", nfield, s);
/* можно сделать с полем s что-то еще */
if(next){ *next= c; s= next+1; continue; }
else { break; /* последнее поле */ }
}
}
7.70. Разработайте архитектуру и систему команд учебной машины и напишите интерпре-
татор учебного ассемблера, отрабатывающего по крайней мере такие команды: