/* * parser main */ #include #include "kw.h" CMD *block(); extern char *mmalloc(); /* * parses supported: * * if x then y elif z else a fi * while x do y done * for i in list do z done */ /* * print out an error message and the offending symbol */ sherr(ptr, text) register CMD *ptr; char *text; { fprintf(stderr, "%s %.*s\n", text, ptr->c_fe-ptr->c_fs, ptr->c_text+ptr->c_fs); } /* * block() parses a block; returns a pointer to it and the terminating * command. Endset is a bitset of the keywords that will terminate * this block (see kw.h for how they're laid out); termpp is where we * return a pointer to the terminating command; if expect is nonzero, * it's the keyword (THEN for IF, DONE for WHILE and FOR) that we * expect to see - not seeing it is an error. */ CMD * block(expect, endset, termpp) CMD **termpp; { register CMD *ptr, *tmp; CMD *head; *termpp = 0; if ((ptr=head=parse()) == (CMD*)0) return (CMD*)0; if (expect && ptr->c_type != expect) { sherr(ptr, "unexpected sym"); garbage(ptr); return (CMD *)0; } if (ptr->c_type & endset) { /* immediately found a match to the */ /* end set, so this is a null block */ /* - we need to return an EMPTY cmd */ *termpp = ptr; ptr = (CMD *)mmalloc(sizeof *ptr); memset(ptr, 0, sizeof *ptr); ptr->c_type = EMPTY; return ptr; } /* Now that we've got the head pointer and have done the neccesary * checking to see if we got what we expected in head, we can accumulate * commands and only worry about checking for the endset. */ while (1) { if ((tmp = parse()) == (CMD*)0) { garbage(head); return (CMD *)0; } if (tmp->c_type & endset) { *termpp = tmp; return head; } ptr->c_next = tmp; ptr = tmp; } } /* block */ /* * ifblock() does an if block */ ifblock(ptr) register CMD *ptr; { register CMD *tmp; if ((ptr->c_expr = parse()) == (CMD *)0) return 0; if ((ptr->c_true = block(THEN, ELSE|ELIF|FI, &ptr->c_ctl)) == (CMD *)0) return 0; switch (ptr->c_ctl->c_type) { case FI: ptr->c_false = (CMD *)0; break; case ELSE: ptr->c_false = ptr->c_ctl; if ((tmp = block(0, FI, &ptr->c_ctl)) == (CMD *)0) { garbage(ptr); return 0; } ptr->c_false->c_next = tmp; break; case ELIF: /* recursively grab elif block, then take the FI from the elif * and make it our own. */ ptr->c_false = ptr->c_ctl; if (ifblock(ptr->c_false) == 0) { garbage(ptr); return 0; } ptr->c_ctl = ptr->c_false->c_ctl; ptr->c_false->c_ctl = (CMD*)0; } return 1; } /* ifblock */ /* * parse() builds a simple list. */ CMD * parse() { register CMD *ptr = (CMD *)0; register sep; if ((ptr = scan()) == (CMD *)0) return (CMD*)0; if ((sep = delim()) == EOF) { garbage(ptr); return (CMD*)0; } /* * build pipelines */ else if (sep == OR || sep == AND || sep == PIPE) { ptr->c_listop = sep; ptr->c_list = parse(); if (ptr->c_list == (CMD *)0) { garbage(ptr); return (CMD *)0; } } else ptr->c_listop = sep; /* * build control blocks, if we need to */ switch (ptr->c_type) { case IF: /* if parse */ if (ifblock(ptr) == 0) { garbage(ptr); return (CMD *)0; } break; case WHILE: /* while parse */ if ((ptr->c_expr = parse()) == (CMD*)0) { garbage(ptr); return (CMD*)0; } case FOR: /* or for ... */ ptr->c_body = block(DO, DONE, &ptr->c_ctl); if (ptr->c_body == (CMD*)0) { garbage(ptr); return (CMD *)0; } break; } return ptr; } /* parse */ /* * garbage() does garbage collection on a block */ garbage(ptr) register CMD *ptr; { register CMD *np; while (ptr) { /* * wipe out control structures */ switch (ptr->c_type) { case IF: case ELIF: garbage(ptr->c_expr); garbage(ptr->c_true); garbage(ptr->c_false); garbage(ptr->c_ctl); break; case WHILE: garbage(ptr->c_expr); case FOR: garbage(ptr->c_body); garbage(ptr->c_ctl); break; } /* * if we're the head of a list, we need to wipe the list */ if (ptr->c_filein) { unlink(ptr->c_filein); mfree(ptr->c_filein); } if (ptr->c_listop) garbage(ptr->c_list); np = ptr; ptr = ptr->c_next; if (np->c_text) mfree(np->c_text); mfree(np); } } /* garbage */