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 return 1 } list() { if and_or; then while separator_op; do if ! and_or; then return 1 fi done return 0 fi return 1 } 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() { accept T_BANG if pipe_sequence; then return 0 fi return 1 } pipe_sequence() { if command; then while accept T_PIPE; do if ! linebreak || ! command; then return 1 fi done return 0 fi return 1 } command() { # XXX: Unfinished accept T_WORD } 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 } parse() { local fn="${1}" shift 1 init_lexer "${fn}" # If this returns (does not exit), there are no errors. while complete_command; do :; 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`'