HT="$(printf '\t.')"; HT="${HT%.}" LF="$(printf '\n.')"; LF="${LF%.}" RS="$(printf '\036.')"; RS="${RS%.}" US="$(printf '\037.')"; US="${US%.}" dbg() { dbg=true #dbg=false if ${dbg}; then printf 'DEBUG: %s\n' "${@}" >&2 fi } . ./tokens.sh . ./lexer.sh complete_command() { if list; then separator return 0 fi # Unexpected EOF synexp '' } list() { dbg 'list()' if and_or; then while separator_op; do if ! and_or; then return 1 fi done return 0 fi return 1 } and_or() { dbg 'and_or()' if pipeline; then while accept T_AND_IF || accept T_OR_IF; do if ! linebreak || ! pipeline; then return 1 fi done return 0 fi return 1 } pipeline() { dbg 'pipeline()' accept T_BANG if pipe_sequence; then return 0 fi return 1 } pipe_sequence() { dbg 'pipe_sequence()' if command; then while accept T_PIPE; do if ! linebreak || ! command; then return 1 fi done return 0 fi return 1 } command() { dbg 'command()' if compound_command; then redirect_list return 0 elif function_defn; then return 0 elif simple_command; then return 0 fi return 1 } compound_command() { dbg 'compound_command()' if brace_group; then return 0 elif subshell; then return 0 elif for_clause; then return 0 elif case_clause; then return 0 elif if_clause; then return 0 elif while_clause; then return 0 elif until_clause; then return 0 fi return 1 } subshell() { dbg 'subshell()' if accept T_LPAREN && compound_list && expect T_RPAREN; then return 0 fi return 1 } compound_list() { dbg 'compound_list()' newline_list if term; then dbg FOUND TERM separator return 0 fi return 1 } term() { dbg 'term()' if and_or; then while separator; do and_or done return 0 fi return 1 } for_clause() { dbg 'for_clause()' if accept T_FOR; then if expect T_NAME && linebreak; then if accept T_IN; then wordlist if ! sequential_sep; then return 1 fi fi if do_group; then return 0 fi fi fi return 1 } wordlist() { dbg 'wordlist()' if accept T_WORD; then while accept T_WORD; do :; done return 0 fi return 1 } case_clause() { : TODO: Implement return 1 } case_list_ns() { : TODO: Implement return 1 } case_list() { : TODO: Implement return 1 } case_item_ns() { : TODO: Implement return 1 } case_item() { : TODO: Implement return 1 } pattern() { : TODO: Implement return 1 } if_clause() { : TODO: Implement return 1 } else_part() { : TODO: Implement return 1 } while_clause() { : TODO: Implement return 1 } until_clause() { : TODO: Implement return 1 } function_defn() { : TODO: Implement return 1 } function_body() { : TODO: Implement return 1 } brace_group() { dbg 'brace_group()' if accept T_LBRACE && compound_list && expect T_RBRACE; then return 0 fi return 1 } do_group() { dbg 'do_group()' if accept T_DO && compound_list && expect T_DONE; then return 0 fi return 1 } simple_command() { dbg 'simple_command()' if cmd_prefix; then if cmd_word; then cmd_suffix fi return 0 elif cmd_name; then cmd_suffix return 0 fi return 1 } cmd_name() { dbg 'cmd_name()' # TODO: Assignment if accept T_CMDNAME; then return 0 fi return 1 } cmd_word() { dbg 'cmd_word()' # TODO: Assignment if accept T_WORD; then return 0 fi return 1 } cmd_prefix() { : TODO: Implement return 1 } cmd_suffix() { : TODO: Implement return 1 } redirect_list() { : TODO: Implement return 1 } io_redirect() { : TODO: Implement return 1 } io_file() { : TODO: Implement return 1 } filename() { : TODO: Implement return 1 } io_here() { : TODO: Implement return 1 } here_end() { : TODO: Implement return 1 } newline_list() { if accept T_NEWLINE; then while accept T_NEWLINE; do : done return 0 fi return 1 } linebreak() { newline_list return 0 } separator_op() { if accept T_AND || accept T_SEMI; then return 0 fi return 1 } separator() { if separator_op && linebreak; then return 0 elif newline_list; then return 0 fi return 1 } sequential_sep() { dbg 'sequential_sep()' if accept T_SEMI; then if linebreak; then return 0 fi elif newline_list; then return 0 fi return 1 } parse() { local fn="${1}" shift 1 init_lexer "${fn}" # If this returns (does not exit), there are no errors. while ! accept T_EOF; do complete_command done get_tokens return 0 } try() { local tokens= local t= printf 'Trying script:\n' printf '\t%s\n' "${@}" if tokens="$(printf '%s\n' "${@}" | parse -)"; then IFS="${RS}" for t in ${tokens}; do printf 'Token: %s\n' "$(tokname "${t}")" case "${t%${US}*}" in T_WORD) printf ' "%s"\n' "${t#T_WORD${US}}" ;; esac done unset IFS else printf 'FAIL\n' fi printf '\n\n' } #try '"foo bar" && $baz || qux' '${quux%uux quuux' #try '"foo bar" && $baz || qux' '${quux%uux } quuux' #try 'foo ${bar}' #try 'foo ${#bar}' #try 'foo ${bar#baz}' #try 'foo ${#bar#}' #try 'foo ${^}' #try 'foo `bar`' try 'foo &&' #try '{ foo; }' #try '( foo )' #try 'for i in 1 2 3; do stuff; done'