_zero_arg_funs:
    GETCH
|
    GETPID
|
    GETS
;

_one_arg_funs:
    ASCII
|
    EVAL
|
    EXISTS
|
    LISTLEN
|
    ECHO_TOKEN
|
    CMD_TAIL
|
    CMD_HEAD
|
    ARG_HEAD
|
    ARG_TAIL
|
    G_BASE
|
    G_PATH
|
    G_EXT
|
    G_DEXT
|
    PUTENV
|
    GETENV
|
    STRLEN
|
    STRUPR
|
    STRLWR
|
    TRIM
|
    TRIMLEFT
|
    TRIMRIGHT
;

_two_arg_funs:
    C_EXT                           /* string, string */
|
    C_BASE
|
    C_PATH
|
    ELEMENT                         /* int, list | int, string */
|
    FGETS                           /* list fgets(string, int) */
|
    FIELDS                          /* string, string */
|
    LISTFIND                        /* list, string */
|
    LISTUNION                       /* list, list | list, string */
|
    STRCHR                          /* string, string */
|
    STRFIND                         /* string, string */
|
    RESIZE                          /* string, int */
;

_optint_string:
    STAT
|
    CHDIR
|
    SYSTEM
;

_comma_expr:
    ',' errExpression
    {
        $$ = SVmove(&$2);
    }
|
    zeroSemVal
;

_optint_special:                        /* optional extra int arg is OK */
    EXEC                                /* indicating P_CHECK (0) or    */
|                                       /* P_NOCHECK (2)                */
    EXECUTE
;

_comma_arglist:
    ','
    args                                /* argument list, see   */
    {                                   /* README.args          */
        $$ = SVmove(&$2);         
    }
|
    zeroSemVal                          /* 0 arguments          */
;

_opt_arglist:
    args
|
    zeroSemVal
;

_funname:
    IDENTIFIER
    {
        $$ = SVint(p_functionIdx());
    }
;

function:
    _zero_arg_funs                       /* getch() or gets() */
    openParen
    {
        $$ = pmv_zeroArgs(svType(&$1));
    }
|
    _one_arg_funs openParen errExpression
    {
        $$ = pmv_oneArg(svType(&$1), &$3);
    }
|
    _two_arg_funs openParen errExpression comma errExpression
    {
        $$ = pmv_twoArgs(svType(&$1), &$3, &$5);
    }
|
    SUBSTR                              /* three arg function */
    openParen errExpression comma errExpression comma errExpression
    {
        $$ = pmv_threeArgs($1.type, &$3, &$5, &$7);
    }
|
    _optint_string                      /* CHDIR, SYSTEM, STAT */
    openParen
    errExpression                      /* int inserted if string */
    _comma_expr                         /* may be string if first == int */
    {
        $$ = pmv_optIntString(svType(&$1), &$3, &$4);
    }
|
    _optint_special                     /* $1 EXEC, EXECUTE            */
    openParen                           /*      alternatives:          */
    errExpression                       /* $3   fun(int, string, ...)  */
    _comma_arglist                      /* $4   or fun(string, ...)    */
    {
        $$ = pmv_checkSpecial(svType(&$1), &$3, &$4);
    }
|
    PRINTF openParen args                 /* first may be anything */
    {
        $$ = pmv_specials(f_printf, &$3);
    }
|
    FPRINTF openParen args                /* argcount >= 2 required */
    {
        $$ = pmv_fprintf(svType(&$1), &$3);
    }
|
    STRFORMAT openParen args        /* first may be anything */
    {
        $$ = pmv_specials(f_strformat, &$3);
    }
|
    _funname openParen _opt_arglist
    {
        $$ = pmv_callFunction(svValue(&$1), &$3);
    }
|
    makeList
;
