📄 syn.c
字号:
}static struct op *caselist(){ register struct op *t, *tl; int c; c = token(CONTIN|KEYWORD|ALIAS); /* A {...} can be used instead of in...esac for case statements */ if (c == IN) c = ESAC; else if (c == '{') c = '}'; else syntaxerr((char *) 0); t = tl = NULL; while ((tpeek(CONTIN|KEYWORD|ESACONLY)) != c) { /* no ALIAS here */ struct op *tc = casepart(c); if (tl == NULL) t = tl = tc, tl->right = NULL; else tl->right = tc, tl = tc; } musthave(c, KEYWORD|ALIAS); return (t);}static struct op *casepart(endtok) int endtok;{ register struct op *t; register int c; XPtrV ptns; XPinit(ptns, 16); t = newtp(TPAT); c = token(CONTIN|KEYWORD); /* no ALIAS here */ if (c != '(') REJECT; do { musthave(LWORD, 0); XPput(ptns, yylval.cp); } while ((c = token(0)) == '|'); REJECT; XPput(ptns, NULL); t->vars = (char **) XPclose(ptns); musthave(')', 0); t->left = c_list(TRUE); /* Note: Posix requires the ;; */ if ((tpeek(CONTIN|KEYWORD|ALIAS)) != endtok) musthave(BREAK, CONTIN|KEYWORD|ALIAS); return (t);}static struct op *function_body(name, ksh_func) char *name; int ksh_func; /* function foo { ... } vs foo() { .. } */{ char *sname, *p; struct op *t; int old_func_parse; sname = wdstrip(name); /* Check for valid characters in name. posix and ksh93 say only * allow [a-zA-Z_0-9] but this allows more as old pdksh's have * allowed more (the following were never allowed: * nul space nl tab $ ' " \ ` ( ) & | ; = < > * C_QUOTE covers all but = and adds # [ ? *) */ for (p = sname; *p; p++) if (ctype(*p, C_QUOTE) || *p == '=') yyerror("%s: invalid function name\n", sname); t = newtp(TFUNCT); t->str = sname; t->u.ksh_func = ksh_func; t->lineno = source->line; /* Note that POSIX allows only compound statements after foo(), sh and * at&t ksh allow any command, go with the later since it shouldn't * break anything. However, for function foo, at&t ksh only accepts * an open-brace. */ if (ksh_func) { musthave('{', CONTIN|KEYWORD|ALIAS); /* } */ REJECT; } old_func_parse = e->flags & EF_FUNC_PARSE; e->flags |= EF_FUNC_PARSE; if ((t->left = get_command(CONTIN)) == (struct op *) 0) { /* * Probably something like foo() followed by eof or ;. * This is accepted by sh and ksh88. * To make "typset -f foo" work reliably (so its output can * be used as input), we pretend there is a colon here. */ t->left = newtp(TCOM); t->left->args = (char **) alloc(sizeof(char *) * 2, ATEMP); t->left->args[0] = alloc(sizeof(char) * 3, ATEMP); t->left->args[0][0] = CHAR; t->left->args[0][1] = ':'; t->left->args[0][2] = EOS; t->left->args[1] = (char *) 0; t->left->vars = (char **) alloc(sizeof(char *), ATEMP); t->left->vars[0] = (char *) 0; t->left->lineno = 1; } if (!old_func_parse) e->flags &= ~EF_FUNC_PARSE; return t;}static char **wordlist(){ register int c; XPtrV args; XPinit(args, 16); /* Posix does not do alias expansion here... */ if ((c = token(CONTIN|KEYWORD|ALIAS)) != IN) { if (c != ';') /* non-POSIX, but at&t ksh accepts a ; here */ REJECT; return NULL; } while ((c = token(0)) == LWORD) XPput(args, yylval.cp); if (c != '\n' && c != ';') syntaxerr((char *) 0); if (XPsize(args) == 0) { XPfree(args); return NULL; } else { XPput(args, NULL); return (char **) XPclose(args); }}/* * supporting functions */static struct op *block(type, t1, t2, wp) int type; struct op *t1, *t2; char **wp;{ register struct op *t; t = newtp(type); t->left = t1; t->right = t2; t->vars = wp; return (t);}const struct tokeninfo { const char *name; short val; short reserved;} tokentab[] = { /* Reserved words */ { "if", IF, TRUE }, { "then", THEN, TRUE }, { "else", ELSE, TRUE }, { "elif", ELIF, TRUE }, { "fi", FI, TRUE }, { "case", CASE, TRUE }, { "esac", ESAC, TRUE }, { "for", FOR, TRUE },#ifdef KSH { "select", SELECT, TRUE },#endif /* KSH */ { "while", WHILE, TRUE }, { "until", UNTIL, TRUE }, { "do", DO, TRUE }, { "done", DONE, TRUE }, { "in", IN, TRUE }, { "function", FUNCTION, TRUE }, { "time", TIME, TRUE }, { "{", '{', TRUE }, { "}", '}', TRUE }, { "!", BANG, TRUE },#ifdef KSH { "[[", DBRACKET, TRUE },#endif /* KSH */ /* Lexical tokens (0[EOF], LWORD and REDIR handled specially) */ { "&&", LOGAND, FALSE }, { "||", LOGOR, FALSE }, { ";;", BREAK, FALSE },#ifdef KSH { "((", MDPAREN, FALSE }, { "|&", COPROC, FALSE },#endif /* KSH */ /* and some special cases... */ { "newline", '\n', FALSE }, { 0 }};voidinitkeywords(){ register struct tokeninfo const *tt; register struct tbl *p; tinit(&keywords, APERM, 32); /* must be 2^n (currently 20 keywords) */ for (tt = tokentab; tt->name; tt++) { if (tt->reserved) { p = tenter(&keywords, tt->name, hash(tt->name)); p->flag |= DEFINED|ISSET; p->type = CKEYWD; p->val.i = tt->val; } }}static voidsyntaxerr(what) const char *what;{ char redir[6]; /* 2<<- is the longest redirection, I think */ const char *s; struct tokeninfo const *tt; int c; if (!what) what = "unexpected"; REJECT; c = token(0); Again: switch (c) { case 0: if (nesting.start_token) { c = nesting.start_token; source->errline = nesting.start_line; what = "unmatched"; goto Again; } /* don't quote the EOF */ yyerror("syntax error: unexpected EOF\n"); /*NOTREACHED*/ case LWORD: s = snptreef((char *) 0, 32, "%S", yylval.cp); break; case REDIR: s = snptreef(redir, sizeof(redir), "%R", yylval.iop); break; default: for (tt = tokentab; tt->name; tt++) if (tt->val == c) break; if (tt->name) s = tt->name; else { if (c > 0 && c < 256) { redir[0] = c; redir[1] = '\0'; } else shf_snprintf(redir, sizeof(redir), "?%d", c); s = redir; } } yyerror("syntax error: `%s' %s\n", s, what);}static voidnesting_push(save, tok) struct nesting_state *save; int tok;{ *save = nesting; nesting.start_token = tok; nesting.start_line = source->line;}static voidnesting_pop(saved) struct nesting_state *saved;{ nesting = *saved;}static struct op *newtp(type) int type;{ register struct op *t; t = (struct op *) alloc(sizeof(*t), ATEMP); t->type = type; t->u.evalflags = 0; t->args = t->vars = NULL; t->ioact = NULL; t->left = t->right = NULL; t->str = NULL; return (t);}struct op *compile(s) Source *s;{ nesting.start_token = 0; nesting.start_line = 0; herep = heres; source = s; yyparse(); return outtree;}/* This kludge exists to take care of sh/at&t ksh oddity in which * the arguments of alias/export/readonly/typeset have no field * splitting, file globbing, or (normal) tilde expansion done. * at&t ksh seems to do something similar to this since * $ touch a=a; typeset a=[ab]; echo "$a" * a=[ab] * $ x=typeset; $x a=[ab]; echo "$a" * a=a * $ */static intassign_command(s) char *s;{ char c = *s; if (Flag(FPOSIX) || !*s) return 0; return (c == 'a' && strcmp(s, "alias") == 0) || (c == 'e' && strcmp(s, "export") == 0) || (c == 'r' && strcmp(s, "readonly") == 0) || (c == 't' && strcmp(s, "typeset") == 0);}/* Check if we are in the middle of reading an alias */static intinalias(s) struct source *s;{ for (; s && s->type == SALIAS; s = s->next) if (!(s->flags & SF_ALIASEND)) return 1; return 0;}#ifdef KSH/* Order important - indexed by Test_meta values * Note that ||, &&, ( and ) can't appear in as unquoted strings * in normal shell input, so these can be interpreted unambiguously * in the evaluation pass. */static const char dbtest_or[] = { CHAR, '|', CHAR, '|', EOS };static const char dbtest_and[] = { CHAR, '&', CHAR, '&', EOS };static const char dbtest_not[] = { CHAR, '!', EOS };static const char dbtest_oparen[] = { CHAR, '(', EOS };static const char dbtest_cparen[] = { CHAR, ')', EOS };const char *const dbtest_tokens[] = { dbtest_or, dbtest_and, dbtest_not, dbtest_oparen, dbtest_cparen };const char db_close[] = { CHAR, ']', CHAR, ']', EOS };const char db_lthan[] = { CHAR, '<', EOS };const char db_gthan[] = { CHAR, '>', EOS };/* Test if the current token is a whatever. Accepts the current token if * it is. Returns 0 if it is not, non-zero if it is (in the case of * TM_UNOP and TM_BINOP, the returned value is a Test_op). */static intdbtestp_isa(te, meta) Test_env *te; Test_meta meta;{ int c = tpeek(ARRAYVAR | (meta == TM_BINOP ? 0 : CONTIN)); int uqword = 0; char *save = (char *) 0; int ret = 0; /* unquoted word? */ uqword = c == LWORD && *ident; if (meta == TM_OR) ret = c == LOGOR; else if (meta == TM_AND) ret = c == LOGAND; else if (meta == TM_NOT) ret = uqword && strcmp(yylval.cp, dbtest_tokens[(int) TM_NOT]) == 0; else if (meta == TM_OPAREN) ret = c == '(' /*)*/; else if (meta == TM_CPAREN) ret = c == /*(*/ ')'; else if (meta == TM_UNOP || meta == TM_BINOP) { if (meta == TM_BINOP && c == REDIR && (yylval.iop->flag == IOREAD || yylval.iop->flag == IOWRITE)) { ret = 1; save = wdcopy(yylval.iop->flag == IOREAD ? db_lthan : db_gthan, ATEMP); } else if (uqword && (ret = (int) test_isop(te, meta, ident))) save = yylval.cp; } else /* meta == TM_END */ ret = uqword && strcmp(yylval.cp, db_close) == 0; if (ret) { ACCEPT; if (meta != TM_END) { if (!save) save = wdcopy(dbtest_tokens[(int) meta], ATEMP); XPput(*te->pos.av, save); } } return ret;}static const char *dbtestp_getopnd(te, op, do_eval) Test_env *te; Test_op op; int do_eval;{ int c = tpeek(ARRAYVAR); if (c != LWORD) return (const char *) 0; ACCEPT; XPput(*te->pos.av, yylval.cp); return null;}static intdbtestp_eval(te, op, opnd1, opnd2, do_eval) Test_env *te; Test_op op; const char *opnd1; const char *opnd2; int do_eval;{ return 1;}static voiddbtestp_error(te, offset, msg) Test_env *te; int offset; const char *msg;{ te->flags |= TEF_ERROR; if (offset < 0) { REJECT; /* Kludgy to say the least... */ symbol = LWORD; yylval.cp = *(XPptrv(*te->pos.av) + XPsize(*te->pos.av) + offset); } syntaxerr(msg);}#endif /* KSH */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -