From a90e94c845cded2aeda7b70ea3ec5d6b6085f44a Mon Sep 17 00:00:00 2001 From: Patrick Spek Date: Mon, 15 May 2023 08:18:12 +0200 Subject: Remove bpt from the codebase again The power of the templating tool were not as big as I had hoped. The inclusion of another license also doesn't really seem like it is worth the trouble considering the tool didn't really get me the high level of templating I had desired. The quest for templating continues... --- CHANGELOG.md | 6 - LICENSES/MIT.txt | 22 - lib/util.bash | 51 +-- lib/vendor/bpt.bash | 1244 --------------------------------------------------- 4 files changed, 6 insertions(+), 1317 deletions(-) delete mode 100644 LICENSES/MIT.txt delete mode 100644 lib/vendor/bpt.bash diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e752aa..976c578 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,12 +69,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The `sync` subcomman will now `stash` any changes before it attempts to `pull`. Afterwards, `stash pop` will be ran to apply the last `stash`ed changes again. -- The `file_template` function has been altered to be able to use various - templating engines. Currently it supports `satpl` (which stands for sed-awk - template, the "original" templating mechanism used in Bashtard) and `bpt` - (which stands for [Bash Pure Templates](https://github.com/husixu1/bpt). The - engine used is defined through the file's extension. If no extension is set, - it will default to `satpl`. ## [1.0.0] - 2022-05-06 diff --git a/LICENSES/MIT.txt b/LICENSES/MIT.txt deleted file mode 100644 index da4c7e6..0000000 --- a/LICENSES/MIT.txt +++ /dev/null @@ -1,22 +0,0 @@ - - The MIT License (MIT) - Copyright (c) 2023 Hu Sixu - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE - OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/lib/util.bash b/lib/util.bash index bae620b..c2429a1 100644 --- a/lib/util.bash +++ b/lib/util.bash @@ -10,8 +10,6 @@ . "$BASHTARD_LIBDIR/util/pkg.bash" # shellcheck source=lib/util/svc.bash . "$BASHTARD_LIBDIR/util/svc.bash" -# shellcheck source=lib/vendor/bpt.bash -. "$BASHTARD_LIBDIR/vendor/bpt.bash" # Change the working directory. In usage, this is the same as using cd, # however, it will make additional checks to ensure everything is going fine. @@ -144,44 +142,17 @@ file_hash_md5() { file_template() { local file - local system - - file="$1" ; shift - - ext="${file##*.}" - path="$(playbook_path "base")/share/$file" - system="bashtard/file_template" - - if [[ -z "$ext" || "$ext" == "$file" ]] - then - debug "$system" "Missing extension for template $file, assuming satpl" - ext="satpl" - fi + local sedfile - if [[ ! -f "$path" ]] - then - crit "$system" "No template found at $path" - return 1 - fi + file="$(playbook_path "base")/share/$1" ; shift + sedfile="$(tmpfile)" - if [[ "$(type -t "file_template_$ext")" != "function" ]] + if [[ ! -f $file ]] then - crit "$system" "Invalid extension for template $file ($ext)" - return 1 + crit "bashtard/template" "Tried to render template from $file, but it doesn't exist" + return fi - "file_template_$ext" "$path" "$@" -} - -file_template_satpl() -{ - local file - local sedfile - - file="$1" ; shift - - sedfile="$(tmpfile)" - for kv in "$@" do debug "bashtard/template" "Adding $kv to sedfile at $sedfile" @@ -207,16 +178,6 @@ file_template_satpl() sed -f "$sedfile" "$file" } -file_template_bpt() -{ - local file - - file="$1" ; shift - - eval "$* bpt.main ge \"$file\"" - printf "\n" -} - # Check if the first argument given appears in the list of all following # arguments. in_args() { diff --git a/lib/vendor/bpt.bash b/lib/vendor/bpt.bash deleted file mode 100644 index cda1030..0000000 --- a/lib/vendor/bpt.bash +++ /dev/null @@ -1,1244 +0,0 @@ -#!/usr/bin/env bash - -# SPDX-FileCopyrightText: 2023 Sixu Hu -# SPDX-FileCopyrightText: 2023 Patrick Spek -# -# SPDX-License-Identifier: MIT - -# shellcheck disable=SC2317 - -# Import once -if [[ -n $__BPT_VERSION ]]; then return; fi -readonly __BPT_VERSION="v0.1" - -# Add multiple traps for EXIT -# See https://stackoverflow.com/questions/3338030 -bpt.__add_exit_trap() { - trap_add_cmd=${1:?${FUNCNAME[0]} usage error} - trap -- "$( - extract_trap_cmd() { printf '%s\n' "$3"; } - eval "extract_trap_cmd $(trap -p EXIT)" - printf '%s\n' "${trap_add_cmd}" - )" EXIT || echo "Unable to add to trap" >&2 -} - -# The shift-reduce LR(1) parser. -# $1: Parse table name. -# The parse table should be an associative array where the key is -# , and the value actions (s /r /a/). -# $2: Reduce function hook -# This function can access `rule=(LHS RHS1 RHS2 ...)`. -# This function can access the RHS(i+1)'s content': `${contents[$((s + i))]}`. -# This function should store the reduce result to `contents[$s]`. -# $3: Error handler function hook -# Args passed to this function: -# $1: Line -# $2: Column -# $3: Default error message -# $4: (optional) If set, enable debug. -# shellcheck disable=SC2030 # Modification to `contents` and `rule` are local. -bpt.__lr_parse() ( - local -rn table="$1" - local -r reduce_fn="${2:-echo}" - local -r error_fn="${3:-__error}" - if [[ -n $4 ]]; then local -r NDEBUG=false; else local -r NDEBUG=true; fi - - # 20 should be enough ... - # I assume no one's writing a BNF with more than 20 RHSs ... - local -a STATE_PTRN=('') - for i in {1..20}; do STATE_PTRN[i]="${STATE_PTRN[i - 1]}:*"; done - - # Parse stack - # Using string manipulation for states is faster than using an array. - local states=':0' stack_size=1 - # Contents stack associatied wit the parse stack - # Large dict indexing is significantly faster than a regular one. - # Thus we use stack_size + associative array to emulate a regular array. - local -A contents=([0]='') - - # Current reduction rule - local -a rule=() - # Current look-ahead token and its content - local token='' content='' action='' - # Location tracking variables - local num_lines=0 num_bytes=0 - # Temporary variables - local i=0 str_lines=0 buffer='' - - # $1: Goto state after shift - __shift() { - states+=":$1" - contents["$stack_size"]="$content" - ((++stack_size)) - token='' content='' - } - - # $1: Rule - __reduce() { - # Although not robust, word splitting is faster than `read` - # shellcheck disable=SC2206 - local num_rhs=$((${#rule[@]} - 1)) - - # Reduce and goto state - # shellcheck disable=SC2295 - states="${states%${STATE_PTRN[$num_rhs]}}" - states+=":${table["${states##*:},${rule[0]}"]}" - - # Reduction start location (on the contents stack) - local s=$((stack_size - num_rhs)) - - # Run reduce hook (which saves the reduce result to `contents[$s]`) - $reduce_fn || exit 1 - stack_size=$((s + 1)) - } - - # Simply print the result - __accept() { - printf '%s' "${contents[1]}" - } - - # Default error handler - __error() { - echo "Error: Line $(($1 + 1)) Column $(($2 + 1))" - echo "$3" - } >&2 - - # Debugging support - $NDEBUG || { - eval __orig"$(declare -f __shift)" - eval __orig"$(declare -f __reduce)" - eval __orig"$(declare -f __accept)" - __shift() { - echo "[DBG] ${states##*:} Shift $1 \`$content\`" >&2 - __orig__shift "$@" - } - __reduce() { - echo "[DBG] ${states##*:} Reduce ${rule[*]}" >&2 - __orig__reduce - } - __accept() { - $NDEBUG || echo "[DBG] Result accepted" >&2 - __orig__accept - } - } - - while true; do - [[ -n $token ]] || { - read -r token str_lines num_lines num_bytes - IFS= read -r buffer || return 1 - content="$buffer" - for ((i = 1; i < str_lines; ++i)); do - IFS= read -r buffer || return 1 - content+=$'\n'"$buffer" - done - } - - action="${table["${states##*:},$token"]}" - case "$action" in - # Shift - s*) __shift "${action#s }" ;; - # Reduce - r*) # shellcheck disable=SC2206 - rule=(${action#r }) - __reduce - ;; - # Accept - a) __accept && break ;; - # Error - '') - local expects='' rule_key='' - for rule_key in "${!table[@]}"; do - [[ $rule_key != "${states##*:},"* || - -z "${table["$rule_key"]}" || - "${table["$rule_key"]}" =~ ^[[:digit:]]+$ ]] || - expects+="${expects:+,}${BPT_PP_TOKEN_TABLE["${rule_key##*,}"]:-${rule_key##*,}}" - done - $error_fn "$num_lines" "$num_bytes" \ - "Expects one of \`${expects[*]}\` but got \`${token}\` ($content)." - $NDEBUG || echo "[DBG] PARSER STATES ${states} TOKEN ${token} CONTENT ${content}." >&2 - exit 1 - ;; - *) # Parse table error (internal error) - echo "Internal error: STATES ${states} TOKEN ${token} CONTENT ${content}. " >&2 - echo "Internal error: action '$action' not recognized." >&2 - exit 1 - ;; - esac - done -) - -# The tokenizer for bpt -# $1: Left deilmiter -# $2: Right delimiter -# $3: Error handler function hook -# Args passed to this function: -# $1: Line -# $2: Column -# $3: Default error message -# -# Terminal token name to content mappings: -# str: Anything outside the toplevel `ld ... rd` or -# Anything inside `"..."` or `'...'` within any `ld ... rd` -# Note1: `"` inside `"..."` needs to be escaped using `\"`, -# and the same for `'` inside `'...'`. -# -# ld: ${ldelim} rd: ${rdelim} lp: ( rp: ) -# cl: : ex: ! eq: -eq ne: -ne -# lt: -lt gt: -gt le: -le ge: -ge -# streq: == strne: != strlt: < strgt: > -# and|or|if|elif|else|for|in|include: -# id: [[:alpha:]_][[:alnum:]_]* -bpt.scan() ( - # shellcheck disable=SC2030 - local -r ld="$1" rd="$2" error_fn="${3:-__error}" - bpt.__test_delims "$ld" "$rd" || return 1 - - # Default error handler - __error() { - echo "Error: Line $(($1 + 1)) Column $(($2 + 1))" - echo "$3" - } >&2 - - # See man regex.7. We need to escape the meta characters of POSIX regex. - local -rA ESC=( - ['^']=\\ ['.']=\\ ['[']=\\ ['$']=\\ ['(']=\\ [')']=\\ - ['|']=\\ ['*']=\\ ['+']=\\ ['?']=\\ ['{']=\\ [\\]=\\ - ) - local e_ld='' e_rd='' i=0 - for ((i = 0; i < ${#ld}; ++i)); do e_ld+="${ESC["${ld:i:1}"]}${ld:i:1}"; done - for ((i = 0; i < ${#rd}; ++i)); do e_rd+="${ESC["${rd:i:1}"]}${rd:i:1}"; done - - # Keywords - local -ra KW=( - "${e_ld}" "${e_rd}" - '-eq' '-ne' '-gt' '-lt' '-ge' '-le' - '==' '!=' '>' '<' ':' '\!' '"' "'" '\(' '\)' - 'and' 'or' 'if' 'elif' 'else' 'for' 'in' 'include' - ) - local -r KW_RE="$(IFS='|' && echo -n "${KW[*]}")" - local -r ID_RE='[[:alpha:]_][[:alnum:]_]*' - - # Scanner states - local num_ld=0 - local quote='' - - # Location trackers - # shellcheck disable=SC2030 - local num_lines=0 - # shellcheck disable=SC2030 - local num_bytes=0 - - # String processing tracker & buffer. - # `str_lines=''` means currently outside the scope of string - local string='' str_lines='' str_bytes='' - # Start scannign string - __start_string() { - str_lines="${str_lines:-1}" - str_bytes="${str_bytes:-$num_bytes}" - } - # Commit (possibly multiline) string buffer - # shellcheck disable=SC2031 - __commit_string() { - ((str_lines > 0)) || return - echo "str $str_lines $((num_lines + 1 - str_lines)) $str_bytes" - # `$content` can be a literal `-ne`. Thus printf is needed. - printf '%s\n' "$string" - string='' str_lines='' str_bytes='' - } - - # Tokenizer - local line='' content='' - while IFS= read -r line || [[ $line ]]; do - # Decide whether currently scanning a string - # Only count newlines in strings (outside `ld ... rd` and inside quotes). - [[ $num_ld -gt 0 && -z "$quote" ]] || { - __start_string - [[ $num_lines -eq 0 ]] || { string+=$'\n' && ((++str_lines)); } - } - - # Scan the line - while [[ -n "$line" ]]; do - content='' # The consumed content (to be removed from `line`) - if [[ $num_ld -eq 0 ]]; then - # Outside `ld ... rd` - if [[ $line =~ ^(${e_ld}) ]]; then - # If met `ld`, enter `ld ... rd` - __commit_string - ((++num_ld)) - content="${BASH_REMATCH[1]}" - echo "ld 1 $num_lines $num_bytes" - printf '%s\n' "$content" - elif [[ $line =~ (${e_ld}) ]]; then - content="${line%%"${BASH_REMATCH[1]}"*}" - string+="$content" - else - content="$line" - string+="$line" - fi - elif [[ -n "$quote" ]]; then - # Inside quotes in `ld ... rd` - # Scan for `str` until we find a non-escaped quote. - local line_copy="$line" - while [[ $line_copy =~ ^[^${quote}]*\\${quote} ]]; do - # Escape quote inside string - string+="${line_copy%%"\\${quote}"*}${quote}" - content+="${line_copy%%"\\${quote}"*}\\${quote}" - line_copy="${line_copy#"${BASH_REMATCH[0]}"}" - done - - if [[ $line_copy =~ ${quote} ]]; then - # Remove the closing quote from line - content+="${line_copy%%"${quote}"*}${quote}" - string+="${line_copy%%"${quote}"*}" - quote='' - __commit_string - else - content="$line_copy" - string+="$line_copy" - fi - else - # Non-strings. Commit string first. - __commit_string - if [[ $line =~ ^(${KW_RE}) ]]; then - # Inside `ld ... rd` and matches a keyword at front - content="${BASH_REMATCH[1]}" - case "$content" in - '-eq') echo -n eq ;; '-ne') echo -n ne ;; - '-lt') echo -n lt ;; '-gt') echo -n gt ;; - '-le') echo -n le ;; '-ge') echo -n ge ;; - '==') echo -n streq ;; '!=') echo -n strne ;; - '>') echo -n strgt ;; '<') echo -n strlt ;; - '!') echo -n ex ;; ':') echo -n cl ;; - '(') echo -n lp ;; ')') echo -n rp ;; - '"' | "'") - quote="$content" - __start_string - ;; - "$ld") - ((++num_ld)) - echo -n ld - ;; - "$rd") - ((num_ld-- > 0)) || { - $error_fn "$num_lines" "$num_bytes" "Extra '$rd'." - return 1 - } - ((num_ld != 0)) || __start_string - echo -n rd - ;; - and | or | if | elif | else) ;& - for | in | include) echo -n "$content" ;; - *) - $error_fn "$num_lines" "$num_bytes" \ - "Internal error: Unrecognized token ${content}" - return 1 - ;; - esac - [[ -n $quote ]] || { - echo " 1 $num_lines $num_bytes" - printf '%s\n' "$content" - } - else # Inside `ld ... rd` but outside quotes - # Ignore spaces inside `ld ... rd` - [[ $line =~ ^([[:space:]]+)(.*) ]] && { - line="${BASH_REMATCH[2]}" - ((num_bytes += ${#BASH_REMATCH[1]})) - continue - } - content="$line" - - # Contents are either keywords or identifiers - if [[ $content =~ (${KW_RE}) ]]; then - content="${content%%"${BASH_REMATCH[1]}"*}" - fi - if [[ ! $content =~ ^(${ID_RE}) ]]; then - $error_fn "$num_lines" "$num_bytes" \ - "'$content' is not a valid identifier" - return 1 - fi - content="${BASH_REMATCH[1]}" - echo "id 1 $num_lines $num_bytes" - printf '%s\n' "$content" - fi - fi - - # Post-processing only counts the last line read. - line="${line#"$content"}" - ((num_bytes += ${#content})) - done - ((++num_lines)) - num_bytes=0 content='' - done - __commit_string - echo "$ 1 $num_lines 0" # The EOF token - echo '' # The EOF content (empty) -) - -bpt.__test_delims() { - [[ $1 != *' '* && $2 != *' '* ]] || { - echo "Left and right delimiters must not contain spaces." >&2 - return 1 - } - [[ "$1" != "$2" ]] || { - echo "Left and right delimiters must be different." >&2 - return 1 - } -} - -# The reduce function to collect all variables -# shellcheck disable=SC2031 # Direct access of `rule` and `contents` for speed. -bpt.__reduce_collect_vars() { - # For all `id` token, allow only the path via the VAR rule. - # For all `str` token, allow only the path to the INCLUDE rule. - case "${rule[0]}" in - ID | STR) ;; - STMT) [[ "${rule[1]}" != STR ]] || contents[$s]='' ;; - VAR) - contents[$s]="${contents[$((s + 1))]}"$'\n' - [[ ${#rule[@]} -eq 4 || ${rule[4]} != VAR ]] || - contents[$s]+="${contents[$((s + 3))]}" - ;; - BUILTIN) contents[$s]="${contents[$((s + 3))]}" ;; - INCLUDE) contents[$s]="$(__recursive_process "${contents[$((s + 3))]}")"$'\n' ;; - FORIN) # Filter tokens defined by the FORIN rule - contents[$s]="${contents[$((s + 4))]}" - local var - while read -r var; do - [[ -z $var || $var == "${contents[$((s + 2))]}" ]] || contents[$s]+="$var"$'\n' - done <<<"${contents[$((s + 6))]}" - ;; - *) # Prevent the propagation of all other non-terminals - [[ "${#rule[@]}" -ne 1 ]] || { contents[$s]='' && return; } - [[ "${rule[1]^^}" == "${rule[1]}" ]] || contents[$s]='' - local i=1 - for (( ; i < ${#rule[@]}; ++i)); do - [[ "${rule[i + 1],,}" == "${rule[i + 1]}" ]] || - contents[$s]+="${contents[$((s + i))]}" - done - ;; - esac -} - -# The reduce function to collect all includes -# shellcheck disable=SC2031 -bpt.__reduce_collect_includes() { - # For all `str` token, allow only the path via the INCLUDE rule. - case "${rule[0]}" in - STR) ;; # Allow the propagation of str - STMT) [[ "${rule[1]}" != STR ]] || contents[$s]='' ;; - VAR) contents[$s]='' ;; - INCLUDE) - contents[$s]="${contents[$((s + 3))]}"$'\n' - contents[$s]+="$(__recursive_process "${contents[$((s + 3))]}")" - ;; - *) # Prevent the propagation of all other non-terminals - [[ "${#rule[@]}" -ne 1 ]] || { contents[$s]='' && return; } - [[ "${rule[1]^^}" == "${rule[1]}" ]] || contents[$s]='' - local i=1 - for (( ; i < ${#rule[@]}; ++i)); do - [[ "${rule[i + 1],,}" == "${rule[i + 1]}" ]] || - contents[$s]+="${contents[$((s + i))]}" - done - ;; - esac -} - -# The reduce function to generate the template -# shellcheck disable=SC2031 -bpt.__reduce_generate() { - case "${rule[0]}" in - # Note: Since `contents[$s]` is exactly the first RHS, the - # `${contents[$s]}="${contents[$s]}"` assignment is unnecessary here. - STR | UOP | BOP) ;; - # Tag location for BUILTIN error reporting - ID) contents[$s]+=":$num_lines:$num_bytes" ;; - VAR) - case "${rule[3]}" in - rd) contents[$s]="\${${contents[$((s + 1))]%:*:*}}" ;; - or) contents[$s]="\${${contents[$((s + 1))]%:*:*}:-\$(e " ;;& - and) contents[$s]="\${${contents[$((s + 1))]%:*:*}:+\$(e " ;;& - *) case "${rule[4]}" in - VAR) contents[$s]+="\"${contents[$((s + 3))]}\")}" ;; - STR) contents[$s]+="${contents[$((s + 3))]@Q})}" ;; - esac ;; - esac - ;; - ARGS) - # Strip the tag from STMT - local stmt_type='' stmt= - case "${#rule[@]}" in - 2) - stmt_type="${contents[$s]%%:*}" - stmt="${contents[$s]#*:}" - contents[$s]='' - ;; - 3) - stmt_type="${contents[$((s + 1))]%%:*}" - stmt="${contents[$((s + 1))]#*:}" - ;; - esac - - # Note: `${stmt@Q}` is faster than `printf '%q' ${stmt}` - case "$stmt_type" in - STR) contents[$s]+=" ${stmt@Q} " ;; - VAR | BUILTIN) contents[$s]+=" $stmt " ;; - INCLUDE | FORIN | IF) contents[$s]+=" \"\$($stmt)\" " ;; - esac - ;; - BUILTIN) - # Filter allowed builtints - local builtin_name=${contents[$((s + 1))]%:*:*} - case "$builtin_name" in - len | seq) contents[$s]="\$($builtin_name ${contents[$((s + 3))]})" ;; - quote) contents[$s]="\"\$(e ${contents[$((s + 3))]})\"" ;; - *) # Extract and compute correct error location from ID - local line_col="${contents[$((s + 1))]#"$builtin_name"}" - local err_line=${line_col%:*} && err_line="${err_line:1}" - local err_byte=$((${line_col##*:} - ${#builtin_name})) - $error_fn "$err_line" "$err_byte" \ - "Error Unrecognized builtin function $builtin_name" >&2 - exit 1 - ;; - esac - ;; - INCLUDE) contents[$s]="$(__recursive_process "${contents[$((s + 3))]}")" ;; - FORIN) contents[$s]="for ${contents[$((s + 2))]%:*:*} in ${contents[$((s + 4))]}; do ${contents[$((s + 6))]} done" ;; - BOOL) - case "${#rule[@]}" in - 2) contents[$s]="\"\$(e ${contents[$s]})\"" ;; - 4) contents[$s]+=" ${contents[$((s + 1))]} \"\$(e ${contents[$((s + 2))]})\"" ;; - esac - ;; - BOOLA) - case "${#rule[@]}" in - 4) case "${rule[1]}" in - BOOLA) contents[$s]="${contents[$s]} && ${contents[$((s + 2))]}" ;; - lp) contents[$s]="( ${contents[$((s + 1))]} )" ;; - esac ;; - 5) contents[$s]="${contents[$s]} && ${contents[$((s + 2))]} ${contents[$((s + 3))]}" ;; - 6) contents[$s]="${contents[$s]} && ( ${contents[$((s + 3))]} )" ;; - 7) contents[$s]="${contents[$s]} && ${contents[$((s + 2))]} ( ${contents[$((s + 4))]} )" ;; - esac - ;; - BOOLO) - case "${#rule[@]}" in - 4) contents[$s]="${contents[$s]} || ${contents[$((s + 2))]}" ;; - 5) contents[$s]="${contents[$s]} || ${contents[$((s + 2))]} ${contents[$((s + 3))]}" ;; - esac - ;; - BOOLS) [[ ${#rule[@]} -eq 2 ]] || contents[$s]="${contents[$s]} ${contents[$((s + 1))]}" ;; - ELSE) - case "${#rule[@]}" in - 1) contents[$s]='' ;; - *) contents[$s]="else ${contents[$((s + 2))]}" ;; - esac - ;; - ELIF) - case "${#rule[@]}" in - 1) contents[$s]='' ;; - *) contents[$s]="${contents[$s]} elif [[ ${contents[$((s + 2))]} ]]; then ${contents[$((s + 4))]}" ;; - esac - ;; - IF) contents[$s]="if [[ ${contents[$((s + 2))]} ]]; then ${contents[$((s + 4))]}${contents[$((s + 5))]}${contents[$((s + 6))]} fi" ;; - STMT) - # Tag the sub-type to the reduce result - # (Need to strip the tag wherever STMT is used) - contents[$s]="${rule[1]}:${contents[$s]}" - ;; - DOC) # Similar to ARGS but produces commands instead of strings - # Return when document is empty - [[ "${#rule[@]}" -ne 1 ]] || { contents[$s]='' && return; } - - # Strip the tag from STMT - local stmt_type="${contents[$((s + 1))]%%:*}" - local stmt="${contents[$((s + 1))]#*:}" - - # Reduce the document - case "$stmt_type" in - STR) contents[$s]+="{ e ${stmt@Q}; };" ;; - BUILTIN | VAR) contents[$s]+="{ e \"$stmt\"; };" ;; - INCLUDE) contents[$s]+="$stmt" ;; - FORIN | IF) contents[$s]+="{ $stmt; };" ;; - esac - ;; - *) echo "Internal error: Rule ${rule[*]} not recognized" >&2 ;; - esac -} - -# Process the template -# -# $1: Left deilmiter -# $2: Right delimiter -# $3: The reduce function hook to pass to the parser. -# Defaults to bpt.__reduce_collect_vars -# $4: File to process -# $5: (optional) If set, enable debug. -# -# Input: Template from stdin -# -# Grammar: -# DOC -> DOC STMT -# | . -# STMT -> IF | FORIN | INCLUDE | BUILTIN | VAR | STR . -# IF -> ld if BOOLS cl DOC ELIF ELSE rd . -# ELIF -> ELIF elif BOOLS cl DOC -# | . -# ELSE -> else cl DOC -# | . -# BOOLS -> BOOLO -# | UOP BOOLS . -# BOOLO -> BOOLO or BOOLA -# | BOOLO or UOP BOOLA -# | BOOLA . -# BOOLA -> BOOLA and BOOL -# | BOOLA and UOP BOOL -# | BOOLA and lp BOOLS rp -# | BOOLA and UOP lp BOOLS rp -# | lp BOOLS rp -# | BOOL . -# BOOL -> ARGS BOP ARGS -# | ARGS . -# FORIN -> ld for ID in ARGS cl DOC rd . -# INCLUDE -> ld include cl STR rd . -# BUILTIN -> ld ID cl ARGS rd . -# ARGS -> ARGS STMT -# | STMT . -# VAR -> ld ID rd -# | ld ID or VAR rd -# | ld ID or STR rd -# | ld ID and VAR rd -# | ld ID and STR rd . -# BOP -> ne | eq | gt | lt | ge | le | strgt | strlt | streq | strne . -# UOP -> ex . -# ID -> id . -# STR -> str . -# -# Note1: the combination of BOOLO and BOOLA is equivalent to the expression -# grammar `BOOLS -> BOOL or BOOL | BOOL and BOOL | lp BOOL rp | BOOL .` -# with left associativity for `and` and `or` meanwhile `and` having higher -# precidence than `or`. (Solves shift-reduce conflict with subclassing). -# See: https://ece.uwaterloo.ca/~vganesh/TEACHING/W2014/lectures/lecture08.pdf -# -# Note2: the `ID -> id` and `STR -> str` rules are not redundant. -# They are for better controls when hooking the reduce function. -bpt.process() ( - local -r ld="$1" rd="$2" file="$4" debug="$5" - local -r reduce_fn="${3:-bpt.__reduce_generate}" - local -a file_stack=("${file_stack[@]}") - - [[ -f $file ]] || { - echo "Error: file '$file' does not exist" >&2 - return 1 - } - file_stack+=("$file") - - # Curry this function so that it can be called by the reducer recursively - __recursive_process() { - local file - # Detect recursive includes - for file in "${file_stack[@]}"; do - [[ $file -ef $1 ]] && { - printf "Error: cyclic include detected:\n" - printf ' In: %s\n' "${file_stack[0]}" - printf ' --> %s\n' "${file_stack[@]:1}" - printf ' --> %s\n' "${file}" - return 1 - } >&2 - done - bpt.process "$ld" "$rd" "$reduce_fn" "$1" "$debug" - } - - # Pretty-print parse errors - __error_handler() { - echo "Error: File '$file' Line $(($1 + 1)) Column $(($2 + 1))" - echo "$3" - echo - # Pretty-print the error location - local -a line=() - mapfile -t -s "$1" -n 1 line <"$file" - echo "${line[0]}" - printf "%$2s^--- \033[1mHERE\033[0m\n" - } >&2 - - # Prase with the provided reduce function - bpt.__lr_parse BPT_PARSE_TABLE "$reduce_fn" __error_handler "$debug" \ - < <(bpt.scan "$ld" "$rd" __error_handler <"$file") -) - -# $1: Left deilmiter -# $2: Right delimiter -# $3: File to process -# $4: (optional) If set, enable debug. -# shellcheck disable=SC2207 -bpt.fingerprint() { - local -r ld="$1" rd="$2" file="$3" debug="$4" - - # Collect vars and includes - local -a vars=() incs=() - mapfile -t vars < <(bpt.__dedup "$(bpt.process "$ld" "$rd" bpt.__reduce_collect_vars "$infile" "$debug")") - mapfile -t incs < <(bpt.__dedup "$(bpt.process "$ld" "$rd" bpt.__reduce_collect_includes "$infile" "$debug")") - local fingerprint='' - local -a md5=() - local util - - case "${BASHTARD_PLATFORM[key]}" in - freebsd) util=md5 ;; - linux-*) util=md5sum ;; - *) - debug "bpt/fingerprint" "Falling back to md5sum for hashing" - util=md5sum - ;; - esac - - # Hash this script (the generator) - md5=($("$util" "${BASH_SOURCE[0]}")) && fingerprint+="M:${md5[0]}" - - # Hash the file itself - md5=($("$util" "$file")) && fingerprint+=":S:${md5[0]}" - - # Digest the includes - for inc in "${incs[@]}"; do - md5=($("$util" "$inc")) && fingerprint+=":I:${md5[0]}" - done - # Digest and check for missing vars - for var in "${vars[@]}"; do - if [[ -z ${!var+.} ]]; then - echo "Error: variable '$var' is required but not set" >&2 - return 1 - fi - md5=($(echo -n "${var}${!var}" | "$util")) && fingerprint+=":V:${md5[0]}" - done - - # Digest the digests - [[ $debug ]] && echo "[DBG] Raw fingerprint: $fingerprint" - md5=($(echo -n "${fingerprint}" | "$util")) && fingerprint="${md5[0]}" - echo "$fingerprint" -} - -bpt.print_help() { - echo -e "\033[1mbpt - A command-line tool for processing simple templates\033[0m" - echo - echo -e "\033[1mSYNOPSIS\033[0m" - echo " bpt [-l ] [-r ] [-d] []" - echo - echo -e "\033[1mCOMMANDS\033[0m" - echo " scan, s:" - echo " Call the scanner (lexer)." - echo - echo " generate, g:" - echo " Generate a shell script based on the input file. Output is sent to stdout." - echo - echo " generate-eval, ge:" - echo " Same as generate, but the output is evaluated. Output is sent to stdout." - echo - echo " collect-vars, cv:" - echo " Collect variable used in the input file recursively and output them to stdout." - echo - echo " collect-includes, ci:" - echo " Collect all files included in the input file recursively and output them to stdout." - echo - echo " fingerprint, f:" - echo " Generate a unique identifier based on all factors affecting the evaluation output." - echo - echo " -h, --help:" - echo " Print this help." - echo - echo " -v, --version:" - echo " Print version number." - echo - echo -e "\033[1mOPTIONS\033[0m" - echo " -l , --left-delimiter :" - echo " Set the left delimiter to use for placeholders (default \`{{\`)." - echo - echo " -r , --right-delimiter :" - echo " Set the right delimiter to use for placeholders (default \`}}\`)." - echo - echo " -d, --debug:" - echo " Enable debug mode." - echo - echo -e "\033[1mARGUMENTS\033[0m" - echo " bpt takes an optional input file path as its argument. If no input file is specified, bpt will read from stdin." - echo - echo -e "\033[1mEXAMPLES\033[0m" - echo " Generate script from a single input file using default delimiters:" - echo " bpt g input.tpl > output.sh" - echo - echo " Render the input file:" - echo " var1=VAR1 var2=VAR2 ... bpt ge input.tpl" - echo - echo " Collect variable names and values from an input file:" - echo " bpt cv input.tpl" - echo - echo " Collect include file paths from an input file:" - echo " bpt ci input.tpl" - echo - echo " Generate a fingerprint for an input" - echo " var1=VAR1 var2=VAR2 ... bpt f input.tpl" - echo - echo " Using custom delimiters:" - echo " bpt -l \"<<\" -r \">>\" g input.tpl > output.sh" - echo - echo -e "\033[1mTEMPLATE GRAMMAR EXAMPLES\033[0m" - echo " Variable replacements" - echo ' {{ var }}' - echo ' {{ var or "abc" }}' - echo ' {{ var or {{var2}} }}' - echo - echo " Branching" - echo ' {{ if {{x}}: {{var1}} else : {{var2}} }}' - echo ' {{ if {{x}} -gt "5": {{var1}} elif: {{var2}} else: {{var3}} }}' - echo ' {{ if ({{var1}} > "abc" and {{var2}} < "def") or {{var3}} == "hello" : {{ include : "input2.tpl" }} }}' - echo - echo " Available operators are: " - echo " compare numbers: -ne, -eq, -gt, -lt, -ge, -le " - echo " compare strings: >, <, ==, !=" - echo " logical operators: and, or, !" - echo " grouping: ()" - echo - echo " Looping" - echo ' {{ for {{i}} in "a" "b" "c": "abc"{{i}}"def" }}' - echo ' {{ for {{i}} in {{seq: "5"}}: "abc"{{i}}"def" }}' - echo - echo " Include another template" - echo ' {{ include : "input2.tpl" }}' - echo - echo " Builtin functions" - echo ' {{ seq: "5" }}' - echo ' {{ len: "abc" }}' - echo ' {{ quote: {{seq: "1" "2" "5"}} }}' - echo - echo " Note: bpt doesn't distinguish between strings and numbers." - echo " All non-keywords should be treated as strings." - echo " All strings inside {{...}} need to be quoted. e.g. 'abc', \"abc\", '123'." - echo - echo -e "\033[1mCOPYRIGHT\033[0m" - echo " MIT License." - echo " Copyright (c) 2023 Hu Sixu." - echo " https://github.com/husixu1/bpt" -} - -bpt.main() ( - # Clean the environment to avoid builtin overrides - # See https://unix.stackexchange.com/questions/188327 - POSIXLY_CORRECT=1 - \unset -f help read unset - \unset POSIXLY_CORRECT - while \read -r cmd; do - [[ "$cmd" =~ ^([a-z:.\[]+): ]] && \unset -f "${BASH_REMATCH[1]}" - done < <(\help -s "*") - - local ld='{{' rd='}}' - local infile='' - local cmd='' reduce_fn=bpt.__reduce_generate post_process=eval - local debug='' - - # Parse command - case "$1" in - scan | s) cmd=scan ;; - generate | g) - cmd=generate - reduce_fn=bpt.__reduce_generate - post_process='echo' - ;; - generate-eval | ge) - cmd=generate-eval - reduce_fn=bpt.__reduce_generate - post_process='eval' - ;; - collect-vars | cv) - cmd=collect-vars - reduce_fn=bpt.__reduce_collect_vars - post_process=bpt.__dedup - ;; - collect-includes | ci) - cmd=collect-includes - reduce_fn=bpt.__reduce_collect_includes - post_process=bpt.__dedup - ;; - fingerprint | f) - cmd=fingerprint - ;; - -v | --version) echo "$__BPT_VERSION" && exit 0 ;; - -h | --help | '') bpt.print_help && exit 0 ;; - *) - echo "Unrecognized command '$1'" >&2 - bpt.print_help - exit 1 - ;; - esac - shift - - # Parse arguments - while [[ $# -gt 0 ]]; do - case "$1" in - -l | --left-delimiter) shift && ld="$1" ;; - -r | --right-delimiter) shift && rd="$1" ;; - -d | --debug) debug=1 ;; - -h | --help) - bpt.print_help - exit 0 - ;; - *) - [[ -z $infile ]] || { - echo "Error: Option '$1' not recognized." >&2 - bpt.print_help - return 1 - } - infile="$1" - ;; - esac - shift - done - - # If file not provided, read into a temporary file and use that file. - [[ -n $infile ]] || { - infile="$(mktemp)" || { echo "Error: mktemp failed." >&2 && exit 1; } - bpt.__add_exit_trap "rm -f \"${infile:?}\"" - cat >"${infile:?}" - } - - # Global constants for pretty-printing - local -rA BPT_PP_TOKEN_TABLE=( - [ld]="$ld" [rd]="$rd" - [eq]='-eq' [ne]='-ne' [gt]='-gt' [lt]='-lt' [ge]='-ge' [le]='-le' - [streq]='==' [strne]='!=' [strgt]='>' [strlt]='<' - [cl]=':' [ex]='!' [lp]='(' [rp]=')') - - # shellcheck disable=SC2034 - # >>> BPT_PARSE_TABLE_S >>> - local -rA BPT_PARSE_TABLE=( # {{{ - ["0,ld"]="r DOC" ["0,str"]="r DOC" ["0,$"]="r DOC" ["0,DOC"]="1" ["1,ld"]="s 9" - ["1,str"]="s 10" ["1,$"]="a" ["1,STMT"]="2" ["1,IF"]="3" ["1,FORIN"]="4" - ["1,INCLUDE"]="5" ["1,BUILTIN"]="6" ["1,VAR"]="7" ["1,STR"]="8" - ["2,ld"]="r DOC DOC STMT" ["2,rd"]="r DOC DOC STMT" ["2,elif"]="r DOC DOC STMT" - ["2,else"]="r DOC DOC STMT" ["2,str"]="r DOC DOC STMT" ["2,$"]="r DOC DOC STMT" - ["3,ld"]="r STMT IF" ["3,cl"]="r STMT IF" ["3,rd"]="r STMT IF" - ["3,elif"]="r STMT IF" ["3,else"]="r STMT IF" ["3,or"]="r STMT IF" - ["3,and"]="r STMT IF" ["3,rp"]="r STMT IF" ["3,ne"]="r STMT IF" - ["3,eq"]="r STMT IF" ["3,gt"]="r STMT IF" ["3,lt"]="r STMT IF" - ["3,ge"]="r STMT IF" ["3,le"]="r STMT IF" ["3,strgt"]="r STMT IF" - ["3,strlt"]="r STMT IF" ["3,streq"]="r STMT IF" ["3,strne"]="r STMT IF" - ["3,str"]="r STMT IF" ["3,$"]="r STMT IF" ["4,ld"]="r STMT FORIN" - ["4,cl"]="r STMT FORIN" ["4,rd"]="r STMT FORIN" ["4,elif"]="r STMT FORIN" - ["4,else"]="r STMT FORIN" ["4,or"]="r STMT FORIN" ["4,and"]="r STMT FORIN" - ["4,rp"]="r STMT FORIN" ["4,ne"]="r STMT FORIN" ["4,eq"]="r STMT FORIN" - ["4,gt"]="r STMT FORIN" ["4,lt"]="r STMT FORIN" ["4,ge"]="r STMT FORIN" - ["4,le"]="r STMT FORIN" ["4,strgt"]="r STMT FORIN" ["4,strlt"]="r STMT FORIN" - ["4,streq"]="r STMT FORIN" ["4,strne"]="r STMT FORIN" ["4,str"]="r STMT FORIN" - ["4,$"]="r STMT FORIN" ["5,ld"]="r STMT INCLUDE" ["5,cl"]="r STMT INCLUDE" - ["5,rd"]="r STMT INCLUDE" ["5,elif"]="r STMT INCLUDE" - ["5,else"]="r STMT INCLUDE" ["5,or"]="r STMT INCLUDE" ["5,and"]="r STMT INCLUDE" - ["5,rp"]="r STMT INCLUDE" ["5,ne"]="r STMT INCLUDE" ["5,eq"]="r STMT INCLUDE" - ["5,gt"]="r STMT INCLUDE" ["5,lt"]="r STMT INCLUDE" ["5,ge"]="r STMT INCLUDE" - ["5,le"]="r STMT INCLUDE" ["5,strgt"]="r STMT INCLUDE" - ["5,strlt"]="r STMT INCLUDE" ["5,streq"]="r STMT INCLUDE" - ["5,strne"]="r STMT INCLUDE" ["5,str"]="r STMT INCLUDE" ["5,$"]="r STMT INCLUDE" - ["6,ld"]="r STMT BUILTIN" ["6,cl"]="r STMT BUILTIN" ["6,rd"]="r STMT BUILTIN" - ["6,elif"]="r STMT BUILTIN" ["6,else"]="r STMT BUILTIN" - ["6,or"]="r STMT BUILTIN" ["6,and"]="r STMT BUILTIN" ["6,rp"]="r STMT BUILTIN" - ["6,ne"]="r STMT BUILTIN" ["6,eq"]="r STMT BUILTIN" ["6,gt"]="r STMT BUILTIN" - ["6,lt"]="r STMT BUILTIN" ["6,ge"]="r STMT BUILTIN" ["6,le"]="r STMT BUILTIN" - ["6,strgt"]="r STMT BUILTIN" ["6,strlt"]="r STMT BUILTIN" - ["6,streq"]="r STMT BUILTIN" ["6,strne"]="r STMT BUILTIN" - ["6,str"]="r STMT BUILTIN" ["6,$"]="r STMT BUILTIN" ["7,ld"]="r STMT VAR" - ["7,cl"]="r STMT VAR" ["7,rd"]="r STMT VAR" ["7,elif"]="r STMT VAR" - ["7,else"]="r STMT VAR" ["7,or"]="r STMT VAR" ["7,and"]="r STMT VAR" - ["7,rp"]="r STMT VAR" ["7,ne"]="r STMT VAR" ["7,eq"]="r STMT VAR" - ["7,gt"]="r STMT VAR" ["7,lt"]="r STMT VAR" ["7,ge"]="r STMT VAR" - ["7,le"]="r STMT VAR" ["7,strgt"]="r STMT VAR" ["7,strlt"]="r STMT VAR" - ["7,streq"]="r STMT VAR" ["7,strne"]="r STMT VAR" ["7,str"]="r STMT VAR" - ["7,$"]="r STMT VAR" ["8,ld"]="r STMT STR" ["8,cl"]="r STMT STR" - ["8,rd"]="r STMT STR" ["8,elif"]="r STMT STR" ["8,else"]="r STMT STR" - ["8,or"]="r STMT STR" ["8,and"]="r STMT STR" ["8,rp"]="r STMT STR" - ["8,ne"]="r STMT STR" ["8,eq"]="r STMT STR" ["8,gt"]="r STMT STR" - ["8,lt"]="r STMT STR" ["8,ge"]="r STMT STR" ["8,le"]="r STMT STR" - ["8,strgt"]="r STMT STR" ["8,strlt"]="r STMT STR" ["8,streq"]="r STMT STR" - ["8,strne"]="r STMT STR" ["8,str"]="r STMT STR" ["8,$"]="r STMT STR" - ["9,if"]="s 11" ["9,for"]="s 12" ["9,include"]="s 13" ["9,id"]="s 15" - ["9,ID"]="14" ["10,ld"]="r STR str" ["10,cl"]="r STR str" ["10,rd"]="r STR str" - ["10,elif"]="r STR str" ["10,else"]="r STR str" ["10,or"]="r STR str" - ["10,and"]="r STR str" ["10,rp"]="r STR str" ["10,ne"]="r STR str" - ["10,eq"]="r STR str" ["10,gt"]="r STR str" ["10,lt"]="r STR str" - ["10,ge"]="r STR str" ["10,le"]="r STR str" ["10,strgt"]="r STR str" - ["10,strlt"]="r STR str" ["10,streq"]="r STR str" ["10,strne"]="r STR str" - ["10,str"]="r STR str" ["10,$"]="r STR str" ["11,ld"]="s 9" ["11,lp"]="s 21" - ["11,ex"]="s 20" ["11,str"]="s 10" ["11,STMT"]="24" ["11,IF"]="3" - ["11,FORIN"]="4" ["11,INCLUDE"]="5" ["11,BUILTIN"]="6" ["11,VAR"]="7" - ["11,STR"]="8" ["11,BOOLS"]="16" ["11,BOOLO"]="17" ["11,UOP"]="18" - ["11,BOOLA"]="19" ["11,BOOL"]="22" ["11,ARGS"]="23" ["12,id"]="s 15" - ["12,ID"]="25" ["13,cl"]="s 26" ["14,cl"]="s 27" ["14,rd"]="s 28" - ["14,or"]="s 29" ["14,and"]="s 30" ["15,cl"]="r ID id" ["15,rd"]="r ID id" - ["15,or"]="r ID id" ["15,and"]="r ID id" ["15,in"]="r ID id" ["16,cl"]="s 31" - ["17,cl"]="r BOOLS BOOLO" ["17,or"]="s 32" ["17,rp"]="r BOOLS BOOLO" - ["18,ld"]="s 9" ["18,lp"]="s 21" ["18,ex"]="s 20" ["18,str"]="s 10" - ["18,STMT"]="24" ["18,IF"]="3" ["18,FORIN"]="4" ["18,INCLUDE"]="5" - ["18,BUILTIN"]="6" ["18,VAR"]="7" ["18,STR"]="8" ["18,BOOLS"]="33" - ["18,BOOLO"]="17" ["18,UOP"]="18" ["18,BOOLA"]="19" ["18,BOOL"]="22" - ["18,ARGS"]="23" ["19,cl"]="r BOOLO BOOLA" ["19,or"]="r BOOLO BOOLA" - ["19,and"]="s 34" ["19,rp"]="r BOOLO BOOLA" ["20,ld"]="r UOP ex" - ["20,lp"]="r UOP ex" ["20,ex"]="r UOP ex" ["20,str"]="r UOP ex" ["21,ld"]="s 9" - ["21,lp"]="s 21" ["21,ex"]="s 20" ["21,str"]="s 10" ["21,STMT"]="24" - ["21,IF"]="3" ["21,FORIN"]="4" ["21,INCLUDE"]="5" ["21,BUILTIN"]="6" - ["21,VAR"]="7" ["21,STR"]="8" ["21,BOOLS"]="35" ["21,BOOLO"]="17" - ["21,UOP"]="18" ["21,BOOLA"]="19" ["21,BOOL"]="22" ["21,ARGS"]="23" - ["22,cl"]="r BOOLA BOOL" ["22,or"]="r BOOLA BOOL" ["22,and"]="r BOOLA BOOL" - ["22,rp"]="r BOOLA BOOL" ["23,ld"]="s 9" ["23,cl"]="r BOOL ARGS" - ["23,or"]="r BOOL ARGS" ["23,and"]="r BOOL ARGS" ["23,rp"]="r BOOL ARGS" - ["23,ne"]="s 38" ["23,eq"]="s 39" ["23,gt"]="s 40" ["23,lt"]="s 41" - ["23,ge"]="s 42" ["23,le"]="s 43" ["23,strgt"]="s 44" ["23,strlt"]="s 45" - ["23,streq"]="s 46" ["23,strne"]="s 47" ["23,str"]="s 10" ["23,STMT"]="37" - ["23,IF"]="3" ["23,FORIN"]="4" ["23,INCLUDE"]="5" ["23,BUILTIN"]="6" - ["23,VAR"]="7" ["23,STR"]="8" ["23,BOP"]="36" ["24,ld"]="r ARGS STMT" - ["24,cl"]="r ARGS STMT" ["24,rd"]="r ARGS STMT" ["24,or"]="r ARGS STMT" - ["24,and"]="r ARGS STMT" ["24,rp"]="r ARGS STMT" ["24,ne"]="r ARGS STMT" - ["24,eq"]="r ARGS STMT" ["24,gt"]="r ARGS STMT" ["24,lt"]="r ARGS STMT" - ["24,ge"]="r ARGS STMT" ["24,le"]="r ARGS STMT" ["24,strgt"]="r ARGS STMT" - ["24,strlt"]="r ARGS STMT" ["24,streq"]="r ARGS STMT" ["24,strne"]="r ARGS STMT" - ["24,str"]="r ARGS STMT" ["25,in"]="s 48" ["26,str"]="s 10" ["26,STR"]="49" - ["27,ld"]="s 9" ["27,str"]="s 10" ["27,STMT"]="24" ["27,IF"]="3" - ["27,FORIN"]="4" ["27,INCLUDE"]="5" ["27,BUILTIN"]="6" ["27,VAR"]="7" - ["27,STR"]="8" ["27,ARGS"]="50" ["28,ld"]="r VAR ld ID rd" - ["28,cl"]="r VAR ld ID rd" ["28,rd"]="r VAR ld ID rd" - ["28,elif"]="r VAR ld ID rd" ["28,else"]="r VAR ld ID rd" - ["28,or"]="r VAR ld ID rd" ["28,and"]="r VAR ld ID rd" - ["28,rp"]="r VAR ld ID rd" ["28,ne"]="r VAR ld ID rd" ["28,eq"]="r VAR ld ID rd" - ["28,gt"]="r VAR ld ID rd" ["28,lt"]="r VAR ld ID rd" ["28,ge"]="r VAR ld ID rd" - ["28,le"]="r VAR ld ID rd" ["28,strgt"]="r VAR ld ID rd" - ["28,strlt"]="r VAR ld ID rd" ["28,streq"]="r VAR ld ID rd" - ["28,strne"]="r VAR ld ID rd" ["28,str"]="r VAR ld ID rd" - ["28,$"]="r VAR ld ID rd" ["29,ld"]="s 53" ["29,str"]="s 10" ["29,VAR"]="51" - ["29,STR"]="52" ["30,ld"]="s 53" ["30,str"]="s 10" ["30,VAR"]="54" - ["30,STR"]="55" ["31,ld"]="r DOC" ["31,rd"]="r DOC" ["31,elif"]="r DOC" - ["31,else"]="r DOC" ["31,str"]="r DOC" ["31,DOC"]="56" ["32,ld"]="s 9" - ["32,lp"]="s 21" ["32,ex"]="s 20" ["32,str"]="s 10" ["32,STMT"]="24" - ["32,IF"]="3" ["32,FORIN"]="4" ["32,INCLUDE"]="5" ["32,BUILTIN"]="6" - ["32,VAR"]="7" ["32,STR"]="8" ["32,UOP"]="58" ["32,BOOLA"]="57" ["32,BOOL"]="22" - ["32,ARGS"]="23" ["33,cl"]="r BOOLS UOP BOOLS" ["33,rp"]="r BOOLS UOP BOOLS" - ["34,ld"]="s 9" ["34,lp"]="s 61" ["34,ex"]="s 20" ["34,str"]="s 10" - ["34,STMT"]="24" ["34,IF"]="3" ["34,FORIN"]="4" ["34,INCLUDE"]="5" - ["34,BUILTIN"]="6" ["34,VAR"]="7" ["34,STR"]="8" ["34,UOP"]="60" - ["34,BOOL"]="59" ["34,ARGS"]="23" ["35,rp"]="s 62" ["36,ld"]="s 9" - ["36,str"]="s 10" ["36,STMT"]="24" ["36,IF"]="3" ["36,FORIN"]="4" - ["36,INCLUDE"]="5" ["36,BUILTIN"]="6" ["36,VAR"]="7" ["36,STR"]="8" - ["36,ARGS"]="63" ["37,ld"]="r ARGS ARGS STMT" ["37,cl"]="r ARGS ARGS STMT" - ["37,rd"]="r ARGS ARGS STMT" ["37,or"]="r ARGS ARGS STMT" - ["37,and"]="r ARGS ARGS STMT" ["37,rp"]="r ARGS ARGS STMT" - ["37,ne"]="r ARGS ARGS STMT" ["37,eq"]="r ARGS ARGS STMT" - ["37,gt"]="r ARGS ARGS STMT" ["37,lt"]="r ARGS ARGS STMT" - ["37,ge"]="r ARGS ARGS STMT" ["37,le"]="r ARGS ARGS STMT" - ["37,strgt"]="r ARGS ARGS STMT" ["37,strlt"]="r ARGS ARGS STMT" - ["37,streq"]="r ARGS ARGS STMT" ["37,strne"]="r ARGS ARGS STMT" - ["37,str"]="r ARGS ARGS STMT" ["38,ld"]="r BOP ne" ["38,str"]="r BOP ne" - ["39,ld"]="r BOP eq" ["39,str"]="r BOP eq" ["40,ld"]="r BOP gt" - ["40,str"]="r BOP gt" ["41,ld"]="r BOP lt" ["41,str"]="r BOP lt" - ["42,ld"]="r BOP ge" ["42,str"]="r BOP ge" ["43,ld"]="r BOP le" - ["43,str"]="r BOP le" ["44,ld"]="r BOP strgt" ["44,str"]="r BOP strgt" - ["45,ld"]="r BOP strlt" ["45,str"]="r BOP strlt" ["46,ld"]="r BOP streq" - ["46,str"]="r BOP streq" ["47,ld"]="r BOP strne" ["47,str"]="r BOP strne" - ["48,ld"]="s 9" ["48,str"]="s 10" ["48,STMT"]="24" ["48,IF"]="3" - ["48,FORIN"]="4" ["48,INCLUDE"]="5" ["48,BUILTIN"]="6" ["48,VAR"]="7" - ["48,STR"]="8" ["48,ARGS"]="64" ["49,rd"]="s 65" ["50,ld"]="s 9" - ["50,rd"]="s 66" ["50,str"]="s 10" ["50,STMT"]="37" ["50,IF"]="3" - ["50,FORIN"]="4" ["50,INCLUDE"]="5" ["50,BUILTIN"]="6" ["50,VAR"]="7" - ["50,STR"]="8" ["51,rd"]="s 67" ["52,rd"]="s 68" ["53,id"]="s 15" ["53,ID"]="69" - ["54,rd"]="s 70" ["55,rd"]="s 71" ["56,ld"]="s 9" ["56,rd"]="r ELIF" - ["56,elif"]="r ELIF" ["56,else"]="r ELIF" ["56,str"]="s 10" ["56,STMT"]="2" - ["56,IF"]="3" ["56,FORIN"]="4" ["56,INCLUDE"]="5" ["56,BUILTIN"]="6" - ["56,VAR"]="7" ["56,STR"]="8" ["56,ELIF"]="72" - ["57,cl"]="r BOOLO BOOLO or BOOLA" ["57,or"]="r BOOLO BOOLO or BOOLA" - ["57,and"]="s 34" ["57,rp"]="r BOOLO BOOLO or BOOLA" ["58,ld"]="s 9" - ["58,lp"]="s 21" ["58,str"]="s 10" ["58,STMT"]="24" ["58,IF"]="3" - ["58,FORIN"]="4" ["58,INCLUDE"]="5" ["58,BUILTIN"]="6" ["58,VAR"]="7" - ["58,STR"]="8" ["58,BOOLA"]="73" ["58,BOOL"]="22" ["58,ARGS"]="23" - ["59,cl"]="r BOOLA BOOLA and BOOL" ["59,or"]="r BOOLA BOOLA and BOOL" - ["59,and"]="r BOOLA BOOLA and BOOL" ["59,rp"]="r BOOLA BOOLA and BOOL" - ["60,ld"]="s 9" ["60,lp"]="s 75" ["60,str"]="s 10" ["60,STMT"]="24" - ["60,IF"]="3" ["60,FORIN"]="4" ["60,INCLUDE"]="5" ["60,BUILTIN"]="6" - ["60,VAR"]="7" ["60,STR"]="8" ["60,BOOL"]="74" ["60,ARGS"]="23" ["61,ld"]="s 9" - ["61,lp"]="s 21" ["61,ex"]="s 20" ["61,str"]="s 10" ["61,STMT"]="24" - ["61,IF"]="3" ["61,FORIN"]="4" ["61,INCLUDE"]="5" ["61,BUILTIN"]="6" - ["61,VAR"]="7" ["61,STR"]="8" ["61,BOOLS"]="76" ["61,BOOLO"]="17" - ["61,UOP"]="18" ["61,BOOLA"]="19" ["61,BOOL"]="22" ["61,ARGS"]="23" - ["62,cl"]="r BOOLA lp BOOLS rp" ["62,or"]="r BOOLA lp BOOLS rp" - ["62,and"]="r BOOLA lp BOOLS rp" ["62,rp"]="r BOOLA lp BOOLS rp" ["63,ld"]="s 9" - ["63,cl"]="r BOOL ARGS BOP ARGS" ["63,or"]="r BOOL ARGS BOP ARGS" - ["63,and"]="r BOOL ARGS BOP ARGS" ["63,rp"]="r BOOL ARGS BOP ARGS" - ["63,str"]="s 10" ["63,STMT"]="37" ["63,IF"]="3" ["63,FORIN"]="4" - ["63,INCLUDE"]="5" ["63,BUILTIN"]="6" ["63,VAR"]="7" ["63,STR"]="8" - ["64,ld"]="s 9" ["64,cl"]="s 77" ["64,str"]="s 10" ["64,STMT"]="37" - ["64,IF"]="3" ["64,FORIN"]="4" ["64,INCLUDE"]="5" ["64,BUILTIN"]="6" - ["64,VAR"]="7" ["64,STR"]="8" ["65,ld"]="r INCLUDE ld include cl STR rd" - ["65,cl"]="r INCLUDE ld include cl STR rd" - ["65,rd"]="r INCLUDE ld include cl STR rd" - ["65,elif"]="r INCLUDE ld include cl STR rd" - ["65,else"]="r INCLUDE ld include cl STR rd" - ["65,or"]="r INCLUDE ld include cl STR rd" - ["65,and"]="r INCLUDE ld include cl STR rd" - ["65,rp"]="r INCLUDE ld include cl STR rd" - ["65,ne"]="r INCLUDE ld include cl STR rd" - ["65,eq"]="r INCLUDE ld include cl STR rd" - ["65,gt"]="r INCLUDE ld include cl STR rd" - ["65,lt"]="r INCLUDE ld include cl STR rd" - ["65,ge"]="r INCLUDE ld include cl STR rd" - ["65,le"]="r INCLUDE ld include cl STR rd" - ["65,strgt"]="r INCLUDE ld include cl STR rd" - ["65,strlt"]="r INCLUDE ld include cl STR rd" - ["65,streq"]="r INCLUDE ld include cl STR rd" - ["65,strne"]="r INCLUDE ld include cl STR rd" - ["65,str"]="r INCLUDE ld include cl STR rd" - ["65,$"]="r INCLUDE ld include cl STR rd" ["66,ld"]="r BUILTIN ld ID cl ARGS rd" - ["66,cl"]="r BUILTIN ld ID cl ARGS rd" ["66,rd"]="r BUILTIN ld ID cl ARGS rd" - ["66,elif"]="r BUILTIN ld ID cl ARGS rd" - ["66,else"]="r BUILTIN ld ID cl ARGS rd" ["66,or"]="r BUILTIN ld ID cl ARGS rd" - ["66,and"]="r BUILTIN ld ID cl ARGS rd" ["66,rp"]="r BUILTIN ld ID cl ARGS rd" - ["66,ne"]="r BUILTIN ld ID cl ARGS rd" ["66,eq"]="r BUILTIN ld ID cl ARGS rd" - ["66,gt"]="r BUILTIN ld ID cl ARGS rd" ["66,lt"]="r BUILTIN ld ID cl ARGS rd" - ["66,ge"]="r BUILTIN ld ID cl ARGS rd" ["66,le"]="r BUILTIN ld ID cl ARGS rd" - ["66,strgt"]="r BUILTIN ld ID cl ARGS rd" - ["66,strlt"]="r BUILTIN ld ID cl ARGS rd" - ["66,streq"]="r BUILTIN ld ID cl ARGS rd" - ["66,strne"]="r BUILTIN ld ID cl ARGS rd" - ["66,str"]="r BUILTIN ld ID cl ARGS rd" ["66,$"]="r BUILTIN ld ID cl ARGS rd" - ["67,ld"]="r VAR ld ID or VAR rd" ["67,cl"]="r VAR ld ID or VAR rd" - ["67,rd"]="r VAR ld ID or VAR rd" ["67,elif"]="r VAR ld ID or VAR rd" - ["67,else"]="r VAR ld ID or VAR rd" ["67,or"]="r VAR ld ID or VAR rd" - ["67,and"]="r VAR ld ID or VAR rd" ["67,rp"]="r VAR ld ID or VAR rd" - ["67,ne"]="r VAR ld ID or VAR rd" ["67,eq"]="r VAR ld ID or VAR rd" - ["67,gt"]="r VAR ld ID or VAR rd" ["67,lt"]="r VAR ld ID or VAR rd" - ["67,ge"]="r VAR ld ID or VAR rd" ["67,le"]="r VAR ld ID or VAR rd" - ["67,strgt"]="r VAR ld ID or VAR rd" ["67,strlt"]="r VAR ld ID or VAR rd" - ["67,streq"]="r VAR ld ID or VAR rd" ["67,strne"]="r VAR ld ID or VAR rd" - ["67,str"]="r VAR ld ID or VAR rd" ["67,$"]="r VAR ld ID or VAR rd" - ["68,ld"]="r VAR ld ID or STR rd" ["68,cl"]="r VAR ld ID or STR rd" - ["68,rd"]="r VAR ld ID or STR rd" ["68,elif"]="r VAR ld ID or STR rd" - ["68,else"]="r VAR ld ID or STR rd" ["68,or"]="r VAR ld ID or STR rd" - ["68,and"]="r VAR ld ID or STR rd" ["68,rp"]="r VAR ld ID or STR rd" - ["68,ne"]="r VAR ld ID or STR rd" ["68,eq"]="r VAR ld ID or STR rd" - ["68,gt"]="r VAR ld ID or STR rd" ["68,lt"]="r VAR ld ID or STR rd" - ["68,ge"]="r VAR ld ID or STR rd" ["68,le"]="r VAR ld ID or STR rd" - ["68,strgt"]="r VAR ld ID or STR rd" ["68,strlt"]="r VAR ld ID or STR rd" - ["68,streq"]="r VAR ld ID or STR rd" ["68,strne"]="r VAR ld ID or STR rd" - ["68,str"]="r VAR ld ID or STR rd" ["68,$"]="r VAR ld ID or STR rd" - ["69,rd"]="s 28" ["69,or"]="s 29" ["69,and"]="s 30" - ["70,ld"]="r VAR ld ID and VAR rd" ["70,cl"]="r VAR ld ID and VAR rd" - ["70,rd"]="r VAR ld ID and VAR rd" ["70,elif"]="r VAR ld ID and VAR rd" - ["70,else"]="r VAR ld ID and VAR rd" ["70,or"]="r VAR ld ID and VAR rd" - ["70,and"]="r VAR ld ID and VAR rd" ["70,rp"]="r VAR ld ID and VAR rd" - ["70,ne"]="r VAR ld ID and VAR rd" ["70,eq"]="r VAR ld ID and VAR rd" - ["70,gt"]="r VAR ld ID and VAR rd" ["70,lt"]="r VAR ld ID and VAR rd" - ["70,ge"]="r VAR ld ID and VAR rd" ["70,le"]="r VAR ld ID and VAR rd" - ["70,strgt"]="r VAR ld ID and VAR rd" ["70,strlt"]="r VAR ld ID and VAR rd" - ["70,streq"]="r VAR ld ID and VAR rd" ["70,strne"]="r VAR ld ID and VAR rd" - ["70,str"]="r VAR ld ID and VAR rd" ["70,$"]="r VAR ld ID and VAR rd" - ["71,ld"]="r VAR ld ID and STR rd" ["71,cl"]="r VAR ld ID and STR rd" - ["71,rd"]="r VAR ld ID and STR rd" ["71,elif"]="r VAR ld ID and STR rd" - ["71,else"]="r VAR ld ID and STR rd" ["71,or"]="r VAR ld ID and STR rd" - ["71,and"]="r VAR ld ID and STR rd" ["71,rp"]="r VAR ld ID and STR rd" - ["71,ne"]="r VAR ld ID and STR rd" ["71,eq"]="r VAR ld ID and STR rd" - ["71,gt"]="r VAR ld ID and STR rd" ["71,lt"]="r VAR ld ID and STR rd" - ["71,ge"]="r VAR ld ID and STR rd" ["71,le"]="r VAR ld ID and STR rd" - ["71,strgt"]="r VAR ld ID and STR rd" ["71,strlt"]="r VAR ld ID and STR rd" - ["71,streq"]="r VAR ld ID and STR rd" ["71,strne"]="r VAR ld ID and STR rd" - ["71,str"]="r VAR ld ID and STR rd" ["71,$"]="r VAR ld ID and STR rd" - ["72,rd"]="r ELSE" ["72,elif"]="s 79" ["72,else"]="s 80" ["72,ELSE"]="78" - ["73,cl"]="r BOOLO BOOLO or UOP BOOLA" ["73,or"]="r BOOLO BOOLO or UOP BOOLA" - ["73,and"]="s 34" ["73,rp"]="r BOOLO BOOLO or UOP BOOLA" - ["74,cl"]="r BOOLA BOOLA and UOP BOOL" ["74,or"]="r BOOLA BOOLA and UOP BOOL" - ["74,and"]="r BOOLA BOOLA and UOP BOOL" ["74,rp"]="r BOOLA BOOLA and UOP BOOL" - ["75,ld"]="s 9" ["75,lp"]="s 21" ["75,ex"]="s 20" ["75,str"]="s 10" - ["75,STMT"]="24" ["75,IF"]="3" ["75,FORIN"]="4" ["75,INCLUDE"]="5" - ["75,BUILTIN"]="6" ["75,VAR"]="7" ["75,STR"]="8" ["75,BOOLS"]="81" - ["75,BOOLO"]="17" ["75,UOP"]="18" ["75,BOOLA"]="19" ["75,BOOL"]="22" - ["75,ARGS"]="23" ["76,rp"]="s 82" ["77,ld"]="r DOC" ["77,rd"]="r DOC" - ["77,str"]="r DOC" ["77,DOC"]="83" ["78,rd"]="s 84" ["79,ld"]="s 9" - ["79,lp"]="s 21" ["79,ex"]="s 20" ["79,str"]="s 10" ["79,STMT"]="24" - ["79,IF"]="3" ["79,FORIN"]="4" ["79,INCLUDE"]="5" ["79,BUILTIN"]="6" - ["79,VAR"]="7" ["79,STR"]="8" ["79,BOOLS"]="85" ["79,BOOLO"]="17" - ["79,UOP"]="18" ["79,BOOLA"]="19" ["79,BOOL"]="22" ["79,ARGS"]="23" - ["80,cl"]="s 86" ["81,rp"]="s 87" ["82,cl"]="r BOOLA BOOLA and lp BOOLS rp" - ["82,or"]="r BOOLA BOOLA and lp BOOLS rp" - ["82,and"]="r BOOLA BOOLA and lp BOOLS rp" - ["82,rp"]="r BOOLA BOOLA and lp BOOLS rp" ["83,ld"]="s 9" ["83,rd"]="s 88" - ["83,str"]="s 10" ["83,STMT"]="2" ["83,IF"]="3" ["83,FORIN"]="4" - ["83,INCLUDE"]="5" ["83,BUILTIN"]="6" ["83,VAR"]="7" ["83,STR"]="8" - ["84,ld"]="r IF ld if BOOLS cl DOC ELIF ELSE rd" - ["84,cl"]="r IF ld if BOOLS cl DOC ELIF ELSE rd" - ["84,rd"]="r IF ld if BOOLS cl DOC ELIF ELSE rd" - ["84,elif"]="r IF ld if BOOLS cl DOC ELIF ELSE rd" - ["84,else"]="r IF ld if BOOLS cl DOC ELIF ELSE rd" - ["84,or"]="r IF ld if BOOLS cl DOC ELIF ELSE rd" - ["84,and"]="r IF ld if BOOLS cl DOC ELIF ELSE rd" - ["84,rp"]="r IF ld if BOOLS cl DOC ELIF ELSE rd" - ["84,ne"]="r IF ld if BOOLS cl DOC ELIF ELSE rd" - ["84,eq"]="r IF ld if BOOLS cl DOC ELIF ELSE rd" - ["84,gt"]="r IF ld if BOOLS cl DOC ELIF ELSE rd" - ["84,lt"]="r IF ld if BOOLS cl DOC ELIF ELSE rd" - ["84,ge"]="r IF ld if BOOLS cl DOC ELIF ELSE rd" - ["84,le"]="r IF ld if BOOLS cl DOC ELIF ELSE rd" - ["84,strgt"]="r IF ld if BOOLS cl DOC ELIF ELSE rd" - ["84,strlt"]="r IF ld if BOOLS cl DOC ELIF ELSE rd" - ["84,streq"]="r IF ld if BOOLS cl DOC ELIF ELSE rd" - ["84,strne"]="r IF ld if BOOLS cl DOC ELIF ELSE rd" - ["84,str"]="r IF ld if BOOLS cl DOC ELIF ELSE rd" - ["84,$"]="r IF ld if BOOLS cl DOC ELIF ELSE rd" ["85,cl"]="s 89" - ["86,ld"]="r DOC" ["86,rd"]="r DOC" ["86,str"]="r DOC" ["86,DOC"]="90" - ["87,cl"]="r BOOLA BOOLA and UOP lp BOOLS rp" - ["87,or"]="r BOOLA BOOLA and UOP lp BOOLS rp" - ["87,and"]="r BOOLA BOOLA and UOP lp BOOLS rp" - ["87,rp"]="r BOOLA BOOLA and UOP lp BOOLS rp" - ["88,ld"]="r FORIN ld for ID in ARGS cl DOC rd" - ["88,cl"]="r FORIN ld for ID in ARGS cl DOC rd" - ["88,rd"]="r FORIN ld for ID in ARGS cl DOC rd" - ["88,elif"]="r FORIN ld for ID in ARGS cl DOC rd" - ["88,else"]="r FORIN ld for ID in ARGS cl DOC rd" - ["88,or"]="r FORIN ld for ID in ARGS cl DOC rd" - ["88,and"]="r FORIN ld for ID in ARGS cl DOC rd" - ["88,rp"]="r FORIN ld for ID in ARGS cl DOC rd" - ["88,ne"]="r FORIN ld for ID in ARGS cl DOC rd" - ["88,eq"]="r FORIN ld for ID in ARGS cl DOC rd" - ["88,gt"]="r FORIN ld for ID in ARGS cl DOC rd" - ["88,lt"]="r FORIN ld for ID in ARGS cl DOC rd" - ["88,ge"]="r FORIN ld for ID in ARGS cl DOC rd" - ["88,le"]="r FORIN ld for ID in ARGS cl DOC rd" - ["88,strgt"]="r FORIN ld for ID in ARGS cl DOC rd" - ["88,strlt"]="r FORIN ld for ID in ARGS cl DOC rd" - ["88,streq"]="r FORIN ld for ID in ARGS cl DOC rd" - ["88,strne"]="r FORIN ld for ID in ARGS cl DOC rd" - ["88,str"]="r FORIN ld for ID in ARGS cl DOC rd" - ["88,$"]="r FORIN ld for ID in ARGS cl DOC rd" ["89,ld"]="r DOC" - ["89,rd"]="r DOC" ["89,elif"]="r DOC" ["89,else"]="r DOC" ["89,str"]="r DOC" - ["89,DOC"]="91" ["90,ld"]="s 9" ["90,rd"]="r ELSE else cl DOC" ["90,str"]="s 10" - ["90,STMT"]="2" ["90,IF"]="3" ["90,FORIN"]="4" ["90,INCLUDE"]="5" - ["90,BUILTIN"]="6" ["90,VAR"]="7" ["90,STR"]="8" ["91,ld"]="s 9" - ["91,rd"]="r ELIF ELIF elif BOOLS cl DOC" - ["91,elif"]="r ELIF ELIF elif BOOLS cl DOC" - ["91,else"]="r ELIF ELIF elif BOOLS cl DOC" ["91,str"]="s 10" ["91,STMT"]="2" - ["91,IF"]="3" ["91,FORIN"]="4" ["91,INCLUDE"]="5" ["91,BUILTIN"]="6" - ["91,VAR"]="7" ["91,STR"]="8" - ) # }}} - # <<< BPT_PARSE_TABLE_E <<< - - # Deduplication function for collect-{var,include} - bpt.__dedup() { echo "$1" | sort | uniq; } - - # Append this if reducer is bpt.__reduce_generate - local HEADER='' - [[ $reduce_fn != bpt.__reduce_generate ]] || { - read -r -d '' HEADER <<-'EOF' -#!/bin/bash -e(){ local OIFS="$IFS"; IFS=; echo -n "$*"; IFS="$OIFS"; }; -len(){ echo -n "${#1}"; }; -seq(){ command seq -s ' ' -- "$@"; }; -EOF - } - - # Execute command - case "$cmd" in - scan) bpt.scan "$ld" "$rd" <"$infile" ;; - fingerprint) bpt.fingerprint "$ld" "$rd" "$infile" "$debug" ;; - *) result="$(bpt.process "$ld" "$rd" "$reduce_fn" "$infile" "$debug")" && - $post_process "$HEADER$result" ;; - esac -) - -(return 0 2>/dev/null) || bpt.main "$@" -- cgit v1.1