Читаем без скачивания UNIX — универсальная среда программирования - Брайан Керниган
Шрифт:
Интервал:
Закладка:
Синтаксис определения таков:
function: func имя () оператор
procedure: proc имя() оператор
Здесь имя может быть именем некоторой переменной встроенные функции исключаются. Определение, вплоть до открывающейся скобки оператора, должно помещаться на одной строке, как в приведенном выше операторе if.
В отличие от Си тело функции или процедуры может быть любым оператором, не обязательно составным (в скобках). Поскольку символ ; не имеет своего значения в hoc, пустое тело процедуры формируется пустой парой скобок.
Функции и процедуры при вызовах могут получать аргументы, отделенные запятыми. На аргументы ссылаются так же, как в shell: $3 относится к третьему, индексируемому, начиная с единицы, аргументу. Они передаются значениями и внутри функций семантически эквивалентны переменным. Ссылка на аргумент с помощью числа, превышающего число аргументов, переданных процедуре, считается ошибкой. Контроль ошибок динамический, поскольку подпрограмма может иметь переменное число параметров, если ее начальные аргументы влияют на это число (см. функцию printf в Си).
Функции и процедуры могут быть рекурсивными, но стек имеет ограниченную глубину (около сотни вызовов).
Примеры• Ниже показано определение функции Аккерманна в hoc:
$ hoc
time ack() {
if ($1 == 0) return $2+1
if ($2 ==0) return ack($1 - 1, 1)
return ack($1 - 1, ack($1, $2 - 1))
}
ack(3,2)
29
ack(3,3)
61
ack(3,4)
hoc: стек слишком велик (строка 8)
...
• Формула Стирлинга:
$ hoc
func stirl() {
return sqrt(2 * $1 * PI) * ($1 / E) ^ $1 * (1 + 1 / (12 * $1))
}
stirl(10) 3628684.7
stirl(20) 2.4328818e+18
• Функция факториал n!
func fac() if ($1 <= 0) return 1 else return $1 * fac($1 - 1)
Отношение факториала к приближению Стирлинга:
i = 9
while ((i = i + 1) <= 20) {
print i, " ", fac(i)/stirl(i), "n"
}
10 1.0000318
11 1.0000265
12 1.0000224
13 1.0000192
14 1.0000166
15 1.0000146
16 1.0000128
17 1.0000114
18 1.0000102
19 1.0000092
20 1.0000083
Приложение 3
Исходные тексты калькулятора hoc
These files contain all the code from "The Unix Programming Environment", by Brian Kernighan and Rob Pike (Prentice Hall, 1984, ISBN 0-13-937681-X). A separate hoc6 distribution contains any fixes that we have applied to that; the version in this file is from the book.
Copyright © Lucent Technologies, 1997. All Rights Reserved
Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both the copyright notice and this permission notice and warranty disclaimer appear in supporting documentation, and that the name of Lucent Technologies or any of its entities not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission.
LUCENT TECHNOLOGIES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
3.1 hoc1
3.1.1 makefile
hoc1: hoc.o
cc hoc.o -o hoc1
3.1.2 hoc.y
%{
#define YYSTYPE double /* data type of yacc stack */
%}
%token NUMBER
%left '+' /* left associative, same precedence */
%left '*' '/' /* left assoc., higher precedence */
%%
list : /* nothing */
| list 'n'
| list expr 'n' { printf("t%.8gn", $2); }
;
expr: NUMBER { $$ = $1; }
| expr '+' expr { $$ = $1 + $3; }
| expr '-' expr { $$ = $1 - $3; }
| expr '*' expr { $$ = $1 * $3; }
| expr '/' expr { $$ = $1 / $3; }
| '(' expr ')' { $$ = $2; }
;
%%
/* end of grammar */
#include <stdio.h>
#include <ctype.h>
char *progname; /* for error messages */
int lineno = 1;
main(argc, argv) /* hoc1 */
char *argv[];
{
progname = argv[0];
yyparse();
}
yylex() /* hoc1 */
{
int c;
while ((c=getchar()) == ' ' || c == 't')
;
if (c == EOF)
return 0;
if (c == '.' || isdigit(c)) { /* number */
ungetc(c, stdin);
scanf("%lf", &yylval);
return NUMBER;
if (c == 'n')
lineno++;
return c;
}
yyerror(s) /* called for yacc syntax error */
char *s;
{
warning(s, (char*)0);
}
warning(s, t) /* print warning message */
char *s, *t;
{
fprintf(stderr, "%s: %s", progname, s);
if (t)
fprintf(stderr, " %s", t);
fprintf(stderr, " near line %dn", lineno);
}
3.1.3 hoc1.y, версия 1.5
%{
#define YYSTYPE double /* data type of yacc stack */
%}
%token NUMBER
%left '-' '+'
%left '*' '/'
%left UNARYMINUS
%%
list: /* nothing */
| list 'n'
| list expr 'n' { printf("t%.8gn", $2); }
;
expr: NUMBER { $$ = $1; }
| expr '+' expr { $$ = $1 + $3; }
| expr '-' expr { $$ = $1 - $3; }
| expr '*' expr { $$ = $1 * $3; }
| expr '/' expr { $$ = $1 / $3; }
| '-' expr %prec UNARYMINUS { $$ = -$2; } /* new */
| '(' expr ')' { $$ = $2; }
;
%%
/* end of grammar */
#include <stdio.h>
#include <ctype.h>
char *progname; /* for error messages */
int lineno = 1;
main(argc, argv) /* hoc1 */
char *argv[];
{
progname = argv[0];
yyparse();
}
yylex() /* hoc1 */
{
int c;
while ((c=getchar()) == ' ' || с == 't')
;
if (c == EOF)
return 0;
if (c == '.' || isdigit(c)) { /* number */
ungetc(c, stdin);
scanf("%lf", &yylval);
return NUMBER;
}
if (c == 'n')
lineno++;
return c;
}
yyerror(s)
char *s;
{
warning(s, (char *)0);
}
warning(s, t)
char *s, *t;
{
fprintf(stderr, "%s: %s", progname, s);
if (t && *t)
fprintf(stderr, "%s", t);
fprintf(stderr, " near line %dn", lineno);
}
3.2 hoc2
3.2.1 hoc.y
%{
double mem[26]; /* memory for variables 'a'..'z' */
%}
%union { /* stack type */
double val; /* actual value */
int index; /* index into mem[] */
}
%token <val> NUMBER
%token <index> VAR
%type <val> expr
%right '='
%left '+' '-'
%left '*' '/'
%left UNARYMINUS
%%
list: /* nothing */
| list 'n'
| list expr 'n' { printf("t%.8gn $2); }
| list error 'n' { yyerrok; }
;
expr: NUMBER
| VAR { $$ = mem[$1]; }
| VAR '=' expr { $$ = mem[$1] = $3; }
| expr '+' expr { $$ = $1 + $3; }
| expr '-' expr { $$ = $1 - $3; }
| expr '*' expr { $$ = $1 * $3; }
| expr '/' expr {
if ($3 == 0.0)
execerror("division by zero", "");
$$ = $1 / $3;
}
| '(' expr ')' { $$ = $2; }
| '-' expr %prec UNARYMINUS { $$ = -$2; }
;
%%
/* end of grammar */
#include <stdio.h>
#include <ctype.h>
char *progname;
int lineno = 1;
#include <signal.h>
#include <setjmp.h>
jmp_buf begin;
main(argc, argv) /* hoc2 */
char *argv[];
{
int fpecatch();
progname = argv[0];
setjmp(begin);
signal(SIGFPE, fpecatch);
yyparse();
}
yylex() /* hoc2 */
{
int c;
while ((c=getchar()) == ' ' || c == 't')
;
if (c == EOF)
return 0;
if (c == '.' || isdigit(c)) { /* number */
ungetc(c, stdin);
scanf("%lf", &yylval.val);
return NUMBER;
}
if (islower(c)) {
yylval.index = c - 'a'; /* ASCII only */
return VAR;
}
if (c == 'n')
lineno++;
return c;
}
yyerror(s) /* report compile-time error */
char *s;
{
warning(s, (char*)0);
}
execerror(s, t) /* recover from run-time error */
char *s, *t;
{
warning(s, t);
longjmp(begin, 0);
}
fpecatch() /* catch floating point exceptions */