# 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_USE; then expect T_NAME #ptrace_pass simple_command return 0 fi 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 if accept T_TYPE; then expect T_NAME while accept T_COMMA; do expect T_TYPE expect T_NAME done fi 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 }