# Eggshell parser # # Copyright (C) 2016 Patrick "P. J." McDermott # # This file is part of the Eggshell Compiler. # # The Eggshell Compiler 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 3 of # the License, or (at your option) any later version. # # The Eggshell Compiler 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 the Eggshell Compiler. If not, see # . ptrace=false # # Function tracing # ptrace_begn() { local fn="${1}" shift 1 if ${ptrace}; then printf 'TRACE: BEGN %s()\n' "${fn}" >&2 fi } ptrace_pass() { local fn="${1}" shift 1 if ${ptrace}; then printf 'TRACE: PASS %s()\n' "${fn}" >&2 fi } ptrace_fail() { local fn="${1}" shift 1 if ${ptrace}; then printf 'TRACE: FAIL %s()\n' "${fn}" >&2 fi } # # Parser # complete_command() { linebreak if list; then separator return 0 fi return 1 } list() { ptrace_begn list if and_or; then while separator && and_or; do : done ptrace_pass list return 0 fi ptrace_fail list return 1 } and_or() { ptrace_begn and_or if pipeline; then while accept T_AND_IF || accept T_OR_IF; do if ! linebreak || ! pipeline; then ptrace_fail and_or return 1 fi done ptrace_pass and_or return 0 fi ptrace_fail and_or return 1 } pipeline() { ptrace_begn pipeline accept T_BANG if pipe_sequence; then ptrace_pass pipeline return 0 fi ptrace_fail pipeline return 1 } pipe_sequence() { ptrace_begn pipe_sequence if command; then while accept T_PIPE; do if ! linebreak || ! command; then ptrace_fail pipe_sequence return 1 fi done ptrace_pass pipe_sequence return 0 fi ptrace_fail pipe_sequence return 1 } command() { ptrace_begn command if simple_command; then ptrace_pass command return 0 elif compound_command; then redirect_list ptrace_pass command return 0 fi ptrace_fail command return 1 } compound_command() { ptrace_begn compound_command if brace_group; then ptrace_pass compound_command return 0 elif subshell; then ptrace_pass compound_command return 0 elif for_clause; then ptrace_pass compound_command return 0 elif case_clause; then ptrace_pass compound_command return 0 elif if_clause; then ptrace_pass compound_command return 0 elif while_clause; then ptrace_pass compound_command return 0 elif until_clause; then ptrace_pass compound_command return 0 fi ptrace_fail compound_command return 1 } subshell() { ptrace_begn subshell if accept T_LPAREN && compound_list && expect T_RPAREN; then ptrace_pass subshell return 0 fi ptrace_fail subshell return 1 } compound_list() { ptrace_begn compound_list newline_list if term; then separator ptrace_pass compound_list return 0 fi ptrace_fail compound_list return 1 } term() { ptrace_begn term if and_or; then while separator; do and_or done ptrace_pass term return 0 fi ptrace_fail term return 1 } for_clause() { ptrace_begn for_clause if accept T_FOR; then if expect T_NAME && linebreak; then if accept T_IN; then wordlist if ! sequential_sep; then ptrace_fail for_clause return 1 fi fi if do_group; then ptrace_pass for_clause return 0 fi fi fi ptrace_fail for_clause return 1 } wordlist() { ptrace_begn wordlist if accept T_WORD; then while accept T_WORD; do :; done ptrace_pass wordlist return 0 fi ptrace_fail wordlist return 1 } case_clause() { if accept T_CASE; then if expect T_WORD && linebreak && expect T_IN && linebreak; then case_list || case_list_ns expect T_ESAC return 0 fi fi return 1 } case_list_ns() { if case_list && case_item_ns; then return 0 elif case_item_ns; then return 0 fi return 1 } case_list() { if case_item; then while case_item; do : done return 0 fi return 1 } case_item_ns() { accept T_LPAREN if pattern && expect RPAREN; then compound_list if linebreak; then return 0 fi fi return 1 } case_item() { accept T_LPAREN if pattern && expect T_RPAREN; then if compound_list || linebreak; then if expect T_DSEMI && linebreak; then return 0 fi fi fi return 1 } pattern() { if accept T_CMDNAME; then while accept T_PIPE; do expect T_WORD done return 0 fi return 1 } if_clause() { if accept T_IF; then if compound_list && expect T_THEN && compound_list; then else_part expect T_FI return 0 fi fi return 1 } else_part() { while accept T_ELIF; do if compound_list && expect T_THEN && compound_list; then continue fi return 1 done if accept T_ELSE; then if compound_list; then return 0 fi fi return 1 } while_clause() { if accept T_WHILE; then if compound_list && do_group; then return 0 fi fi return 1 } until_clause() { if accept T_UNTIL; then if compound_list && do_group; then return 0 fi fi return 1 } function_body() { ptrace_begn function_body inject T_LBRACE inject T_FN_START if compound_command; then inject T_SEMI inject T_FN_END inject T_RBRACE ptrace_pass function_body return 0 fi ptrace_fail function_body return 1 } brace_group() { ptrace_begn brace_group if accept T_LBRACE && compound_list && expect T_RBRACE; then ptrace_pass brace_group return 0 fi ptrace_fail brace_group return 1 } do_group() { ptrace_begn do_group if accept T_DO && compound_list && expect T_DONE; then ptrace_pass do_group return 0 fi ptrace_fail do_group return 1 } simple_command() { ptrace_begn simple_command if accept T_LOCAL || accept T_RETURN; then cmd_suffix ptrace_pass simple_command return 0 fi accept T_STATIC if cmd_prefix; then if cmd_word; then cmd_suffix fi ptrace_pass simple_command return 0 elif accept T_FNAME; then if accept T_LPAREN; then expect T_RPAREN if linebreak && function_body; then ptrace_pass simple_command return 0 fi else cmd_suffix ptrace_pass simple_command return 0 fi elif cmd_name; then cmd_suffix ptrace_pass simple_command return 0 fi ptrace_fail simple_command return 1 } cmd_name() { ptrace_begn cmd_name # TODO: Assignment if accept T_CMDNAME; then ptrace_pass cmd_name return 0 fi ptrace_fail cmd_name return 1 } cmd_word() { ptrace_begn cmd_word # TODO: Assignment if accept T_WORD; then ptrace_pass cmd_word return 0 fi ptrace_fail cmd_word return 1 } cmd_prefix() { ptrace_begn cmd_prefix if io_redirect || accept T_ASSIGNMENT_WORD; then while io_redirect || accept T_ASSIGNMENT_WORD; do : done ptrace_pass cmd_prefix return 0 fi ptrace_fail cmd_prefix return 1 } cmd_suffix() { ptrace_begn cmd_suffix if io_redirect || accept T_WORD; then while io_redirect || accept T_WORD; do : done ptrace_pass cmd_suffix return 0 fi ptrace_fail cmd_suffix return 1 } redirect_list() { ptrace_begn redirect_list if io_redirect; then while io_redirect; do : done ptrace_pass redirect_list return 0 fi ptrace_fail redirect_list return 1 } io_redirect() { ptrace_begn io_redirect accept T_IO_NUMBER if io_file || io_here; then ptrace_pass io_redirect return 0 fi ptrace_fail io_redirect return 1 } io_file() { if accept T_LESS || accept T_LESSAND || accept T_GREAT || \ accept T_GREATAND || accept T_DGREAT || \ accept T_LESSGREAT || accept T_CLOBBER; then if filename; then return 0 fi fi return 1 } filename() { if accept T_WORD; then return 0 fi return 1 } io_here() { if accept T_DLESS || accept T_DLESSDASH; then if here_end; then return 0 fi fi return 1 } here_end() { if accept T_WORD; then return 0 fi 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() { ptrace_begn sequential_sep if accept T_SEMI; then if linebreak; then ptrace_pass sequential_sep return 0 fi elif newline_list; then ptrace_pass sequential_sep return 0 fi ptrace_fail sequential_sep return 1 }