|
|
coherent
/*
* sh/sh.y
* The Bourne shell.
* This shell is dedicated to Ciaran Gerald Aidan O'Donnell.
* May he live a thousand minutes (long enough to fix up YACC).
* It is also dedicated to Steve Bourne.
* May he live a thousand seconds.
*/
%{
#include "sh.h"
#define YYERROR { yyerrflag=1; goto YYerract; }
extern NODE *node();
%}
%union {
NODE *yu_node;
char *yu_strp;
int yu_nval;
}
%token _ANDF
%token _ASGN
%token _CASE
%token _CBRAC
%token _DO
%token _DONE
%token _DSEMI
%token _ELIF
%token _ELSE
%token _ESAC
%token _FI
%token _FOR
%token _IF
%token _IN
%token _IORS
%token _NAME
%token _NULL
%token _OBRAC
%token _ORF
%token _THEN
%token _UNTIL
%token _WHILE
%type <yu_node> arg arg_list case_line case_list
%type <yu_node> cmd cmd_line cmd_list cmd_seq
%type <yu_node> control do_list else_part in_name_list
%type <yu_node> logical_cmd name_list opt_cmd_seq pattern_list
%type <yu_node> pipe_cmd sub_shell
%type <yu_strp> asgn name redirect
%type <yu_nval> whuntile
%%
session:
session cmd_line
|
;
cmd_line:
'\n' {
sesp->s_node = NULL;
reset(RCMD);
NOTREACHED;
}
|
cmd_list '\n' {
sesp->s_node = $1;
reset(errflag ? RERR : RCMD);
NOTREACHED;
}
| error '\n' {
keyflush();
keyflag = 1;
reset(RERR);
NOTREACHED;
}
;
if: _IF optnls ;
then: _THEN optnls ;
elif: _ELIF optnls ;
else: _ELSE optnls ;
whuntile: _WHILE optnls { $$ = NWHILE; }
| _UNTIL optnls { $$ = NUNTIL; }
;
do: _DO optnls | _DO ';' optnls ;
in: _IN | _IN sep ;
oror: _ORF optnls;
andand: _ANDF optnls;
or: '|' optnls;
oparen: '(' optnls ;
obrack: _OBRAC optnls ;
cparen: ')' optnls ;
dsemi: _DSEMI optnls ;
cmd_list:
logical_cmd {
$$ = $1;
}
| logical_cmd '&' {
$$ = node(NBACK, $1, NULL);
}
| logical_cmd ';' {
$$ = $1;
}
| logical_cmd '&' cmd_list {
$$ = node(NBACK, $1, $3);
}
| logical_cmd ';' cmd_list {
$$ = node(NLIST, $1, $3);
}
;
logical_cmd:
pipe_cmd {
$$ = $1;
}
| pipe_cmd oror logical_cmd {
$$ = node(NORF, $1, $3);
}
| pipe_cmd andand logical_cmd {
$$ = node(NANDF, $1, $3);
}
;
pipe_cmd:
cmd or pipe_cmd {
$$ = node(NPIPE, $1, $3);
}
| cmd {
$$ = $1;
}
;
cmd:
arg_list_init arg_list {
$$ = node(NCOMS, $2, NULL);
keypop();
}
;
arg_list_init:
{
keypush();
keyflag = 1;
}
;
arg_list:
arg arg_list {
if (($1->n_type == NCTRL && $2->n_type == NARGS)
|| ($1->n_type == NARGS && $2->n_type == NCTRL)) {
YYERROR;
}
($$ = $1)->n_next = $2;
}
| arg {
$$ = $1;
}
;
arg:
redirect {
$$ = node(NIORS, $1, NULL);
}
| name {
$$ = node(NARGS, $1, NULL);
keyflag = 0;
}
| asgn {
$$ = node(NASSG, $1, NULL);
}
| control {
if (!keyflag) {
YYERROR;
}
$$ = node(NCTRL, $1, NULL);
keyflag = 0;
}
;
redirect: _IORS {
$$ = duplstr(strt, 0);
}
;
name: _NAME {
$$ = duplstr(strt, 0);
}
;
asgn: _ASGN {
$$ = duplstr(strt, 0);
}
;
control:
_FOR name in_name_list sep do_list _DONE {
$$ = node(NFOR, $2, node(NFOR2, $3, node(NLIST, $5, NULL)));
$$->n_next->n_next->n_next = $$->n_next;
}
| _FOR name in_name_list do_list _DONE {
$$ = node(NFOR, $2, node(NFOR2, $3, node(NLIST, $4, NULL)));
$$->n_next->n_next->n_next = $$->n_next;
}
| _CASE name sep in case_list _ESAC {
$$ = node(NCASE, $2, $5);
}
| _CASE name in case_list _ESAC {
$$ = node(NCASE, $2, $4);
}
| whuntile cmd_seq do_list _DONE {
$$ = node($1, $2, node(NLIST, $3, NULL));
$$->n_next->n_next = $$;
}
| if cmd_seq then opt_cmd_seq else_part _FI {
$$ = node(NIF, node(NNULL, $2, $4), $5);
}
| oparen opt_cmd_seq ')' {
$$ = node(NPARN, $2, NULL);
}
| obrack opt_cmd_seq _CBRAC {
$$ = node(NBRAC, $2, NULL);
}
;
in_name_list:
_IN name_list {
$$ = $2;
}
| {
$$ = node(NARGS, "\"$@\"", NULL);
}
;
name_list:
name name_list {
$$ = node(NARGS, $1, $2);
}
| {
$$ = NULL;
}
;
case_list:
case_line dsemi case_list {
register NODE *np;
for (np=$1; np->n_next; np=np->n_next)
;
np->n_next = $3;
$$ = $1;
}
| case_line {
$$ = $1;
}
| {
$$ = NULL;
}
;
case_line:
pattern_list cparen opt_cmd_seq {
$$ = node(NCASE2, $3, $1);
}
;
pattern_list:
name '|' pattern_list {
$$ = node(NCASE3, $1, $3);
}
| name {
$$ = node(NCASE3, $1, NULL);
}
;
do_list:
do opt_cmd_seq {
$$ = $2;
}
| {
$$ = NULL;
}
;
else_part:
elif cmd_seq then opt_cmd_seq else_part {
$$ = node(NIF, node(NNULL, $2, $4), $5);
}
| else opt_cmd_seq {
$$ = node(NELSE, $2, NULL);
}
| {
$$ = NULL;
}
;
opt_cmd_seq:
cmd_seq {
$$ = $1;
}
|
{
$$ = NULL;
}
;
cmd_seq:
cmd_list nls cmd_seq {
$$ = node(NLIST, $1, $3);
}
| cmd_list optnls {
$$ = $1;
}
;
sep: nls
| ';'
| ';' nls
;
optnls: nls
|
;
nls: '\n'
| nls '\n'
;
%%
/*
* Create a node.
*/
NODE *
node(type, auxp, next)
NODE *auxp, *next;
{
register NODE *np;
np = (NODE *) balloc(sizeof (NODE));
np->n_type = type;
np->n_auxp = auxp;
np->n_next = next;
return np;
}
#define NBPC 8
#define NKEY 8
static char keys[NKEY] = { 0 };
static int keyi = NKEY * NBPC;
keyflush()
{
register char *kp;
for (kp = keys+NKEY; kp > keys; *--kp = 0)
;
keyi = NKEY * NBPC;
}
keypop()
{
register char *kp;
register int km;
if ((km = keyi++) >= NKEY * NBPC) {
panic(11);
NOTREACHED;
}
kp = keys + (km / NBPC);
km = 1 << (km %= NBPC);
keyflag = (*kp & km) ? 1 : 0;
*kp &= ~km;
}
keypush()
{
register char *kp;
register int km;
if ((km = --keyi) < 0) {
panic(12);
NOTREACHED;
}
if (keyflag) {
kp = keys + (km / NBPC);
km = 1 << (km %= NBPC);
*kp |= km;
}
}
/*
* The following fragments might implement named pipes.
* The token declaration goes in the header.
* The nopen production should go with the others of its ilk.
* The production fragment goes into arg:
%token _NOPEN _NCLOSE
nopen: _NOPEN optnls ;
| nopen pipe_cmd ')' {
$$ = node(NRPIPE, $2, NULL);
}
| oparen pipe_cmd _NCLOSE {
$$ = node(NWPIPE, $2, NULL);
}
*
*/
/* end of sh/sh.y */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.