@@ -48,24 +48,34 @@ mdsh-compile() ( # <-- force subshell to prevent escape of compile-time state
4848)
4949__COMPILE__ () {
5050 [[ $1 == fenced && $fence == $' ```' && ! $indent ]] || return 0 # only unindented ``` code
51- local lang=" ${2// [^_[:alnum:]]/ _} " ; # convert language to safe variable/function name
52- local tag_words; mdsh-splitwords " $2 " tag_words; # check for command blocks first
53- if [[ ${tag_words[1]-} == ' !' * ]]; then
54- set -- " $3 " " $2 " " $block_start " ; eval " ${2#*! } " ; return
55- elif [[ ${tag_words[1]-} == ' +' * ]]; then
56- printf ' %s %q\n' " ${2# " ${tag_words[0]} " * +} " " $3 "
57- elif [[ ${tag_words[1]-} == ' |' * ]]; then
58- echo " ${2# " ${tag_words[0]} " * |} <<'\`\`\` '" ; printf $' %s```\n ' " $3 " ; return
59- elif fn-exists " mdsh-lang-$lang " ; then
60- mdsh-rewrite " mdsh-lang-$lang " " {" " } <<'\`\`\` '" ; printf $' %s```\n ' " $3 "
61- elif fn-exists " mdsh-compile-$lang " ; then
62- " mdsh-compile-$lang " " $3 " " $2 " " $block_start "
51+ local mdsh_lang tag_words; mdsh-splitwords " $2 " tag_words; # check for command blocks first
52+ case ${tag_words[1]-} in
53+ ' ' ) mdsh_lang=${tag_words[0]-} ;; # fast exit for common case
54+ ' @' * )
55+ mdsh_lang=${tag_words[1]#@ } ;; # language alias: fall through to function lookup
56+ ' !' * )
57+ mdsh_lang=${tag_words[0]} ; set -- " $3 " " $2 " " $block_start " ; eval " ${2#*! } " ; return
58+ ;;
59+ ' +' * )
60+ printf ' mdsh_lang=%q; %s %q\n' " ${tag_words[0]} " " ${2# " ${tag_words[0]} " * +} " " $3 "
61+ return
62+ ;;
63+ ' |' * )
64+ printf ' mdsh_lang=%q; ' " ${tag_words[0]} "
65+ echo " ${2# " ${tag_words[0]} " * |} <<'\`\`\` '" ; printf $' %s```\n ' " $3 "
66+ return
67+ ;;
68+ * ) mdsh_lang=${2// [^_[:alnum:]]/ _} # convert entire line to safe variable name
69+ esac
70+ if fn-exists " mdsh-lang-$mdsh_lang " ; then
71+ mdsh-rewrite " mdsh-lang-$mdsh_lang " " {" " } <<'\`\`\` '" ; printf $' %s```\n ' " $3 "
72+ elif fn-exists " mdsh-compile-$mdsh_lang " ; then
73+ " mdsh-compile-$mdsh_lang " " $3 " " $2 " " $block_start "
6374 else
6475 mdsh-misc " $2 " " $3 "
6576 fi
66-
67- if fn-exists " mdsh-after-$lang " ; then
68- mdsh-rewrite " mdsh-after-$lang "
77+ if fn-exists " mdsh-after-$mdsh_lang " ; then
78+ mdsh-rewrite " mdsh-after-$mdsh_lang "
6979 fi
7080}
7181# split words in $1 into the array named by $2 (REPLY by default), without wildcard expansion
@@ -234,9 +244,9 @@ run-markdown() {
234244 fi
235245}
236246mdsh_raw_bash_runtime+=($' #!/usr/bin/env bash\n\n # --- BEGIN jqmd runtime ---\n ' )
237- mdsh_raw_bash_runtime+=($'jqmd_imports=\njqmd_filters=\njqmd_defines=\n\nHAVE_FILTERS() { [[ ${jqmd_filters-} ]]; }\nCLEAR_FILTERS() { unset jqmd_filters; JQ_OPTS=(jq); }\n\nIMPORTS() { jqmd_imports+="${jqmd_imports:+$\'\\n\'}$1"; }\nDEFINE() { jqmd_defines+="${jqmd_defines:+$\'\\n\'}$1"; }\nFILTER() {\n\tcase $# in\n\t1) jqmd_filters+="${jqmd_filters:+|}$1"; return ;;\n\t0) return ;;\n\tesac\n\tlocal REPLY ARGS=(printf -v REPLY "$1"); shift\n\tJSON-QUOTE "$@"; ARGS+=("${REPLY[@]}"); "${ARGS[@]}"; FILTER "$REPLY"\n}\n\nJSON-QUOTE() {\n\tset -- "${@//\\\\/\\\\\\\\}"; set -- "${@//\\"/\\\\\\"}" # \\ and "\n\tset -- "${@//$\'\\n\'/\\\\n}"; set -- "${@//$\'\\r\'/\\\\r}"; set -- "${@//$\'\\t\'/\\\\t}" # \\n\\r\\t\n\tset -- "${@/#/\\"}"; set -- "${@/%/\\"}" # leading and trailing \'"\'\n\tREPLY=(); local s r\n\twhile (($#)); do\n\t\ts=${1//[^$\'\\x01\'-$\'\\x1F\']/};\n\t\twhile [[ $s ]]; do\n\t\t\tprintf -v r \\\\\\\\u%04x "\'${s:0:1}"\n\t\t\tset -- "${@//"${s:0:1}"/"$r"}"\n\t\t\ts=${s//"${s:0:1}"/}\n\t\tdone\n\t\tREPLY+=("$1"); shift\n\tdone\n}\n')
238- mdsh_raw_bash_runtime+=($' JQ_OPTS=(jq)\n JQ_OPTS() { JQ_OPTS+=("$@"); }\n ARG() { JQ_OPTS --arg "$1" "$2"; }\n ARGJSON() { JQ_OPTS --argjson "$1" "$2"; }\n ARGQUOTE() { REPLY=JQMD_QA_${#JQ_OPTS[@]}; ARG "$REPLY" "$1"; REPLY=\' $\' $REPLY; }\n ' )
239- mdsh_raw_bash_runtime+=($' JQ_CMD() {\n\t local f= opt nargs cmd=(jq); set -- "${JQ_OPTS[@]:1}" "$@"\n\n\t while (($#)); do\n\t\t case "$1" in\n\t\t -f|--fromfile)\n\t\t\t opt=$(<"$2") || return 69\n\t\t\t FILTER "$opt"; shift 2; continue\n\t\t\t ;;\n\t\t -L|--indent) nargs=2 ;;\n\t\t --arg|--arjgson |--slurpfile|--argfile) nargs=3 ;;\n\t\t --) break ;; # rest of args are data files\n\t\t -*) nargs=1 ;;\n\t\t *) FILTER "$1"; break ;;\t # jq program: data files follow\n\t\t esac\n\t\t cmd+=("${@:1:$nargs}")\t # add $nargs args to cmd\n\t\t shift $nargs\n\t done\n\n\t HAVE_FILTERS || FILTER . # jq needs at least one filter expression\n\t for REPLY in "${jqmd_imports-}" "${jqmd_defines-}" "${jqmd_filters-}"; do\n\t\t [[ $REPLY ]] && f+=${f:+$\'\\ n\' }$REPLY\n\t done\n\n\t REPLY=("${cmd[@]}" "$f" "${@:2}")\n\t CLEAR_FILTERS # cleanup for any re-runs\n }\n\n RUN_JQ() { JQ_CMD "$@" && "${REPLY[@]}"; }\n CALL_JQ() { JQ_CMD "$@" && REPLY=("$("${REPLY[@]}")"); }\n ' )
247+ mdsh_raw_bash_runtime+=($'jqmd_imports=\njqmd_filters=\njqmd_defines=\n\nHAVE_FILTERS() { [[ ${jqmd_filters-} ]]; }\nCLEAR_FILTERS() { unset jqmd_filters; JQ_OPTS=(jq); }\n\nIMPORTS() { jqmd_imports+="${jqmd_imports:+$\'\\n\'}$1"; }\nDEFINE() { jqmd_defines+="${jqmd_defines:+$\'\\n\'}$1"; }\nFILTER() {\n\tcase $# in\n\t1) jqmd_filters+="${jqmd_filters:+$\'\\n\'| }$1"; return ;;\n\t0) return ;;\n\tesac\n\tlocal REPLY ARGS=(printf -v REPLY "$1"); shift\n\tJSON-QUOTE "$@"; ARGS+=("${REPLY[@]}"); "${ARGS[@]}"; FILTER "$REPLY"\n}\n\nAPPLY() {\n\tlocal name filter=\'\' REPLY expr=${1-} lf=$\'\\n\'; shift\n\twhile (($#)); do\n\t\tname=${1#@}; REPLY=${name#*=}\n\t\t[[ $name == *=* ]] || REPLY=${!name}\n\t\tif ((${#REPLY} > 32)); then\n\t\t\tif [[ $1 == @* ]]; then ARGVAL "$REPLY"; else ARGSTR "$REPLY"; fi\n\t\telse\n\t\t\tJSON-QUOTE "$REPLY"; [[ $1 != @* ]] || REPLY="($REPLY|fromjson)"\n\t\tfi\n\t\tfilter+=" | $REPLY as \\$${name%%=*}"; shift\n\tdone\n\tfilter=${filter:3}; [[ ! $expr || $expr == . ]] || filter="( ${filter:+$filter | }$expr )"\n\t${filter:+FILTER "$filter"}\n}\n\nJSON-QUOTE() {\n\tlocal LC_ALL=C\n\tset -- "${@//\\\\/\\\\\\\\}"; set -- "${@//\\"/\\\\\\"}" # \\ and "\n\tset -- "${@//$\'\\n\'/\\\\n}"; set -- "${@//$\'\\r\'/\\\\r}"; set -- "${@//$\'\\t\'/\\\\t}" # \\n\\r\\t\n\tset -- "${@/#/\\"}"; set -- "${@/%/\\"}" # leading and trailing \'"\'\n\tREPLY=(); local s r\n\twhile (($#)); do\n\t\ts=${1//[^$\'\\x01\'-$\'\\x1F\']/};\n\t\twhile [[ $s ]]; do\n\t\t\tprintf -v r \\\\\\\\u%04x "\'${s:0:1}"\n\t\t\tset -- "${@//"${s:0:1}"/"$r"}"\n\t\t\ts=${s//"${s:0:1}"/}\n\t\tdone\n\t\tREPLY+=("$1"); shift\n\tdone\n}\n')
248+ mdsh_raw_bash_runtime+=($' JQ_OPTS=(jq)\n JQ_OPTS() { JQ_OPTS+=("$@"); }\n ARG() { JQ_OPTS --arg "$1" "$2"; }\n ARGJSON() { JQ_OPTS --argjson "$1" "$2"; }\n ARGQUOTE() { ARGSTR "$1"; } # deprecated \n ARGSTR() { REPLY=JQMD_QA_${#JQ_OPTS[@]}; ARG "$REPLY" "$1"; REPLY= \' $ \' $REPLY; } \n ARGVAL() { REPLY=JQMD_JA_${#JQ_OPTS[@]}; ARGJSON "$REPLY" "$1"; REPLY=\' $\' $REPLY; }\n ' )
249+ mdsh_raw_bash_runtime+=($' JQ_CMD() {\n\t local f= opt nargs cmd=(jq); set -- "${JQ_OPTS[@]:1}" "$@"\n\n\t while (($#)); do\n\t\t case "$1" in\n\t\t -f|--fromfile)\n\t\t\t opt=$(<"$2") || return 69\n\t\t\t FILTER "$opt"; shift 2; continue\n\t\t\t ;;\n\t\t -L|--indent) nargs=2 ;;\n\t\t --arg|--argjson |--slurpfile|--argfile) nargs=3 ;;\n\t\t --) break ;; # rest of args are data files\n\t\t -*) nargs=1 ;;\n\t\t *) FILTER "$1"; break ;;\t # jq program: data files follow\n\t\t esac\n\t\t cmd+=("${@:1:$nargs}")\t # add $nargs args to cmd\n\t\t shift $nargs\n\t done\n\n\t HAVE_FILTERS || FILTER . # jq needs at least one filter expression\n\t for REPLY in "${jqmd_imports-}" "${jqmd_defines-}" "${jqmd_filters-}"; do\n\t\t [[ $REPLY ]] && f+=${f:+$\'\\ n\' }$REPLY\n\t done\n\n\t REPLY=("${cmd[@]}" "$f" "${@:2}")\n\t CLEAR_FILTERS # cleanup for any re-runs\n }\n\n RUN_JQ() { JQ_CMD "$@" && "${REPLY[@]}"; }\n CALL_JQ() { JQ_CMD "$@" && REPLY=("$("${REPLY[@]}")"); }\n ' )
240250mdsh_raw_bash_runtime+=($' YAML() { y2j "$1"; JSON "$REPLY"; }\n JSON() { FILTER "jqmd_data($1)" "${@:2}"; }\n ' )
241251mdsh_raw_bash_runtime+=($' DEFINE \'\n def jqmd::blend($other; combine): . as $this | . * $other | . as $combined | with_entries(\n if (.key | in($this)) and (.key | in($other)) then\n .this = $this[.key] | .other = $other[.key] | combine\n else . end\n );\n\n def jqmd::combine: (.this|type) as $this | (.other|type) as $other | .value =\n if $this == "array" then\n if $other == "array" then .this + .other else .this + [.other] end\n elif $this == "object" then\n if $other == "object" then\n .other as $o | (.this | jqmd::blend($o; jqmd::combine))\n else .other end\n else .other end; # everything else just overrides\n\n def jqmd::data($data): {this: ., other:$data} | jqmd::combine | .value ;\n def jqmd_data($data): jqmd::data($data) ;\n\'\n ' )
242252mdsh_raw_bash_runtime+=($' y2j() {\n\t local p j="$(echo "$1" | yaml2json)" || return $?; REPLY=\n\t while [[ $j == *\'\\\\ (\' * ]]; do\n\t\t p=${j%%\'\\\\ (\' *}; j=${j#"$p"\'\\\\ (\' }\n\t\t if [[ $p =~ (^|[^\\\\ ])(\'\\\\\\\\\' )*$ ]]; then\n\t\t\t p="${p}"\'\\ (\' # odd, unbalance the backslash\n\t\t else\n\t\t\t p="${p}(" # even, remove one actual backslash\n\t\t fi\n\t\t REPLY+=$p\n\t done\n\t REPLY+=$j\n }\n ' )
@@ -250,6 +260,17 @@ mdsh-compile-yml() { y2j "$1"; mdsh-compile-json "$REPLY"; }
250260mdsh-compile-yaml () { y2j " $1 " ; mdsh-compile-json " $REPLY " ; }
251261mdsh-compile-json () { mdsh-compile-jq " jqmd_data($1 )" ; }
252262
263+ mdsh-compile-func () {
264+ case ${tag_words-} in
265+ yml|yaml) y2j " $1 " ; REPLY=" jqmd_data($REPLY )" $' \n ' ;;
266+ json* ) REPLY=" jqmd_data($1 )" $' \n ' ;;
267+ jq|javascript|js) REPLY=$1 ;;
268+ * ) mdsh-error " Invalid language for function: '%s'" " ${tag_words-} " ; return
269+ esac
270+ printf ' function %s() {\n\tAPPLY %q \\\n\t\t%s\n}\n' " ${tag_words[2]} " " $REPLY " \
271+ " ${2#* ${tag_words} * ${tag_words[1]} * ${tag_words[2]} } "
272+ }
273+
253274const () {
254275 case " ${tag_words-} " in
255276 yaml|yml) y2j " $block " ; printf ' DEFINE %q\n' " def $1 : $REPLY ;" $' \n ' ;;
0 commit comments