/* Yash: yet another shell */
/* parser.h: syntax parser */
/* (C) 2007-2011 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"


/********** 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 */
    CT_FUNCDEF,    /* function definition */
} commandtype_T;

/* command in a pipeline */
typedef struct command_T {
    struct command_T *next;
    unsigned          refcount;   /* reference count */
    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 */
	} 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 {
	    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_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_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. */

/* 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 */
} caseitem_T;
/* `ci_patterns' is a NULL-terminated array of pointers to `wordunit_T' that are
 * cast to `void *'. */

/* 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? */
#if YASH_ENABLE_ALIAS
    _Bool enable_alias;   /* perform alias substitution? */
#endif
    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? */
    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__((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)
{
    c->refcount++;
    return c;
}


#endif /* YASH_PARSER_H */


/* vim: set ts=8 sts=4 sw=4 noet tw=80: */
