# Shell command language code generator # # 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 # . sbufi= sbufc= sc= tu_id= static_fn_n= pragma_nostack=false sgetc() { if [ ${sbufi} -ge ${sbufc} ]; then sc='' else eval "sc=\${sbufv_${sbufi}}" sbufi=$((${sbufi} + 1)) fi } add_static_fn() { local fn="${1}" printf '__%s_static_fns="${__%s_static_fns} %s:__%s_fn%d"\n' \ "${tu_id}" "${tu_id}" "${fn}" "${tu_id}" ${static_fn_n} printf '__%s_fn%d' "${tu_id}" ${static_fn_n} static_fn_n=$((${static_fn_n} + 1)) } codegen_sub() { local array="${1}" shift 1 local print_spc= local ionum= local pragma= local fname= local static= local static_fname= local type= local params= local types= local t= print_spc=false ionum=false pragma=false fname='' static=false static_fname=false type=false params=0 types='' IFS="${RS}" for t in ${array}; do if ${print_spc}; then case "${t%${US}*}" in T_NEWLINE) ;; *) printf ' ' ;; esac print_spc=false fi # Handle I/O number false positives. if ${ionum}; then case "${t%${US}*}" in T_WORD|T_CMDNAME|T_FNAME) printf ' ' ;; esac ionum=false fi case "${t%${US}*}" in T_USE) pragma=true continue ;; T_FNAME) fname="${t}" params=0 types='' ;; T_STATIC) static=true continue ;; T_TYPE) type=true params=$((${params} + 1)) types="${types}${t#*${US}}:" continue ;; T_VOID) params=-1 continue ;; T_COMMA) continue ;; esac if ${pragma}; then pragma=false eval "pragma_${t#*${US}}=true" continue fi # State machine for static variables and functions if ${static}; then static=false case "${t%${US}*}" in T_FNAME) static_fname=true continue ;; *) printf '__static ' ;; esac elif ${static_fname}; then static_fname=false case "${t%${US}*}" in T_LPAREN) add_static_fn "${fname#*${US}}" ;; *) printf '__static ' toktext "${fname}" ;; esac elif ${type}; then type=false types="${types}${t#*${US}} " continue fi # Function start/end tokens case "${t%${US}*}" in T_FN_START) if ! ${pragma_nostack}; then printf '__fn_start %s %s;' \ "${tu_id}" "${fname#*${US}}" else printf ':;' fi if [ ${params} -gt 0 ]; then printf ' __check_args %s %d %s %s;' \ "${fname#*${US}}" ${params} \ "'${types% }'" '"${@}"' elif [ ${params} -eq -1 ]; then printf ' __check_args %s 0 "" "${@}";' \ "${fname#*${US}}" fi ;; T_FN_END) if ! ${pragma_nostack}; then printf '__fn_end;' else printf ':;' fi ;; T_RETURN) if ! ${pragma_nostack}; then printf '__fn_end; ' fi ;; T_STATIC) printf '__static ' continue ;; T_LOCAL) printf '__local ' continue ;; esac toktext "${t}" case "${t%${US}*}" in T_NEWLINE|T_LPAREN) # Indenting lines can mess up here-documents. # Adding a space after "(" can mess up function # definitions (on zsh at least, while other # shells accept the space). ;; T_IO_NUMBER) ionum=true ;; *) print_spc=true ;; esac done unset IFS } # The token stack is encoded in a string in the following grammar: # Terminal symbols: # TOKEN # Production rules: # stack = tokens [ '' type '' stack '' [ tokens ] ] ; # tokens = TOKEN { '' TOKEN } ; # type = 'C' ; # We need to recurse through this stack to get to all the tokens. # Each element in the stack (an array of tokens) gets run through the codegen to # become text that is inserted into the array below. parse_stack() { local array= local res= array='' while :; do sgetc case "${sc}" in '') # EOF break ;; "${SOH}") # New stack element sgetc case "${sc}" in 'C') # Command substitution sgetc # STX res="$(parse_stack)" sbufi="${res%% *}" array="${array}${res#* }" ;; esac ;; "${ETX}") # End of stack element break ;; *) # Token character array="${array}${sc}" ;; esac done printf '%d ' ${sbufi} codegen_sub "${array}" } sh_parse_stack() { local buf="${1}" shift 1 local res= eval "$(printf '%s' "${buf}" | awk -v FS='' -v j=0 \ -v squote="'" -v esc_squote="'\\\\''" ' { for (i = 1; i <= NF; ++i) { sub(squote, esc_squote, $i); printf("sbufv_%d='\''%s'\''\n", j++, $i); }; printf("sbufv_%d='\''\n'\''\n", j++); } ')" sbufi=0 sbufc=${#buf} res="$(parse_stack; printf '.')" res="${res%.}" printf '%s' "${res#* }" } sh_set_tu_id() { local toks="${1}" tu_id="$(printf '%s' "${toks}" | sha256sum -)" tu_id="${tu_id% -}" return 0 } sh_start_tu() { printf '__tu=%s\n' "${tu_id}" printf '__%s_static_vars=\n' "${tu_id}" printf '__%s_static_var_vals=\n' "${tu_id}" printf '__%s_static_fns=\n' "${tu_id}" static_fn_n=0 } sh_end_tu() { printf '__tu_end\n' }