Renaming all functions

This makes function names POSIX compliant.  Not a big deal with bash,
but very important if you want to use tools like ctags or tomdoc.sh.
This commit is contained in:
Tyler Akins 2015-10-02 09:06:16 -05:00
parent b16d73b5a7
commit 851e619fbd
2 changed files with 244 additions and 209 deletions

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Test individual functions in mo and make sure they are performing their # Test individual functions in mo and make sure they are performing their
# tasks correctly. This can be used to help determine what needs to get # tasks correctly. This can be used to help determine what needs to get
@ -8,13 +8,14 @@
# Functions are tested from the most primitive to the most advanced. # Functions are tested from the most primitive to the most advanced.
# Errors, once found, halt the program. This way we can more easily # Errors, once found, halt the program. This way we can more easily
# diagnose what's not working and fix those low-level functions first. # diagnose what's not working and fix those low-level functions first.
PARENT_PID=$$ PARENT_PID=$$
cd "$(dirname "$0")" cd "$(dirname "$0")"
rm -f diagnostic.test rm -f diagnostic.test
rm -f diagnostic.partial rm -f diagnostic.partial
# Load mo's functions # Load mo's functions
eval "$(sed '/^mustache-get-content /,$d' < mo)" . ./mo
fail() { fail() {
echo "$1" echo "$1"
@ -24,7 +25,7 @@ fail() {
# No dependencies # No dependencies
echo -n "mustache-indirect ... " echo -n "moIndirect ... "
( (
a() { a() {
local V local V
@ -41,14 +42,14 @@ echo -n "mustache-indirect ... "
} }
c() { c() {
local "$1" && mustache-indirect "$1" c local "$1" && moIndirect "$1" c
} }
[[ "$(a)" != "ca" ]] && fail "Did not assign or scope bled '$RESULT'" [[ "$(a)" != "ca" ]] && fail "Did not assign or scope bled '$RESULT'"
) )
echo "ok" echo "ok"
echo -n "mustache-indirect-array ... " echo -n "moIndirectArray ... "
( (
a() { a() {
local V local V
@ -65,98 +66,98 @@ echo -n "mustache-indirect-array ... "
} }
c() { c() {
local "$1" && mustache-indirect-array "$1" c c c local "$1" && moIndirectArray "$1" c c c
} }
[[ "$(a)" != "31" ]] && fail "Did not assign or scope bled '$RESULT'" [[ "$(a)" != "31" ]] && fail "Did not assign or scope bled '$RESULT'"
) )
echo "ok" echo "ok"
echo -n "mustache-is-array ... " echo -n "moIsArray ... "
( (
TEST=1 TEST=1
mustache-is-array TEST && fail "Wrongly said number was an array" moIsArray TEST && fail "Wrongly said number was an array"
) )
( (
TEST=() TEST=()
mustache-is-array TEST || fail "Wrongly said array was not an array" moIsArray TEST || fail "Wrongly said array was not an array"
) )
( (
TEST="" TEST=""
mustache-is-array TEST && fail "Wrongly said string was an array" moIsArray TEST && fail "Wrongly said string was an array"
) )
( (
unset TEST unset TEST
mustache-is-array TEST && fail "Wrongly said undefined was an array" moIsArray TEST && fail "Wrongly said undefined was an array"
) )
echo "ok" echo "ok"
echo -n "mustache-is-function ... " echo -n "moIsFunction ... "
( (
aa() { echo "hi"; } aa() { echo "hi"; }
mustache-is-function aa || fail "Did not find a function" moIsFunction aa || fail "Did not find a function"
mustache-is-function dd && fail "Wrongly found a function" moIsFunction dd && fail "Wrongly found a function"
) )
echo "ok" echo "ok"
echo -n "mustache-find-string ... " echo -n "moFindString ... "
( (
mustache-find-string POS "abcdefg" "ab" moFindString POS "abcdefg" "ab"
[[ "$POS" == "0" ]] || fail "Did not find at beginning of string" [[ "$POS" == "0" ]] || fail "Did not find at beginning of string"
mustache-find-string POS "abcdefg" "fg" moFindString POS "abcdefg" "fg"
[[ "$POS" == "5" ]] || fail "Did not find at end of string" [[ "$POS" == "5" ]] || fail "Did not find at end of string"
mustache-find-string POS "abcdefg" "de" moFindString POS "abcdefg" "de"
[[ "$POS" == "3" ]] || fail "Did not find at middle of string" [[ "$POS" == "3" ]] || fail "Did not find at middle of string"
mustache-find-string POS "abcdefg" "CD" moFindString POS "abcdefg" "CD"
[[ "$POS" == "-1" ]] || fail "Did not correctly return a miss" [[ "$POS" == "-1" ]] || fail "Did not correctly return a miss"
) )
echo "ok" echo "ok"
echo -n "mustache-full-tag-name ... " echo -n "moFullTagName ... "
( (
mustache-full-tag-name TAG "abc" "def" moFullTagName TAG "abc" "def"
[[ "$TAG" == "abc.def" ]] || fail "Did not work with a context" [[ "$TAG" == "abc.def" ]] || fail "Did not work with a context"
mustache-full-tag-name TAG "" "one" moFullTagName TAG "" "one"
[[ "$TAG" == "one" ]] || fail "Did not work without a context" [[ "$TAG" == "one" ]] || fail "Did not work without a context"
) )
echo "ok" echo "ok"
echo -n "mustache-load-file ... " echo -n "moLoadFile ... "
( (
# TODO - find a way to test reading from stdin that is not painful. # TODO - find a way to test reading from stdin that is not painful.
touch diagnostic.test touch diagnostic.test
mustache-load-file RESULT diagnostic.test moLoadFile RESULT diagnostic.test
[[ "${#RESULT}" == "0" ]] || fail "Did not read from empty file '$RESULT'" [[ "${#RESULT}" == "0" ]] || fail "Did not read from empty file '$RESULT'"
echo "abc" >> diagnostic.test echo "abc" >> diagnostic.test
mustache-load-file RESULT diagnostic.test moLoadFile RESULT diagnostic.test
[[ "${#RESULT}" == "4" ]] || fail "Did not read from file '$RESULT'" [[ "${#RESULT}" == "4" ]] || fail "Did not read from file '$RESULT'"
echo "" >> diagnostic.test echo "" >> diagnostic.test
mustache-load-file RESULT diagnostic.test moLoadFile RESULT diagnostic.test
[[ "${#RESULT}" == "5" ]] || fail "Trimmed newline from file '$RESULT'" [[ "${#RESULT}" == "5" ]] || fail "Trimmed newline from file '$RESULT'"
rm diagnostic.test rm diagnostic.test
) )
echo "ok" echo "ok"
echo -n "mustache-standalone-denied ... " echo -n "moStandaloneDenied ... "
( (
mustache-standalone-denied RESULT before tag after > diagnostic.test moStandaloneDenied RESULT before tag after > diagnostic.test
[[ "$(cat diagnostic.test)" == "before" ]] || fail "Did not output content before tag '$(cat diagnostic.test)'" [[ "$(cat diagnostic.test)" == "before" ]] || fail "Did not output content before tag '$(cat diagnostic.test)'"
[[ "$RESULT" == "after" ]] || fail "Did not set the remaining content '$RESULT'" [[ "$RESULT" == "after" ]] || fail "Did not set the remaining content '$RESULT'"
rm diagnostic.test rm diagnostic.test
) )
echo "ok" echo "ok"
echo -n "mustache-test ... " echo -n "moTest ... "
( (
aa() { echo "hi"; } aa() { echo "hi"; }
bb="bb" bb="bb"
@ -166,47 +167,48 @@ echo -n "mustache-test ... "
unset yy unset yy
zz="" zz=""
mustache-test aa || fail "Did not detect a function" moTest aa || fail "Did not detect a function"
mustache-test bb || fail "Did not detect a non-empty string" moTest bb || fail "Did not detect a non-empty string"
mustache-test cc || fail "Did not detect a number" moTest cc || fail "Did not detect a number"
mustache-test dd || fail "Did not detect a populated array" moTest dd || fail "Did not detect a populated array"
mustache-test xx && fail "Erroneously flagged an empty array" moTest xx && fail "Erroneously flagged an empty array"
mustache-test yy && fail "Erroneously flagged an undefined value" moTest yy && fail "Erroneously flagged an undefined value"
mustache-test zz && fail "Erroneously flagged an empty string" moTest zz && fail "Erroneously flagged an empty string"
) )
echo "ok" echo "ok"
echo -n "mustache-trim-chars ... " echo -n "moTrimChars ... "
( (
mustache-trim-chars RESULT "abcdabc" true true a b c moTrimChars RESULT "abcdabc" true true a b c
[[ "$RESULT" == "d" ]] || fail "Did not trim multiple characters '$RESULT'" [[ "$RESULT" == "d" ]] || fail "Did not trim multiple characters '$RESULT'"
mustache-trim-chars RESULT "abc" true false a c moTrimChars RESULT "abc" true false a c
[[ "$RESULT" == "bc" ]] || fail "Did not trim only from the front '$RESULT'" [[ "$RESULT" == "bc" ]] || fail "Did not trim only from the front '$RESULT'"
mustache-trim-chars RESULT "abc" false true a c moTrimChars RESULT "abc" false true a c
[[ "$RESULT" == "ab" ]] || fail "Did not trim only from the end '$RESULT'" [[ "$RESULT" == "ab" ]] || fail "Did not trim only from the end '$RESULT'"
) )
echo "ok" echo "ok"
echo -n "mustache-get-content ... " echo -n "moGetContent ... "
( (
# TODO - find a way to test reading from stdin that is not painful. # TODO - find a way to test reading from stdin that is not painful.
# Until then, mock it # Until then, mock it
mustache-load-file() { local "$1" && mustache-indirect "$1" "STDIN"; }
mustache-get-content RESULT a moLoadFile() { local "$1" && moIndirect "$1" "STDIN"; }
moGetContent RESULT a
[[ "$RESULT" == "{{>a}}" ]] || fail "Did not construct 1 partial correctly '$RESULT'" [[ "$RESULT" == "{{>a}}" ]] || fail "Did not construct 1 partial correctly '$RESULT'"
mustache-get-content RESULT a b c moGetContent RESULT a b c
[[ "$RESULT" == "{{>a}}{{>b}}{{>c}}" ]] || fail "Did not construct 3 partials correctly '$RESULT'" [[ "$RESULT" == "{{>a}}{{>b}}{{>c}}" ]] || fail "Did not construct 3 partials correctly '$RESULT'"
mustache-get-content RESULT moGetContent RESULT
[[ "$RESULT" == "STDIN" ]] || fail "Did not call mustache-load-file correctly" [[ "$RESULT" == "STDIN" ]] || fail "Did not call moLoadFile correctly"
) )
echo "ok" echo "ok"
echo -n "mustache-indent-lines ... " echo -n "moIndentLines ... "
( (
CR=$'\r' CR=$'\r'
LF=$'\n' LF=$'\n'
@ -214,52 +216,52 @@ echo -n "mustache-indent-lines ... "
# CAUTION # CAUTION
# This must have a dot at the end of the input string # This must have a dot at the end of the input string
# That is part of how mustache-partial calls this function # That is part of how moPartial calls this function
for NEWLINE in "CR" "LF" "CRLF"; do for NEWLINE in "CR" "LF" "CRLF"; do
NL="${!NEWLINE}" NL="${!NEWLINE}"
mustache-indent-lines RESULT "" "has${NL}${NEWLINE}${NL}." moIndentLines RESULT "" "has${NL}${NEWLINE}${NL}."
printf -v QUOTED '%q' "$RESULT" printf -v QUOTED '%q' "$RESULT"
[[ "$RESULT" == "has${NL}${NEWLINE}${NL}" ]] || fail "Should not have changed string $QUOTED" [[ "$RESULT" == "has${NL}${NEWLINE}${NL}" ]] || fail "Should not have changed string $QUOTED"
mustache-indent-lines RESULT "" "without${NL}trailing${NL}${NEWLINE}." moIndentLines RESULT "" "without${NL}trailing${NL}${NEWLINE}."
printf -v QUOTED '%q' "$RESULT" printf -v QUOTED '%q' "$RESULT"
[[ "$RESULT" == "without${NL}trailing${NL}${NEWLINE}" ]] || fail "Should not have changed string $QUOTED" [[ "$RESULT" == "without${NL}trailing${NL}${NEWLINE}" ]] || fail "Should not have changed string $QUOTED"
mustache-indent-lines RESULT "_-_" "has${NL}${NL}${NEWLINE}${NL}." moIndentLines RESULT "_-_" "has${NL}${NL}${NEWLINE}${NL}."
printf -v QUOTED '%q' "$RESULT" printf -v QUOTED '%q' "$RESULT"
[[ "$RESULT" == "_-_has${NL}${NL}_-_${NEWLINE}${NL}" ]] || fail "Should have indented $QUOTED" [[ "$RESULT" == "_-_has${NL}${NL}_-_${NEWLINE}${NL}" ]] || fail "Should have indented $QUOTED"
mustache-indent-lines RESULT "_-_" "without${NL}${NL}trailing${NL}${NEWLINE}." moIndentLines RESULT "_-_" "without${NL}${NL}trailing${NL}${NEWLINE}."
printf -v QUOTED '%q' "$RESULT" printf -v QUOTED '%q' "$RESULT"
[[ "$RESULT" == "_-_without${NL}${NL}_-_trailing${NL}_-_${NEWLINE}" ]] || fail "Should have indented $QUOTED" [[ "$RESULT" == "_-_without${NL}${NL}_-_trailing${NL}_-_${NEWLINE}" ]] || fail "Should have indented $QUOTED"
done done
) )
echo "ok" echo "ok"
echo -n "mustache-is-standalone ... " echo -n "moIsStandalone ... "
( (
CR=$'\r' CR=$'\r'
LF=$'\n' LF=$'\n'
TAB=$'\t' TAB=$'\t'
mustache-is-standalone RESULT "" "" false && fail "Needs newline before or beginning of content flag" moIsStandalone RESULT "" "" false && fail "Needs newline before or beginning of content flag"
mustache-is-standalone RESULT "" "" true || fail "Has beginning of content flag and no other content" moIsStandalone RESULT "" "" true || fail "Has beginning of content flag and no other content"
[[ "$RESULT" == "0 0" ]] || fail "Wrong returned value for no content '$RESULT'" [[ "$RESULT" == "0 0" ]] || fail "Wrong returned value for no content '$RESULT'"
mustache-is-standalone RESULT "moo" "cow" false && fail "Needs newlines when there is content" moIsStandalone RESULT "moo" "cow" false && fail "Needs newlines when there is content"
mustache-is-standalone RESULT "moo$CR$LF$TAB $TAB " " $TAB $TAB$CR${LF}pasture" false || fail "Has newlines and content but did not flag" moIsStandalone RESULT "moo$CR$LF$TAB $TAB " " $TAB $TAB$CR${LF}pasture" false || fail "Has newlines and content but did not flag"
[[ "$RESULT" == "5 6" ]] || fail "Wrong returned value when there was whitespace '$RESULT'" [[ "$RESULT" == "5 6" ]] || fail "Wrong returned value when there was whitespace '$RESULT'"
) )
echo "ok" echo "ok"
echo -n "mustache-split ... " echo -n "moSplit ... "
( (
mustache-split RESULT "abc-def-ghi" "-" moSplit RESULT "abc-def-ghi" "-"
[[ "${#RESULT[@]}" == "2" ]] || fail "Returned wrong number of elements with one delimiter ${#RESULT[@]}" [[ "${#RESULT[@]}" == "2" ]] || fail "Returned wrong number of elements with one delimiter ${#RESULT[@]}"
[[ "${RESULT[0]}" == "abc" ]] || fail "Returned wrong left hand string with one delimiter '${RESULT[0]}'" [[ "${RESULT[0]}" == "abc" ]] || fail "Returned wrong left hand string with one delimiter '${RESULT[0]}'"
[[ "${RESULT[1]}" == "def-ghi" ]] || fail "Returned wrong right hand string with one delimiter '${RESULT[1]}'" [[ "${RESULT[1]}" == "def-ghi" ]] || fail "Returned wrong right hand string with one delimiter '${RESULT[1]}'"
mustache-split RESULT "abc-def-ghi" "-" "g" moSplit RESULT "abc-def-ghi" "-" "g"
[[ "${#RESULT[@]}" == "3" ]] || fail "Returned wrong number of elements with two delimiters ${#RESULT[@]}" [[ "${#RESULT[@]}" == "3" ]] || fail "Returned wrong number of elements with two delimiters ${#RESULT[@]}"
[[ "${RESULT[0]}" == "abc" ]] || fail "Returned wrong left hand string with two delimiters '${RESULT[0]}'" [[ "${RESULT[0]}" == "abc" ]] || fail "Returned wrong left hand string with two delimiters '${RESULT[0]}'"
[[ "${RESULT[1]}" == "def-" ]] || fail "Returned wrong middle string with two delimiters '${RESULT[1]}'" [[ "${RESULT[1]}" == "def-" ]] || fail "Returned wrong middle string with two delimiters '${RESULT[1]}'"
@ -267,34 +269,35 @@ echo -n "mustache-split ... "
) )
echo "ok" echo "ok"
echo -n "mustache-trim-whitespace ... " echo -n "moTrimWhitespace ... "
( (
CR=$'\r' CR=$'\r'
LF=$'\n' LF=$'\n'
TAB=$'\t' TAB=$'\t'
mustache-trim-whitespace RESULT "ab cd" moTrimWhitespace RESULT "ab cd"
printf -v QUOTED '%q' "$RESULT" printf -v QUOTED '%q' "$RESULT"
[[ "${RESULT}" == "ab cd" ]] || fail "Trimmed a string that did not need trimming $QUOTED" [[ "${RESULT}" == "ab cd" ]] || fail "Trimmed a string that did not need trimming $QUOTED"
mustache-trim-whitespace RESULT "$CR$LF$TAB ab $CR$LF$TAB cd $CR$LF$TAB $CR$LF$TAB" moTrimWhitespace RESULT "$CR$LF$TAB ab $CR$LF$TAB cd $CR$LF$TAB $CR$LF$TAB"
printf -v QUOTED '%q' "$RESULT" printf -v QUOTED '%q' "$RESULT"
[[ "${RESULT}" == "ab $CR$LF$TAB cd" ]] || fail "Did not fully trim a string $QUOTED" [[ "${RESULT}" == "ab $CR$LF$TAB cd" ]] || fail "Did not fully trim a string $QUOTED"
) )
echo "ok" echo "ok"
echo -n "mustache-standalone-allowed ... " echo -n "moStandaloneAllowed ... "
( (
# Mock mustache-is-standalone to make things simpler # Mock moIsStandalone to make things simpler
mustache-is-standalone() { return 1; }
mustache-standalone-allowed RESULT before tag after > diagnostic.test moIsStandalone() { return 1; }
moStandaloneAllowed RESULT before tag after > diagnostic.test
[[ "$(cat diagnostic.test)" == "before" ]] || fail "Did not output content before tag when not standalone '$(cat diagnostic.test)'" [[ "$(cat diagnostic.test)" == "before" ]] || fail "Did not output content before tag when not standalone '$(cat diagnostic.test)'"
[[ "$RESULT" == "after" ]] || fail "Did not set the remaining content when not standalone '$RESULT'" [[ "$RESULT" == "after" ]] || fail "Did not set the remaining content when not standalone '$RESULT'"
mustache-is-standalone() { local "$1" && mustache-indirect "$1" "3 5"; return 0; } moIsStandalone() { local "$1" && moIndirect "$1" "3 5"; return 0; }
mustache-standalone-allowed RESULT before tag afterwards > diagnostic.test moStandaloneAllowed RESULT before tag afterwards > diagnostic.test
[[ "$(cat diagnostic.test)" == "bef" ]] || fail "Did not output content before tag when standalone '$(cat diagnostic.test)'" [[ "$(cat diagnostic.test)" == "bef" ]] || fail "Did not output content before tag when standalone '$(cat diagnostic.test)'"
[[ "$RESULT" == "wards" ]] || fail "Did not set the remaining content when standalone '$RESULT'" [[ "$RESULT" == "wards" ]] || fail "Did not set the remaining content when standalone '$RESULT'"
@ -302,29 +305,29 @@ echo -n "mustache-standalone-allowed ... "
) )
echo "ok" echo "ok"
echo -n "mustache-find-end-tag ... " echo -n "moFindEndTag ... "
( (
LF=$'\n' LF=$'\n'
mustache-find-end-tag RESULT "moo{{ / cow }}pasture" "cow" moFindEndTag RESULT "moo{{ / cow }}pasture" "cow"
[[ "${#RESULT[@]}" == "3" ]] || fail "(simple) Wrong number of elements in the array ${#RESULT[@]}" [[ "${#RESULT[@]}" == "3" ]] || fail "(simple) Wrong number of elements in the array ${#RESULT[@]}"
[[ "${RESULT[0]}" == "moo" ]] || fail "(simple) Wrong left-hand '${RESULT[0]}'" [[ "${RESULT[0]}" == "moo" ]] || fail "(simple) Wrong left-hand '${RESULT[0]}'"
[[ "${RESULT[1]}" == "{{ / cow }}" ]] || fail "(simple) Wrong middle '${RESULT[1]}'" [[ "${RESULT[1]}" == "{{ / cow }}" ]] || fail "(simple) Wrong middle '${RESULT[1]}'"
[[ "${RESULT[2]}" == "pasture" ]] || fail "(simple) Wrong right-hand '${RESULT[2]}'" [[ "${RESULT[2]}" == "pasture" ]] || fail "(simple) Wrong right-hand '${RESULT[2]}'"
mustache-find-end-tag RESULT "moo$LF {{/cow}} $LF pasture" "cow" moFindEndTag RESULT "moo$LF {{/cow}} $LF pasture" "cow"
[[ "${#RESULT[@]}" == "3" ]] || fail "(standalone) Wrong number of elements in the array ${#RESULT[@]}" [[ "${#RESULT[@]}" == "3" ]] || fail "(standalone) Wrong number of elements in the array ${#RESULT[@]}"
[[ "${RESULT[0]}" == "moo$LF" ]] || fail "(standalone) Wrong left-hand '${RESULT[0]}'" [[ "${RESULT[0]}" == "moo$LF" ]] || fail "(standalone) Wrong left-hand '${RESULT[0]}'"
[[ "${RESULT[1]}" == " {{/cow}} $LF" ]] || fail "(standalone) Wrong middle '${RESULT[1]}'" [[ "${RESULT[1]}" == " {{/cow}} $LF" ]] || fail "(standalone) Wrong middle '${RESULT[1]}'"
[[ "${RESULT[2]}" == " pasture" ]] || fail "(standalone) Wrong right-hand '${RESULT[2]}'" [[ "${RESULT[2]}" == " pasture" ]] || fail "(standalone) Wrong right-hand '${RESULT[2]}'"
mustache-find-end-tag RESULT "aa{{#bb}}cc{{/bb}}dd{{^bb}}ee{{/bb}}ff{{/bb}}gg" "bb" moFindEndTag RESULT "aa{{#bb}}cc{{/bb}}dd{{^bb}}ee{{/bb}}ff{{/bb}}gg" "bb"
[[ "${#RESULT[@]}" == "3" ]] || fail "(recursive) Wrong number of elements in the array ${#RESULT[@]}" [[ "${#RESULT[@]}" == "3" ]] || fail "(recursive) Wrong number of elements in the array ${#RESULT[@]}"
[[ "${RESULT[0]}" == "aa{{#bb}}cc{{/bb}}dd{{^bb}}ee{{/bb}}ff" ]] || fail "(recursive) Wrong left-hand '${RESULT[0]}'" [[ "${RESULT[0]}" == "aa{{#bb}}cc{{/bb}}dd{{^bb}}ee{{/bb}}ff" ]] || fail "(recursive) Wrong left-hand '${RESULT[0]}'"
[[ "${RESULT[1]}" == "{{/bb}}" ]] || fail "(recursive) Wrong middle '${RESULT[1]}'" [[ "${RESULT[1]}" == "{{/bb}}" ]] || fail "(recursive) Wrong middle '${RESULT[1]}'"
[[ "${RESULT[2]}" == "gg" ]] || fail "(recursive) Wrong right-hand '${RESULT[2]}'" [[ "${RESULT[2]}" == "gg" ]] || fail "(recursive) Wrong right-hand '${RESULT[2]}'"
mustache-find-end-tag RESULT "aa{{#bb}}cc{{/dd}}ee" "dd" moFindEndTag RESULT "aa{{#bb}}cc{{/dd}}ee" "dd"
[[ "${#RESULT[@]}" == "3" ]] || fail "(unbalanced) Wrong number of elements in the array ${#RESULT[@]}" [[ "${#RESULT[@]}" == "3" ]] || fail "(unbalanced) Wrong number of elements in the array ${#RESULT[@]}"
[[ "${RESULT[0]}" == "aa{{#bb}}cc{{/dd}}ee" ]] || fail "(unbalanced) Wrong left-hand '${RESULT[0]}'" [[ "${RESULT[0]}" == "aa{{#bb}}cc{{/dd}}ee" ]] || fail "(unbalanced) Wrong left-hand '${RESULT[0]}'"
[[ "${RESULT[1]}" == "" ]] || fail "(unbalanced) Wrong middle '${RESULT[1]}'" [[ "${RESULT[1]}" == "" ]] || fail "(unbalanced) Wrong middle '${RESULT[1]}'"
@ -332,11 +335,11 @@ echo -n "mustache-find-end-tag ... "
) )
echo "ok" echo "ok"
echo -n "mustache-loop ... " echo -n "moLoop ... "
( (
mustache-parse() { echo "parse[$1] context[$2] flag[$3]"; } moParse() { echo "parse[$1] context[$2] flag[$3]"; }
mustache-loop "content" "prefix" a b c > diagnostic.test moLoop "content" "prefix" a b c > diagnostic.test
( (
echo "parse[content] context[prefix.a] flag[false]" echo "parse[content] context[prefix.a] flag[false]"
echo "parse[content] context[prefix.b] flag[false]" echo "parse[content] context[prefix.b] flag[false]"
@ -347,18 +350,18 @@ echo -n "mustache-loop ... "
) )
echo "ok" echo "ok"
echo -n "mustache-partial ... " echo -n "moPartial ... "
( (
NL=$'\n' NL=$'\n'
mustache-parse() { echo "parse[$1] context[$2] flag[$3]"; } moParse() { echo "parse[$1] context[$2] flag[$3]"; }
echo "partial" > diagnostic.partial echo "partial" > diagnostic.partial
mustache-partial RESULT "line$NL" ">diagnostic.partial" " ${NL}line2" false "moo" > diagnostic.test moPartial RESULT "line$NL" ">diagnostic.partial" " ${NL}line2" false "moo" > diagnostic.test
[[ "$RESULT" == "line2" ]] || fail "Did not consume newline for standalone '$RESULT'" [[ "$RESULT" == "line2" ]] || fail "Did not consume newline for standalone '$RESULT'"
printf -v QUOTED '%q' "$(cat diagnostic.test)" printf -v QUOTED '%q' "$(cat diagnostic.test)"
[[ "$(cat diagnostic.test)" == "line${NL}parse[partial${NL}] context[moo] flag[true]" ]] || fail "Did not parse right standalone content $QUOTED" [[ "$(cat diagnostic.test)" == "line${NL}parse[partial${NL}] context[moo] flag[true]" ]] || fail "Did not parse right standalone content $QUOTED"
mustache-partial RESULT "line" ">diagnostic.partial" "line2" false "moo" > diagnostic.test moPartial RESULT "line" ">diagnostic.partial" "line2" false "moo" > diagnostic.test
[[ "$RESULT" == "line2" ]] || fail "Did not preserve content for non-standalone '$RESULT'" [[ "$RESULT" == "line2" ]] || fail "Did not preserve content for non-standalone '$RESULT'"
printf -v QUOTED '%q' "$(cat diagnostic.test)" printf -v QUOTED '%q' "$(cat diagnostic.test)"
[[ "$(cat diagnostic.test)" == "lineparse[partial${NL}] context[moo] flag[true]" ]] || fail "Did not parse right non-standalone content $QUOTED" [[ "$(cat diagnostic.test)" == "lineparse[partial${NL}] context[moo] flag[true]" ]] || fail "Did not parse right non-standalone content $QUOTED"
@ -367,19 +370,19 @@ echo -n "mustache-partial ... "
) )
echo "ok" echo "ok"
echo -n "mustache-show ... " echo -n "moShow ... "
( (
aa() { echo "this is aa"; } aa() { echo "this is aa"; }
bb="BB" bb="BB"
cc=( zero one two ) cc=( zero one two )
[[ "$(mustache-show aa)" == "this is aa" ]] || fail "Did not load function" [[ "$(moShow aa)" == "this is aa" ]] || fail "Did not load function"
[[ "$(mustache-show bb)" == "BB" ]] || fail "Did not show variable" [[ "$(moShow bb)" == "BB" ]] || fail "Did not show variable"
[[ "$(mustache-show cc.1)" == "one" ]] || fail "Did not show value from indexed array" [[ "$(moShow cc.1)" == "one" ]] || fail "Did not show value from indexed array"
) )
echo "ok" echo "ok"
echo -n "mustache-parse ... skipped (tested by specs)" echo -n "moParse ... skipped (tested by specs)"
echo "" echo ""
echo "All diagnostic tests pass" echo "All diagnostic tests pass"

278
mo
View File

@ -1,9 +1,13 @@
#!/bin/bash #!/usr/bin/env bash
# #
# Mo is a mustache template rendering software written in bash. It inserts #/ Mo is a mustache template rendering software written in bash. It inserts
# environment variables into templates. #/ environment variables into templates.
# #/
# Learn more about mustache templates at https://mustache.github.io/ #/ Simply put, mo will change {{VARIABLE}} into the value of that
#/ environment variable. You can use {{#VARIABLE}}content{{/VARIABLE}} to
#/ conditionally display content or iterate over the values of an array.
#/
#/ Learn more about mustache templates at https://mustache.github.io/
# #
# Mo is under a MIT style licence with an additional non-advertising clause. # Mo is under a MIT style licence with an additional non-advertising clause.
# See LICENSE.md for the full text. # See LICENSE.md for the full text.
@ -26,15 +30,15 @@
# $2: Content # $2: Content
# $3: Name of end tag # $3: Name of end tag
# $4: If -z, do standalone tag processing before finishing # $4: If -z, do standalone tag processing before finishing
mustache-find-end-tag() { moFindEndTag() {
local CONTENT SCANNED local CONTENT SCANNED
# Find open tags # Find open tags
SCANNED="" SCANNED=""
mustache-split CONTENT "$2" '{{' '}}' moSplit CONTENT "$2" '{{' '}}'
while [[ "${#CONTENT[@]}" -gt 1 ]]; do while [[ "${#CONTENT[@]}" -gt 1 ]]; do
mustache-trim-whitespace TAG "${CONTENT[1]}" moTrimWhitespace TAG "${CONTENT[1]}"
# Restore CONTENT[1] before we start using it # Restore CONTENT[1] before we start using it
CONTENT[1]='{{'"${CONTENT[1]}"'}}' CONTENT[1]='{{'"${CONTENT[1]}"'}}'
@ -43,20 +47,20 @@ mustache-find-end-tag() {
'#'* | '^'*) '#'* | '^'*)
# Start another block # Start another block
SCANNED="${SCANNED}${CONTENT[0]}${CONTENT[1]}" SCANNED="${SCANNED}${CONTENT[0]}${CONTENT[1]}"
mustache-trim-whitespace TAG "${TAG:1}" moTrimWhitespace TAG "${TAG:1}"
mustache-find-end-tag CONTENT "${CONTENT[2]}" "$TAG" "loop" moFindEndTag CONTENT "${CONTENT[2]}" "$TAG" "loop"
SCANNED="${SCANNED}${CONTENT[0]}${CONTENT[1]}" SCANNED="${SCANNED}${CONTENT[0]}${CONTENT[1]}"
CONTENT=${CONTENT[2]} CONTENT=${CONTENT[2]}
;; ;;
'/'*) '/'*)
# End a block - could be ours # End a block - could be ours
mustache-trim-whitespace TAG "${TAG:1}" moTrimWhitespace TAG "${TAG:1}"
SCANNED="$SCANNED${CONTENT[0]}" SCANNED="$SCANNED${CONTENT[0]}"
if [[ "$TAG" == "$3" ]]; then if [[ "$TAG" == "$3" ]]; then
# Found our end tag # Found our end tag
if [[ -z "$4" ]] && mustache-is-standalone STANDALONE_BYTES "$SCANNED" "${CONTENT[2]}" true; then if [[ -z "$4" ]] && moIsStandalone STANDALONE_BYTES "$SCANNED" "${CONTENT[2]}" true; then
# This is also a standalone tag - clean up whitespace # This is also a standalone tag - clean up whitespace
# and move those whitespace bytes to the "tag" element # and move those whitespace bytes to the "tag" element
STANDALONE_BYTES=( $STANDALONE_BYTES ) STANDALONE_BYTES=( $STANDALONE_BYTES )
@ -65,7 +69,7 @@ mustache-find-end-tag() {
CONTENT[2]="${CONTENT[2]:${STANDALONE_BYTES[1]}}" CONTENT[2]="${CONTENT[2]:${STANDALONE_BYTES[1]}}"
fi fi
local "$1" && mustache-indirect-array "$1" "$SCANNED" "${CONTENT[1]}" "${CONTENT[2]}" local "$1" && moIndirectArray "$1" "$SCANNED" "${CONTENT[1]}" "${CONTENT[2]}"
return 0 return 0
fi fi
@ -80,12 +84,12 @@ mustache-find-end-tag() {
;; ;;
esac esac
mustache-split CONTENT "$CONTENT" '{{' '}}' moSplit CONTENT "$CONTENT" '{{' '}}'
done done
# Did not find our closing tag # Did not find our closing tag
SCANNED="$SCANNED${CONTENT[0]}" SCANNED="$SCANNED${CONTENT[0]}"
local "$1" && mustache-indirect-array "$1" "${SCANNED}" "" "" local "$1" && moIndirectArray "$1" "${SCANNED}" "" ""
} }
@ -95,12 +99,12 @@ mustache-find-end-tag() {
# $1: Destination variable # $1: Destination variable
# $2: Haystack # $2: Haystack
# $3: Needle # $3: Needle
mustache-find-string() { moFindString() {
local POS STRING local POS STRING
STRING=${2%%$3*} STRING=${2%%$3*}
[[ "$STRING" == "$2" ]] && POS=-1 || POS=${#STRING} [[ "$STRING" == "$2" ]] && POS=-1 || POS=${#STRING}
local "$1" && mustache-indirect "$1" $POS local "$1" && moIndirect "$1" $POS
} }
@ -110,11 +114,11 @@ mustache-find-string() {
# $1: Target variable to store results # $1: Target variable to store results
# $2: Context name # $2: Context name
# $3: Desired variable name # $3: Desired variable name
mustache-full-tag-name() { moFullTagName() {
if [[ -z "$2" ]] || [[ "$2" == *.* ]]; then if [[ -z "$2" ]] || [[ "$2" == *.* ]]; then
local "$1" && mustache-indirect "$1" "$3" local "$1" && moIndirect "$1" "$3"
else else
local "$1" && mustache-indirect "$1" "${2}.${3}" local "$1" && moIndirect "$1" "${2}.${3}"
fi fi
} }
@ -125,7 +129,7 @@ mustache-full-tag-name() {
# Parameters: # Parameters:
# $1: Variable name to assign this content back as # $1: Variable name to assign this content back as
# $2-*: File names (optional) # $2-*: File names (optional)
mustache-get-content() { moGetContent() {
local CONTENT FILENAME TARGET local CONTENT FILENAME TARGET
TARGET=$1 TARGET=$1
@ -138,10 +142,10 @@ mustache-get-content() {
CONTENT="$CONTENT"'{{>'"$FILENAME"'}}' CONTENT="$CONTENT"'{{>'"$FILENAME"'}}'
done done
else else
mustache-load-file CONTENT /dev/stdin moLoadFile CONTENT /dev/stdin
fi fi
local "$TARGET" && mustache-indirect "$TARGET" "$CONTENT" local "$TARGET" && moIndirect "$TARGET" "$CONTENT"
} }
@ -152,20 +156,20 @@ mustache-get-content() {
# $1: Name of destination variable to get an array of lines # $1: Name of destination variable to get an array of lines
# $2: The indent string # $2: The indent string
# $3: The string to reindent # $3: The string to reindent
mustache-indent-lines() { moIndentLines() {
local CONTENT FRAGMENT LEN POS_N POS_R RESULT TRIMMED local CONTENT FRAGMENT LEN POS_N POS_R RESULT TRIMMED
RESULT="" RESULT=""
LEN=$((${#3} - 1)) LEN=$((${#3} - 1))
CONTENT="${3:0:$LEN}" # Remove newline and dot from workaround - in mustache-partial CONTENT="${3:0:$LEN}" # Remove newline and dot from workaround - in moPartial
if [ -z "$2" ]; then if [ -z "$2" ]; then
local "$1" && mustache-indirect "$1" "$CONTENT" local "$1" && moIndirect "$1" "$CONTENT"
return 0 return 0
fi fi
mustache-find-string POS_N "$CONTENT" $'\n' moFindString POS_N "$CONTENT" $'\n'
mustache-find-string POS_R "$CONTENT" $'\r' moFindString POS_R "$CONTENT" $'\r'
while [[ "$POS_N" -gt -1 ]] || [[ "$POS_R" -gt -1 ]]; do while [[ "$POS_N" -gt -1 ]] || [[ "$POS_R" -gt -1 ]]; do
if [[ "$POS_N" -gt -1 ]]; then if [[ "$POS_N" -gt -1 ]]; then
@ -176,18 +180,18 @@ mustache-indent-lines() {
CONTENT=${CONTENT:$POS_R + 1} CONTENT=${CONTENT:$POS_R + 1}
fi fi
mustache-trim-chars TRIMMED "$FRAGMENT" false true " " $'\t' $'\n' $'\r' moTrimChars TRIMMED "$FRAGMENT" false true " " $'\t' $'\n' $'\r'
if [ ! -z "$TRIMMED" ]; then if [ ! -z "$TRIMMED" ]; then
FRAGMENT="$2$FRAGMENT" FRAGMENT="$2$FRAGMENT"
fi fi
RESULT="$RESULT$FRAGMENT" RESULT="$RESULT$FRAGMENT"
mustache-find-string POS_N "$CONTENT" $'\n' moFindString POS_N "$CONTENT" $'\n'
mustache-find-string POS_R "$CONTENT" $'\r' moFindString POS_R "$CONTENT" $'\r'
done done
mustache-trim-chars TRIMMED "$CONTENT" false true " " $'\t' moTrimChars TRIMMED "$CONTENT" false true " " $'\t'
if [ ! -z "$TRIMMED" ]; then if [ ! -z "$TRIMMED" ]; then
CONTENT="$2$CONTENT" CONTENT="$2$CONTENT"
@ -195,7 +199,7 @@ mustache-indent-lines() {
RESULT="$RESULT$CONTENT" RESULT="$RESULT$CONTENT"
local "$1" && mustache-indirect "$1" "$RESULT" local "$1" && moIndirect "$1" "$RESULT"
} }
@ -204,7 +208,7 @@ mustache-indent-lines() {
# Parameters: # Parameters:
# $1: Variable name # $1: Variable name
# $2: Value # $2: Value
mustache-indirect() { moIndirect() {
unset -v "$1" unset -v "$1"
printf -v "$1" '%s' "$2" printf -v "$1" '%s' "$2"
} }
@ -215,7 +219,7 @@ mustache-indirect() {
# Parameters: # Parameters:
# $1: Variable name # $1: Variable name
# $2-*: Array elements # $2-*: Array elements
mustache-indirect-array() { moIndirectArray() {
unset -v "$1" unset -v "$1"
eval $1=\(\"\${@:2}\"\) eval $1=\(\"\${@:2}\"\)
} }
@ -228,7 +232,7 @@ mustache-indirect-array() {
# #
# Return code: # Return code:
# 0 if the name is not empty, 1 otherwise # 0 if the name is not empty, 1 otherwise
mustache-is-array() { moIsArray() {
local MUSTACHE_TEST local MUSTACHE_TEST
MUSTACHE_TEST=$(declare -p "$1" 2>/dev/null) || return 1 MUSTACHE_TEST=$(declare -p "$1" 2>/dev/null) || return 1
@ -246,7 +250,7 @@ mustache-is-array() {
# #
# Return code: # Return code:
# 0 if the name is a function, 1 otherwise # 0 if the name is a function, 1 otherwise
mustache-is-function() { moIsFunction() {
local FUNCTIONS NAME local FUNCTIONS NAME
FUNCTIONS=$(declare -F) FUNCTIONS=$(declare -F)
@ -270,7 +274,7 @@ mustache-is-function() {
# string (27) and the number of bytes to trim in the "after" string (10). # string (27) and the number of bytes to trim in the "after" string (10).
# Useful for string manipulation: # Useful for string manipulation:
# #
# mustache-is-standalone RESULT "$before" "$after" false || return 0 # moIsStandalone RESULT "$before" "$after" false || return 0
# RESULT_ARRAY=( $RESULT ) # RESULT_ARRAY=( $RESULT )
# echo "${before:0:${RESULT_ARRAY[0]}}...${after:${RESULT_ARRAY[1]}}" # echo "${before:0:${RESULT_ARRAY[0]}}...${after:${RESULT_ARRAY[1]}}"
# #
@ -279,11 +283,11 @@ mustache-is-function() {
# $2: Content before the tag # $2: Content before the tag
# $3: Content after the tag # $3: Content after the tag
# $4: true/false: is this the beginning of the content? # $4: true/false: is this the beginning of the content?
mustache-is-standalone() { moIsStandalone() {
local AFTER_TRIMMED BEFORE_TRIMMED CHAR local AFTER_TRIMMED BEFORE_TRIMMED CHAR
mustache-trim-chars BEFORE_TRIMMED "$2" false true " " $'\t' moTrimChars BEFORE_TRIMMED "$2" false true " " $'\t'
mustache-trim-chars AFTER_TRIMMED "$3" true false " " $'\t' moTrimChars AFTER_TRIMMED "$3" true false " " $'\t'
CHAR=$((${#BEFORE_TRIMMED} - 1)) CHAR=$((${#BEFORE_TRIMMED} - 1))
CHAR=${BEFORE_TRIMMED:$CHAR} CHAR=${BEFORE_TRIMMED:$CHAR}
@ -303,7 +307,7 @@ mustache-is-standalone() {
CHAR="$CHAR"$'\n' CHAR="$CHAR"$'\n'
fi fi
local "$1" && mustache-indirect "$1" "$((${#BEFORE_TRIMMED})) $((${#3} + ${#CHAR} - ${#AFTER_TRIMMED}))" local "$1" && moIndirect "$1" "$((${#BEFORE_TRIMMED})) $((${#3} + ${#CHAR} - ${#AFTER_TRIMMED}))"
} }
@ -313,7 +317,7 @@ mustache-is-standalone() {
# $1: Variable name to receive the joined content # $1: Variable name to receive the joined content
# $2: Joiner # $2: Joiner
# $3-$*: Elements to join # $3-$*: Elements to join
mustache-join() { moJoin() {
local JOINER PART RESULT TARGET local JOINER PART RESULT TARGET
TARGET=$1 TARGET=$1
@ -325,7 +329,7 @@ mustache-join() {
RESULT="$RESULT$JOINER$PART" RESULT="$RESULT$JOINER$PART"
done done
local "$TARGET" && mustache-indirect "$TARGET" "$RESULT" local "$TARGET" && moIndirect "$TARGET" "$RESULT"
} }
# Read a file # Read a file
@ -333,7 +337,7 @@ mustache-join() {
# Parameters: # Parameters:
# $1: Variable name to receive the file's content # $1: Variable name to receive the file's content
# $2: Filename to load # $2: Filename to load
mustache-load-file() { moLoadFile() {
local CONTENT LEN local CONTENT LEN
# The subshell removes any trailing newlines. We forcibly add # The subshell removes any trailing newlines. We forcibly add
@ -343,7 +347,7 @@ mustache-load-file() {
LEN=$((${#CONTENT} - 1)) LEN=$((${#CONTENT} - 1))
CONTENT=${CONTENT:0:$LEN} # Remove last dot CONTENT=${CONTENT:0:$LEN} # Remove last dot
local "$1" && mustache-indirect "$1" "$CONTENT" local "$1" && moIndirect "$1" "$CONTENT"
} }
@ -353,7 +357,7 @@ mustache-load-file() {
# $1: Content to parse and reparse and reparse # $1: Content to parse and reparse and reparse
# $2: Tag prefix (context name) # $2: Tag prefix (context name)
# $3-*: Names to insert into the parsed content # $3-*: Names to insert into the parsed content
mustache-loop() { moLoop() {
local CONTENT CONTEXT CONTEXT_BASE IGNORE local CONTENT CONTEXT CONTEXT_BASE IGNORE
CONTENT=$1 CONTENT=$1
@ -361,8 +365,8 @@ mustache-loop() {
shift 2 shift 2
while [[ "${#@}" -gt 0 ]]; do while [[ "${#@}" -gt 0 ]]; do
mustache-full-tag-name CONTEXT "$CONTEXT_BASE" "$1" moFullTagName CONTEXT "$CONTEXT_BASE" "$1"
mustache-parse "$CONTENT" "$CONTEXT" false moParse "$CONTENT" "$CONTEXT" false
shift shift
done done
} }
@ -374,7 +378,7 @@ mustache-loop() {
# $1: Block of text to change # $1: Block of text to change
# $2: Current name (the variable NAME for what {{.}} means) # $2: Current name (the variable NAME for what {{.}} means)
# $3: true when no content before this, false otherwise # $3: true when no content before this, false otherwise
mustache-parse() { moParse() {
# Keep naming variables MUSTACHE_* here to not overwrite needed variables # Keep naming variables MUSTACHE_* here to not overwrite needed variables
# used in the string replacements # used in the string replacements
local MUSTACHE_BLOCK MUSTACHE_CONTENT MUSTACHE_CURRENT MUSTACHE_IS_BEGINNING MUSTACHE_TAG local MUSTACHE_BLOCK MUSTACHE_CONTENT MUSTACHE_CURRENT MUSTACHE_IS_BEGINNING MUSTACHE_TAG
@ -383,33 +387,33 @@ mustache-parse() {
MUSTACHE_IS_BEGINNING=$3 MUSTACHE_IS_BEGINNING=$3
# Find open tags # Find open tags
mustache-split MUSTACHE_CONTENT "$1" '{{' '}}' moSplit MUSTACHE_CONTENT "$1" '{{' '}}'
while [[ "${#MUSTACHE_CONTENT[@]}" -gt 1 ]]; do while [[ "${#MUSTACHE_CONTENT[@]}" -gt 1 ]]; do
mustache-trim-whitespace MUSTACHE_TAG "${MUSTACHE_CONTENT[1]}" moTrimWhitespace MUSTACHE_TAG "${MUSTACHE_CONTENT[1]}"
case $MUSTACHE_TAG in case $MUSTACHE_TAG in
'#'*) '#'*)
# Loop, if/then, or pass content through function # Loop, if/then, or pass content through function
# Sets context # Sets context
mustache-standalone-allowed MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" $MUSTACHE_IS_BEGINNING moStandaloneAllowed MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" $MUSTACHE_IS_BEGINNING
mustache-trim-whitespace MUSTACHE_TAG "${MUSTACHE_TAG:1}" moTrimWhitespace MUSTACHE_TAG "${MUSTACHE_TAG:1}"
mustache-find-end-tag MUSTACHE_BLOCK "$MUSTACHE_CONTENT" "$MUSTACHE_TAG" moFindEndTag MUSTACHE_BLOCK "$MUSTACHE_CONTENT" "$MUSTACHE_TAG"
mustache-full-tag-name MUSTACHE_TAG "$MUSTACHE_CURRENT" "$MUSTACHE_TAG" moFullTagName MUSTACHE_TAG "$MUSTACHE_CURRENT" "$MUSTACHE_TAG"
if mustache-test "$MUSTACHE_TAG"; then if moTest "$MUSTACHE_TAG"; then
# Show / loop / pass through function # Show / loop / pass through function
if mustache-is-function "$MUSTACHE_TAG"; then if moIsFunction "$MUSTACHE_TAG"; then
# TODO: Consider piping the output to # TODO: Consider piping the output to
# mustache-get-content so the lambda does not # moGetContent so the lambda does not
# execute in a subshell? # execute in a subshell?
MUSTACHE_CONTENT=$($MUSTACHE_TAG "${MUSTACHE_BLOCK[0]}") MUSTACHE_CONTENT=$($MUSTACHE_TAG "${MUSTACHE_BLOCK[0]}")
mustache-parse "$MUSTACHE_CONTENT" "$MUSTACHE_CURRENT" false moParse "$MUSTACHE_CONTENT" "$MUSTACHE_CURRENT" false
MUSTACHE_CONTENT="${MUSTACHE_BLOCK[2]}" MUSTACHE_CONTENT="${MUSTACHE_BLOCK[2]}"
elif mustache-is-array "$MUSTACHE_TAG"; then elif moIsArray "$MUSTACHE_TAG"; then
eval 'mustache-loop "${MUSTACHE_BLOCK[0]}" "$MUSTACHE_TAG" "${!'"$MUSTACHE_TAG"'[@]}"' eval 'moLoop "${MUSTACHE_BLOCK[0]}" "$MUSTACHE_TAG" "${!'"$MUSTACHE_TAG"'[@]}"'
else else
mustache-parse "${MUSTACHE_BLOCK[0]}" "$MUSTACHE_CURRENT" false moParse "${MUSTACHE_BLOCK[0]}" "$MUSTACHE_CURRENT" false
fi fi
fi fi
@ -418,24 +422,24 @@ mustache-parse() {
'>'*) '>'*)
# Load partial - get name of file relative to cwd # Load partial - get name of file relative to cwd
mustache-partial MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" $MUSTACHE_IS_BEGINNING "$MUSTACHE_CURRENT" moPartial MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" $MUSTACHE_IS_BEGINNING "$MUSTACHE_CURRENT"
;; ;;
'/'*) '/'*)
# Closing tag - If hit in this loop, we simply ignore # Closing tag - If hit in this loop, we simply ignore
# Matching tags are found in mustache-find-end-tag # Matching tags are found in moFindEndTag
mustache-standalone-allowed MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" $MUSTACHE_IS_BEGINNING moStandaloneAllowed MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" $MUSTACHE_IS_BEGINNING
;; ;;
'^'*) '^'*)
# Display section if named thing does not exist # Display section if named thing does not exist
mustache-standalone-allowed MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" $MUSTACHE_IS_BEGINNING moStandaloneAllowed MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" $MUSTACHE_IS_BEGINNING
mustache-trim-whitespace MUSTACHE_TAG "${MUSTACHE_TAG:1}" moTrimWhitespace MUSTACHE_TAG "${MUSTACHE_TAG:1}"
mustache-find-end-tag MUSTACHE_BLOCK "$MUSTACHE_CONTENT" "$MUSTACHE_TAG" moFindEndTag MUSTACHE_BLOCK "$MUSTACHE_CONTENT" "$MUSTACHE_TAG"
mustache-full-tag-name MUSTACHE_TAG "$MUSTACHE_CURRENT" "$MUSTACHE_TAG" moFullTagName MUSTACHE_TAG "$MUSTACHE_CURRENT" "$MUSTACHE_TAG"
if ! mustache-test "$MUSTACHE_TAG"; then if ! moTest "$MUSTACHE_TAG"; then
mustache-parse "${MUSTACHE_BLOCK[0]}" "$MUSTACHE_CURRENT" false "$MUSTACHE_CURRENT" moParse "${MUSTACHE_BLOCK[0]}" "$MUSTACHE_CURRENT" false "$MUSTACHE_CURRENT"
fi fi
MUSTACHE_CONTENT="${MUSTACHE_BLOCK[2]}" MUSTACHE_CONTENT="${MUSTACHE_BLOCK[2]}"
@ -444,53 +448,53 @@ mustache-parse() {
'!'*) '!'*)
# Comment - ignore the tag content entirely # Comment - ignore the tag content entirely
# Trim spaces/tabs before the comment # Trim spaces/tabs before the comment
mustache-standalone-allowed MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" $MUSTACHE_IS_BEGINNING moStandaloneAllowed MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" $MUSTACHE_IS_BEGINNING
;; ;;
.) .)
# Current content (environment variable or function) # Current content (environment variable or function)
mustache-standalone-denied MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" moStandaloneDenied MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}"
mustache-show "$MUSTACHE_CURRENT" "$MUSTACHE_CURRENT" moShow "$MUSTACHE_CURRENT" "$MUSTACHE_CURRENT"
;; ;;
'=') '=')
# Change delimiters # Change delimiters
# Any two non-whitespace sequences separated by whitespace. # Any two non-whitespace sequences separated by whitespace.
# TODO # TODO
mustache-standalone-allowed MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" $MUSTACHE_IS_BEGINNING moStandaloneAllowed MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" $MUSTACHE_IS_BEGINNING
;; ;;
'{'*) '{'*)
# Unescaped - split on }}} not }} # Unescaped - split on }}} not }}
mustache-standalone-denied MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" moStandaloneDenied MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}"
MUSTACHE_CONTENT="${MUSTACHE_TAG:1}"'}}'"$MUSTACHE_CONTENT" MUSTACHE_CONTENT="${MUSTACHE_TAG:1}"'}}'"$MUSTACHE_CONTENT"
mustache-split MUSTACHE_CONTENT "$MUSTACHE_CONTENT" '}}}' moSplit MUSTACHE_CONTENT "$MUSTACHE_CONTENT" '}}}'
mustache-trim-whitespace MUSTACHE_TAG "${MUSTACHE_CONTENT[0]}" moTrimWhitespace MUSTACHE_TAG "${MUSTACHE_CONTENT[0]}"
mustache-full-tag-name MUSTACHE_TAG "$MUSTACHE_CURRENT" "$MUSTACHE_TAG" moFullTagName MUSTACHE_TAG "$MUSTACHE_CURRENT" "$MUSTACHE_TAG"
MUSTACHE_CONTENT=${MUSTACHE_CONTENT[1]} MUSTACHE_CONTENT=${MUSTACHE_CONTENT[1]}
# Now show the value # Now show the value
mustache-show "$MUSTACHE_TAG" "$MUSTACHE_CURRENT" moShow "$MUSTACHE_TAG" "$MUSTACHE_CURRENT"
;; ;;
'&'*) '&'*)
# Unescaped # Unescaped
mustache-standalone-denied MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" moStandaloneDenied MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}"
mustache-trim-whitespace MUSTACHE_TAG "${MUSTACHE_TAG:1}" moTrimWhitespace MUSTACHE_TAG "${MUSTACHE_TAG:1}"
mustache-full-tag-name MUSTACHE_TAG "$MUSTACHE_CURRENT" "$MUSTACHE_TAG" moFullTagName MUSTACHE_TAG "$MUSTACHE_CURRENT" "$MUSTACHE_TAG"
mustache-show "$MUSTACHE_TAG" "$MUSTACHE_CURRENT" moShow "$MUSTACHE_TAG" "$MUSTACHE_CURRENT"
;; ;;
*) *)
# Normal environment variable or function call # Normal environment variable or function call
mustache-standalone-denied MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" moStandaloneDenied MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}"
mustache-full-tag-name MUSTACHE_TAG "$MUSTACHE_CURRENT" "$MUSTACHE_TAG" moFullTagName MUSTACHE_TAG "$MUSTACHE_CURRENT" "$MUSTACHE_TAG"
mustache-show "$MUSTACHE_TAG" "$MUSTACHE_CURRENT" moShow "$MUSTACHE_TAG" "$MUSTACHE_CURRENT"
;; ;;
esac esac
MUSTACHE_IS_BEGINNING=false MUSTACHE_IS_BEGINNING=false
mustache-split MUSTACHE_CONTENT "$MUSTACHE_CONTENT" '{{' '}}' moSplit MUSTACHE_CONTENT "$MUSTACHE_CONTENT" '{{' '}}'
done done
echo -n "${MUSTACHE_CONTENT[0]}" echo -n "${MUSTACHE_CONTENT[0]}"
@ -510,10 +514,10 @@ mustache-parse() {
# $4: Content after the tag # $4: Content after the tag
# $5: true/false: is this the beginning of the content? # $5: true/false: is this the beginning of the content?
# $6: Current context name # $6: Current context name
mustache-partial() { moPartial() {
local MUSTACHE_CONTENT MUSTACHE_FILENAME MUSTACHE_INDENT MUSTACHE_LINE MUSTACHE_PARTIAL MUSTACHE_STANDALONE local MUSTACHE_CONTENT MUSTACHE_FILENAME MUSTACHE_INDENT MUSTACHE_LINE MUSTACHE_PARTIAL MUSTACHE_STANDALONE
if mustache-is-standalone MUSTACHE_STANDALONE "$2" "$4" $5; then if moIsStandalone MUSTACHE_STANDALONE "$2" "$4" $5; then
MUSTACHE_STANDALONE=( $MUSTACHE_STANDALONE ) MUSTACHE_STANDALONE=( $MUSTACHE_STANDALONE )
echo -n "${2:0:${MUSTACHE_STANDALONE[0]}}" echo -n "${2:0:${MUSTACHE_STANDALONE[0]}}"
MUSTACHE_INDENT=${2:${MUSTACHE_STANDALONE[0]}} MUSTACHE_INDENT=${2:${MUSTACHE_STANDALONE[0]}}
@ -524,23 +528,23 @@ mustache-partial() {
MUSTACHE_CONTENT=$4 MUSTACHE_CONTENT=$4
fi fi
mustache-trim-whitespace MUSTACHE_FILENAME "${3:1}" moTrimWhitespace MUSTACHE_FILENAME "${3:1}"
# Execute in subshell to preserve current cwd and environment # Execute in subshell to preserve current cwd and environment
( (
# TODO: Remove dirname and use a function instead # TODO: Remove dirname and use a function instead
cd "$(dirname "$MUSTACHE_FILENAME")" cd "$(dirname "$MUSTACHE_FILENAME")"
mustache-indent-lines MUSTACHE_PARTIAL "$MUSTACHE_INDENT" "$( moIndentLines MUSTACHE_PARTIAL "$MUSTACHE_INDENT" "$(
mustache-load-file MUSTACHE_PARTIAL "${MUSTACHE_FILENAME##*/}" moLoadFile MUSTACHE_PARTIAL "${MUSTACHE_FILENAME##*/}"
# Fix bash handling of subshells # Fix bash handling of subshells
# The extra dot is removed in mustache-indent-lines # The extra dot is removed in moIndentLines
echo -n "${MUSTACHE_PARTIAL}." echo -n "${MUSTACHE_PARTIAL}."
)" )"
mustache-parse "$MUSTACHE_PARTIAL" "$6" true moParse "$MUSTACHE_PARTIAL" "$6" true
) )
local "$1" && mustache-indirect "$1" "$MUSTACHE_CONTENT" local "$1" && moIndirect "$1" "$MUSTACHE_CONTENT"
} }
@ -551,20 +555,20 @@ mustache-partial() {
# Parameters: # Parameters:
# $1: Name of environment variable or function # $1: Name of environment variable or function
# $2: Current context # $2: Current context
mustache-show() { moShow() {
local JOINED MUSTACHE_NAME_PARTS local JOINED MUSTACHE_NAME_PARTS
if mustache-is-function "$1"; then if moIsFunction "$1"; then
CONTENT=$($1 "") CONTENT=$($1 "")
mustache-parse "$CONTENT" "$2" false moParse "$CONTENT" "$2" false
return 0 return 0
fi fi
mustache-split MUSTACHE_NAME_PARTS "$1" "." moSplit MUSTACHE_NAME_PARTS "$1" "."
if [[ -z "${MUSTACHE_NAME_PARTS[1]}" ]]; then if [[ -z "${MUSTACHE_NAME_PARTS[1]}" ]]; then
if mustache-is-array "$1"; then if moIsArray "$1"; then
eval mustache-join JOINED "," "\${$1[@]}" eval moJoin JOINED "," "\${$1[@]}"
echo -n "$JOINED" echo -n "$JOINED"
else else
echo -n "${!1}" echo -n "${!1}"
@ -583,11 +587,11 @@ mustache-show() {
# $2: String to split # $2: String to split
# $3: Starting delimiter # $3: Starting delimiter
# $4: Ending delimiter (optional) # $4: Ending delimiter (optional)
mustache-split() { moSplit() {
local POS RESULT local POS RESULT
RESULT=( "$2" ) RESULT=( "$2" )
mustache-find-string POS "${RESULT[0]}" "$3" moFindString POS "${RESULT[0]}" "$3"
if [[ "$POS" -ne -1 ]]; then if [[ "$POS" -ne -1 ]]; then
# The first delimiter was found # The first delimiter was found
@ -595,7 +599,7 @@ mustache-split() {
RESULT[0]=${RESULT[0]:0:$POS} RESULT[0]=${RESULT[0]:0:$POS}
if [[ ! -z "$4" ]]; then if [[ ! -z "$4" ]]; then
mustache-find-string POS "${RESULT[1]}" "$4" moFindString POS "${RESULT[1]}" "$4"
if [[ "$POS" -ne -1 ]]; then if [[ "$POS" -ne -1 ]]; then
# The second delimiter was found # The second delimiter was found
@ -605,7 +609,7 @@ mustache-split() {
fi fi
fi fi
local "$1" && mustache-indirect-array "$1" "${RESULT[@]}" local "$1" && moIndirectArray "$1" "${RESULT[@]}"
} }
@ -619,16 +623,16 @@ mustache-split() {
# $3: Tag content (not used) # $3: Tag content (not used)
# $4: Content after the tag # $4: Content after the tag
# $5: true/false: is this the beginning of the content? # $5: true/false: is this the beginning of the content?
mustache-standalone-allowed() { moStandaloneAllowed() {
local STANDALONE_BYTES local STANDALONE_BYTES
if mustache-is-standalone STANDALONE_BYTES "$2" "$4" $5; then if moIsStandalone STANDALONE_BYTES "$2" "$4" $5; then
STANDALONE_BYTES=( $STANDALONE_BYTES ) STANDALONE_BYTES=( $STANDALONE_BYTES )
echo -n "${2:0:${STANDALONE_BYTES[0]}}" echo -n "${2:0:${STANDALONE_BYTES[0]}}"
local "$1" && mustache-indirect "$1" "${4:${STANDALONE_BYTES[1]}}" local "$1" && moIndirect "$1" "${4:${STANDALONE_BYTES[1]}}"
else else
echo -n "$2" echo -n "$2"
local "$1" && mustache-indirect "$1" "$4" local "$1" && moIndirect "$1" "$4"
fi fi
} }
@ -641,9 +645,9 @@ mustache-standalone-allowed() {
# $2: Content before the tag that was not yet written # $2: Content before the tag that was not yet written
# $3: Tag content (not used) # $3: Tag content (not used)
# $4: Content after the tag # $4: Content after the tag
mustache-standalone-denied() { moStandaloneDenied() {
echo -n "$2" echo -n "$2"
local "$1" && mustache-indirect "$1" "$4" local "$1" && moIndirect "$1" "$4"
} }
@ -659,11 +663,11 @@ mustache-standalone-denied() {
# #
# Return code: # Return code:
# 0 if the name is not empty, 1 otherwise # 0 if the name is not empty, 1 otherwise
mustache-test() { moTest() {
# Test for functions # Test for functions
mustache-is-function "$1" && return 0 moIsFunction "$1" && return 0
if mustache-is-array "$1"; then if moIsArray "$1"; then
# Arrays must have at least 1 element # Arrays must have at least 1 element
eval '[[ "${#'"$1"'[@]}" -gt 0 ]]' && return 0 eval '[[ "${#'"$1"'[@]}" -gt 0 ]]' && return 0
else else
@ -683,7 +687,7 @@ mustache-test() {
# $3: true/false - trim front? # $3: true/false - trim front?
# $4: true/false - trim end? # $4: true/false - trim end?
# $5-*: Characters to trim # $5-*: Characters to trim
mustache-trim-chars() { moTrimChars() {
local BACK CURRENT FRONT LAST TARGET VAR local BACK CURRENT FRONT LAST TARGET VAR
TARGET=$1 TARGET=$1
@ -705,7 +709,7 @@ mustache-trim-chars() {
done done
done done
local "$TARGET" && mustache-indirect "$TARGET" "$CURRENT" local "$TARGET" && moIndirect "$TARGET" "$CURRENT"
} }
@ -714,21 +718,49 @@ mustache-trim-chars() {
# Parameters: # Parameters:
# $1: Name of variable to store trimmed string # $1: Name of variable to store trimmed string
# $2: The string # $2: The string
mustache-trim-whitespace() { moTrimWhitespace() {
local RESULT local RESULT
mustache-trim-chars RESULT "$2" true true $'\r' $'\n' $'\t' " " moTrimChars RESULT "$2" true true $'\r' $'\n' $'\t' " "
local "$1" && mustache-indirect "$1" "$RESULT" local "$1" && moIndirect "$1" "$RESULT"
} }
# Displays the usage for mo. Pulls this from the file that contained
# the `mo` function. Likely will only work when
#
# $1 - Filename that has the help message
#
# Returns nothing.
moUsage() {
grep '^#/' < "$0" | cut -c 4-
}
# Template parser
#
# $* - Filenames to parse. Can use -h or --help as the only option
# in order to show a help message.
#
# Returns nothing.
mo() ( mo() (
# Execute in a subshell so IFS is reset # Execute in a subshell so IFS is reset
IFS=$' \n\t' IFS=$' \n\t'
mustache-get-content MUSTACHE_CONTENT "$@"
mustache-parse "$MUSTACHE_CONTENT" "" true if [[ $# -gt 0 ]]; then
case "$1" in
-h|--h|--he|--hel|--help)
moUsage "$0"
exit 0
;;
esac
fi
moGetContent MUSTACHE_CONTENT "$@"
moParse "$MUSTACHE_CONTENT" "" true
) )
if [[ "$0" == "$BASH_SOURCE" ]] || ! [[ -n "$BASH_SOURCE" ]]; then if [[ "$0" == "$BASH_SOURCE" ]] || ! [[ -n "$BASH_SOURCE" ]]; then
mo "$@" mo "$@"
fi fi