1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
|
/* Yash: yet another shell */
/* parser.h: syntax parser */
/* (C) 2007-2025 magicant */
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Limitation: Don't include "parser.h" and <termios.h> at a time
* because identifiers prefixed "c_" conflict. */
#ifndef YASH_PARSER_H
#define YASH_PARSER_H
#include <stddef.h>
#include "input.h"
#include "refcount.h"
/********** Parse Tree Structures **********/
/* Basically, parse tree structure elements constitute linked lists.
* For each element, the `next' member points to the next element. */
/* and/or list */
typedef struct and_or_T {
struct and_or_T *next;
struct pipeline_T *ao_pipelines; /* pipelines in this and/or list */
_Bool ao_async;
} and_or_T;
/* ao_async: indicates this and/or list is postfixed by "&", which means the
* list is executed asynchronously. */
/* pipeline */
typedef struct pipeline_T {
struct pipeline_T *next;
struct command_T *pl_commands; /* commands in this pipeline */
_Bool pl_neg, pl_cond;
} pipeline_T;
/* pl_neg: indicates this pipeline is prefix by "!", in which case the exit
* status of the pipeline is inverted.
* pl_cond: true if prefixed by "&&", false by "||". Ignored for the first
* pipeline in an and/or list. */
/* type of command_T */
typedef enum {
CT_SIMPLE, /* simple command */
CT_GROUP, /* command group enclosed by { } */
CT_SUBSHELL, /* subshell command group enclosed by ( ) */
CT_IF, /* if command */
CT_FOR, /* for command */
CT_WHILE, /* while/until command */
CT_CASE, /* case command */
#if YASH_ENABLE_DOUBLE_BRACKET
CT_BRACKET, /* double-bracket command */
#endif
CT_FUNCDEF, /* function definition */
} commandtype_T;
/* command in a pipeline */
typedef struct command_T {
struct command_T *next;
refcount_T refcount;
commandtype_T c_type;
unsigned long c_lineno; /* line number */
struct redir_T *c_redirs; /* redirections */
union {
struct {
struct assign_T *assigns; /* assignments */
void **words; /* command name and arguments */
_Bool isdeclutil; /* is a declaration utility? */
} simplecommand;
struct and_or_T *subcmds; /* contents of command group */
struct ifcommand_T *ifcmds; /* contents of if command */
struct {
wchar_t *forname; /* loop variable of for loop */
void **forwords; /* words assigned to loop variable */
struct and_or_T *forcmds; /* commands executed in for loop */
} forloop;
struct {
_Bool whltype; /* 1 for while loop, 0 for until */
struct and_or_T *whlcond; /* loop condition of while/until loop */
struct and_or_T *whlcmds; /* commands executed in loop */
} whileloop;
struct {
struct wordunit_T *casword; /* word compared to case patterns */
struct caseitem_T *casitems; /* pairs of patterns and commands */
} casecommand;
struct dbexp_T *dbexp; /* double-bracket command expression */
struct {
struct wordunit_T *funcname; /* name of function */
struct command_T *funcbody; /* body of function */
} funcdef;
} c_content;
} command_T;
#define c_assigns c_content.simplecommand.assigns
#define c_words c_content.simplecommand.words
#define c_isdeclutil c_content.simplecommand.isdeclutil
#define c_subcmds c_content.subcmds
#define c_ifcmds c_content.ifcmds
#define c_forname c_content.forloop.forname
#define c_forwords c_content.forloop.forwords
#define c_forcmds c_content.forloop.forcmds
#define c_whltype c_content.whileloop.whltype
#define c_whlcond c_content.whileloop.whlcond
#define c_whlcmds c_content.whileloop.whlcmds
#define c_casword c_content.casecommand.casword
#define c_casitems c_content.casecommand.casitems
#define c_dbexp c_content.dbexp
#define c_funcname c_content.funcdef.funcname
#define c_funcbody c_content.funcdef.funcbody
/* `c_words' and `c_forwords' are NULL-terminated arrays of pointers to
* `wordunit_T' that are cast to `void *'.
* If `c_forwords' is NULL, the for loop doesn't have the "in" clause.
* If `c_forwords[0]' is NULL, the "in" clause exists and is empty. */
/* condition and commands of an if command */
typedef struct ifcommand_T {
struct ifcommand_T *next;
struct and_or_T *ic_condition; /* condition */
struct and_or_T *ic_commands; /* commands */
} ifcommand_T;
/* For an "else" clause, `next' and `ic_condition' are NULL. */
/* type of an case item terminator symbol */
typedef enum {
CC_BREAK, // ;;
CC_FALLTHRU, // ;&
CC_CONTINUE, // ;| aka ;;&
} casecont_T;
/* patterns and commands of a case command */
typedef struct caseitem_T {
struct caseitem_T *next;
void **ci_patterns; /* patterns to do matching */
struct and_or_T *ci_commands; /* commands executed if match succeeds */
casecont_T ci_cont; /* terminator symbol type */
} caseitem_T;
/* `ci_patterns' is a NULL-terminated array of pointers to `wordunit_T' that are
* cast to `void *'. */
/* type of dbexp_T */
typedef enum {
DBE_OR, /* the "||" operator, two operand expressions */
DBE_AND, /* the "&&" operator, two operand expressions */
DBE_NOT, /* the "!" operator, one operand expression */
DBE_UNARY, /* -f, -n, etc., one operand word */
DBE_BINARY, /* -eq, =, etc., two operand words */
DBE_STRING, /* single string primary, one operand word */
} dbexptype_T;
/* operand of expression in double-bracket command */
typedef union dboperand_T {
struct dbexp_T *subexp;
struct wordunit_T *word;
} dboperand_T;
/* expression in double-bracket command */
typedef struct dbexp_T {
dbexptype_T type;
wchar_t *operator;
dboperand_T lhs, rhs;
} dbexp_T;
/* `operator' is NULL for non-primary expressions */
/* `lhs' is NULL for one-operand expressions */
/* embedded command */
typedef struct embedcmd_T {
_Bool is_preparsed;
union {
wchar_t *unparsed;
struct and_or_T *preparsed;
} value;
} embedcmd_T;
/* type of wordunit_T */
typedef enum {
WT_STRING, /* string (including quotes) */
WT_PARAM, /* parameter expansion */
WT_CMDSUB, /* command substitution */
WT_ARITH, /* arithmetic expansion */
} wordunittype_T;
/* element of a word subject to expansion */
typedef struct wordunit_T {
struct wordunit_T *next;
wordunittype_T wu_type;
union {
wchar_t *string; /* string (including quotes) */
struct paramexp_T *param; /* parameter expansion */
struct embedcmd_T cmdsub; /* command substitution */
struct wordunit_T *arith; /* expression for arithmetic expansion */
} wu_value;
} wordunit_T;
#define wu_string wu_value.string
#define wu_param wu_value.param
#define wu_cmdsub wu_value.cmdsub
#define wu_arith wu_value.arith
/* In arithmetic expansion, the expression is subject to parameter expansion
* before it is parsed. So `wu_arith' is of type `wordunit_T *'. */
/* type of paramexp_T */
typedef enum {
PT_NONE, /* normal */
PT_MINUS, /* ${name-subst} */
PT_PLUS, /* ${name+subst} */
PT_ASSIGN, /* ${name=subst} */
PT_ERROR, /* ${name?subst} */
PT_MATCH, /* ${name#match}, ${name%match} */
PT_SUBST, /* ${name/match/subst} */
PT_MASK = (1 << 3) - 1,
PT_NUMBER = 1 << 3, /* ${#name} (only valid for PT_NONE) */
PT_COLON = 1 << 4, /* ${name:-subst}, ${name:+subst}, etc. */
PT_MATCHHEAD = 1 << 5, /* only matches at the beginning of a string */
PT_MATCHTAIL = 1 << 6, /* only matches at the end of a string */
PT_MATCHLONGEST = 1 << 7, /* match as long as possible */
PT_SUBSTALL = 1 << 8, /* substitute all the match */
PT_NEST = 1 << 9, /* have nested expn. like ${${VAR#foo}%bar} */
} paramexptype_T;
/* type COLON MATCHH MATCHT MATCHL SUBSTA
* ${n-s} MINUS no
* ${n+s} PLUS no
* ${n=s} ASSIGN no
* ${n?s} ERROR no
* ${n:-s} MINUS yes
* ${n:+s} PLUS yes
* ${n:=s} ASSIGN yes
* ${n:?s} ERROR yes
* ${n#m} MATCH no yes no no no
* ${n##m} MATCH no yes no yes no
* ${n%m} MATCH no no yes no no
* ${n%%m} MATCH no no yes yes no
* ${n/m/s} SUBST no no no yes no
* ${n/#m/s} SUBST no yes no yes no
* ${n/%m/s} SUBST no no yes yes no
* ${n//m/s} SUBST no no no yes yes
* ${n:/m/s} SUBST yes yes yes
*
* PT_SUBST and PT_NEST is beyond POSIX. */
/* parameter expansion */
typedef struct paramexp_T {
paramexptype_T pe_type;
union {
wchar_t *name;
struct wordunit_T *nest;
} pe_value;
struct wordunit_T *pe_start, *pe_end;
struct wordunit_T *pe_match, *pe_subst;
} paramexp_T;
#define pe_name pe_value.name
#define pe_nest pe_value.nest
/* pe_name: name of parameter
* pe_nest: nested parameter expansion
* pe_start: index of the first element in the range
* pe_end: index of the last element in the range
* pe_match: word to be matched with the value of the parameter
* pe_subst: word to to substitute the matched string with
* `pe_start' and `pe_end' is NULL if the indices are not specified.
* `pe_match' and `pe_subst' may be NULL to denote an empty string. */
/* type of assignment */
typedef enum {
A_SCALAR, A_ARRAY,
} assigntype_T;
/* assignment */
typedef struct assign_T {
struct assign_T *next;
assigntype_T a_type;
wchar_t *a_name;
union {
struct wordunit_T *scalar;
void **array;
} a_value; /* value to assign */
} assign_T;
#define a_scalar a_value.scalar
#define a_array a_value.array
/* `a_scalar' may be NULL to denote an empty string.
* `a_array' is an array of pointers to `wordunit_T'. */
/* type of redirection */
typedef enum {
RT_INPUT, /* <file */
RT_OUTPUT, /* >file */
RT_CLOBBER, /* >|file */
RT_APPEND, /* >>file */
RT_INOUT, /* <>file */
RT_DUPIN, /* <&fd */
RT_DUPOUT, /* >&fd */
RT_PIPE, /* >>|fd */
RT_HERE, /* <<END */
RT_HERERT, /* <<-END */
RT_HERESTR, /* <<<str */
RT_PROCIN, /* <(command) */
RT_PROCOUT, /* >(command) */
} redirtype_T;
/* redirection */
typedef struct redir_T {
struct redir_T *next;
redirtype_T rd_type;
int rd_fd; /* file descriptor to redirect */
union {
struct wordunit_T *filename;
struct {
wchar_t *hereend; /* token indicating end of here-document */
struct wordunit_T *herecontent; /* contents of here-document */
} heredoc;
struct embedcmd_T command;
} rd_value;
} redir_T;
#define rd_filename rd_value.filename
#define rd_hereend rd_value.heredoc.hereend
#define rd_herecontent rd_value.heredoc.herecontent
#define rd_command rd_value.command
/* For example, for "2>&1", `rd_type' = RT_DUPOUT, `rd_fd' = 2 and
* `rd_filename' = "1".
* For RT_HERERT, all the lines in `rd_herecontent' have the leading tabs
* already removed. If `rd_hereend' is quoted, `rd_herecontent' is a single
* word unit of type WT_STRING, since no parameter expansions are performed.
* Anyway `rd_herecontent' is expanded by calling `expand_string' with `esc'
* argument being true. */
/********** Interface to Parsing Routines **********/
/* Holds parameters that affect the behavior of parsing. */
typedef struct parseparam_T {
_Bool print_errmsg; /* print error messages? */
_Bool enable_verbose; /* echo input if `shopt_verbose' is true? */
_Bool enable_alias; /* perform alias substitution? */
const char *filename; /* the input filename, which may be NULL */
unsigned long lineno; /* line number, which should be initialized to 1 */
inputfunc_T *input; /* input function */
void *inputinfo; /* pointer passed to the input function */
_Bool interactive; /* input is interactive? */
_Bool is_dot_builtin; /* input is for the dot built-in? */
inputresult_T lastinputresult; /* last return value of input function */
} parseparam_T;
/* If `interactive' is true, `input' is `input_interactive' and `inputinfo' is a
* pointer to a `struct input_interactive_info_T' object.
* Note that input may not be from a terminal even if `interactive' is true. */
typedef enum parseresult_T {
PR_OK, PR_EOF, PR_SYNTAX_ERROR, PR_INPUT_ERROR,
} parseresult_T;
extern parseresult_T read_and_parse(
parseparam_T *info, and_or_T **restrict resultp)
__attribute__((nonnull,warn_unused_result));
extern _Bool parse_string(parseparam_T *info, wordunit_T **restrict resultp)
__attribute__((nonnull,warn_unused_result));
/********** Auxiliary Functions **********/
extern _Bool is_portable_name_char(wchar_t c)
__attribute__((const));
extern _Bool is_name_char(wchar_t c)
__attribute__((pure));
extern _Bool is_name(const wchar_t *s)
__attribute__((nonnull,pure));
extern _Bool is_assignment_prefix(const wchar_t *s)
__attribute__((nonnull,pure));
extern _Bool is_keyword(const wchar_t *s)
__attribute__((nonnull,pure));
extern _Bool is_token_delimiter_char(wchar_t c)
__attribute__((pure));
/********** Functions That Convert Parse Trees into Strings **********/
extern wchar_t *pipelines_to_wcs(const pipeline_T *pipelines)
__attribute__((malloc,warn_unused_result));
extern wchar_t *command_to_wcs(const command_T *command, _Bool multiline)
__attribute__((malloc,warn_unused_result));
/********** Functions That Free/Duplicate Parse Trees **********/
extern void andorsfree(and_or_T *a);
static inline command_T *comsdup(command_T *c);
extern void comsfree(command_T *c);
extern void wordfree(wordunit_T *w);
extern void paramfree(paramexp_T *p);
/* Duplicates the specified command (virtually). */
command_T *comsdup(command_T *c)
{
refcount_increment(&c->refcount);
return c;
}
#endif /* YASH_PARSER_H */
/* vim: set ts=8 sts=4 sw=4 et tw=80: */
|