diff --git a/mo b/mo index e6d729d..c1affcb 100755 --- a/mo +++ b/mo @@ -225,27 +225,34 @@ moCallFunction() { # $2 - Content # $3 - Name of end tag # $4 - If -z, do standalone tag processing before finishing +# $5 - Current context # # Returns nothing. moFindEndTag() { - local content remaining scanned standaloneBytes tag + local content remaining scanned standaloneBytes tag parsed preservedTag #: Find open tags scanned="" moSplit content "$2" '{{' '}}' while [[ "${#content[@]}" -gt 1 ]]; do + preservedTag="${content[1]}" + if [[ ! -z "$5" ]] && [[ "${content[1]}" =~ "(" ]]; then + parsed=$(moParse "${content[1]}" "$5" "$5" true) + content[1]=$parsed + fi + moTrimWhitespace tag "${content[1]}" #: Restore content[1] before we start using it - content[1]='{{'"${content[1]}"'}}' + content[1]='{{'"${preservedTag}"'}}' case $tag in '#'* | '^'*) #: Start another block scanned="${scanned}${content[0]}${content[1]}" moTrimWhitespace tag "${tag:1}" - moFindEndTag content "${content[2]}" "$tag" "loop" + moFindEndTag content "${content[2]}" "$tag" "loop" "$5" scanned="${scanned}${content[0]}${content[1]}" remaining=${content[2]} ;; @@ -291,12 +298,13 @@ moFindEndTag() { } -# Internal: Find the first index of a substring. If not found, sets the -# index to -1. +# Internal: Find the index of a substring. If not found, sets the +# index to -1. Defaults to finding the first index. # # $1 - Destination variable for the index # $2 - Haystack # $3 - Needle +# $4 - Last occurrence of needle # # Returns nothing. moFindString() { @@ -658,56 +666,84 @@ moLoop() { # Internal: Parse a block of text, writing the result to stdout. # -# $1 - Block of text to change +# $1 - Block of text to parse # $2 - Current name (the variable NAME for what {{.}} means) # $3 - true when no content before this, false otherwise +# $4 - true if subexpression # # Returns nothing. moParse() { # Keep naming variables mo* here to not overwrite needed variables # used in the string replacements - local moArgs moBlock moContent moCurrent moIsBeginning moNextIsBeginning moTag moKey + local moArgs moBlock moContent moCurrent moIsBeginning moNextIsBeginning moTag moKey moIsSubExp moOpen moClose moCurrent=$2 moIsBeginning=$3 + moIsSubExp=$4 # Find open tags - moSplit moContent "$1" '{{' '}}' + moOpen='{{' + moClose='}}' + if [[ $moIsSubExp == true ]]; then + moOpen='(' + moClose=')' + moSplit moContent "$1" "$moOpen" "$moClose" + else + moSplit moContent "$1" "$moOpen" "$moClose" + fi + + while [[ "${#moContent[@]}" -gt 1 ]]; do moTrimWhitespace moTag "${moContent[1]}" moNextIsBeginning=false + if [[ ! "$moTag" == ">"* ]] && [[ "${moContent[1]}" =~ "(" ]]; then + moParsed=$(moParse "${moContent[1]}" "$moCurrent" "$moCurrent" true) + moContent[1]="$moParsed" + moTrimWhitespace moTag "${moContent[1]}" + fi + case $moTag in '#'*) # Loop, if/then, or pass content through function # Sets context - moStandaloneAllowed moContent "${moContent[@]}" "$moIsBeginning" moTrimWhitespace moTag "${moTag:1}" - # Split arguments from the tag name. Arguments are passed to - # functions. - moArgs=$moTag - moTag=${moTag%% *} - moTag=${moTag%%$'\t'*} - moArgs=${moArgs:${#moTag}} - moFindEndTag moBlock "$moContent" "$moTag" - moFullTagName moTag "$moCurrent" "$moTag" - - if moTest "$moTag"; then - # Show / loop / pass through function - if moIsFunction "$moTag"; then - moCallFunction moContent "$moTag" "${moBlock[0]}" "$moArgs" - moParse "$moContent" "$moCurrent" false - moContent="${moBlock[2]}" - elif moIsArray "$moTag"; then - eval "moLoop \"\${moBlock[0]}\" \"$moTag\" \"\${!${moTag}[@]}\"" + if [[ $moIsSubExp == true ]]; then + moStandaloneDenied moContent "${moContent[@]}" + if moTest "$moTag"; then + echo -n "true" else - moParse "${moBlock[0]}" "$moCurrent" true + echo -n "false" fi - fi + else + moStandaloneAllowed moContent "${moContent[@]}" "$moIsBeginning" - moContent="${moBlock[2]}" + # Split arguments from the tag name. Arguments are passed to + # functions. + moArgs=$moTag + moTag=${moTag%% *} + moTag=${moTag%%$'\t'*} + moArgs=${moArgs:${#moTag}} + moFindEndTag moBlock "$moContent" "$moTag" "" "$moCurrent" + moFullTagName moTag "$moCurrent" "$moTag" + + if moTest "$moTag"; then + # Show / loop / pass through function + if moIsFunction "$moTag"; then + moCallFunction moContent "$moTag" "${moBlock[0]}" "$moArgs" + moParse "$moContent" "$moCurrent" false + moContent="${moBlock[2]}" + elif moIsArray "$moTag"; then + eval "moLoop \"\${moBlock[0]}\" \"$moTag\" \"\${!${moTag}[@]}\"" + else + moParse "${moBlock[0]}" "$moCurrent" true + fi + fi + + moContent="${moBlock[2]}" + fi ;; '>'*) @@ -725,16 +761,26 @@ moParse() { '^'*) # Display section if named thing does not exist - moStandaloneAllowed moContent "${moContent[@]}" "$moIsBeginning" moTrimWhitespace moTag "${moTag:1}" - moFindEndTag moBlock "$moContent" "$moTag" - moFullTagName moTag "$moCurrent" "$moTag" - if ! moTest "$moTag"; then - moParse "${moBlock[0]}" "$moCurrent" false "$moCurrent" + if [[ $moIsSubExp == true ]]; then + moStandaloneDenied moContent "${moContent[@]}" + if ! moTest "$moTag"; then + echo -n "true" + else + echo -n "false" + fi + else + moStandaloneAllowed moContent "${moContent[@]}" "$moIsBeginning" + moFindEndTag moBlock "$moContent" "$moTag" "" "$moCurrent" + moFullTagName moTag "$moCurrent" "$moTag" + + if ! moTest "$moTag"; then + moParse "${moBlock[0]}" "$moCurrent" false "$moCurrent" + fi + + moContent="${moBlock[2]}" fi - - moContent="${moBlock[2]}" ;; '!'*) @@ -808,7 +854,7 @@ moParse() { esac moIsBeginning=$moNextIsBeginning - moSplit moContent "$moContent" '{{' '}}' + moSplit moContent "$moContent" "$moOpen" "$moClose" done echo -n "${moContent[0]}" @@ -934,28 +980,39 @@ moShow() { # # Returns nothing. moSplit() { - local pos result + local moNext moResult moOpenPos moClosePos moFurthest - result=( "$2" ) - moFindString pos "${result[0]}" "$3" + moResult=( "$2" ) + moClosePos=-1 - if [[ "$pos" -ne -1 ]]; then - # The first delimiter was found - result[1]=${result[0]:$pos + ${#3}} - result[0]=${result[0]:0:$pos} + moFindString moOpenPos "${moResult[0]}" "$3" + if [[ $moOpenPos -gt -1 ]]; then + moResult[1]=${moResult[0]:$moOpenPos + ${#3}} + moResult[0]=${moResult[0]:0:$moOpenPos} + moNext=$moOpenPos if [[ -n "${4-}" ]]; then - moFindString pos "${result[1]}" "$4" + # Is there another open delimiter inside this one? + moFindString moNext "${moResult[1]}" "$3" + moFindString moClosePos "${moResult[1]}" "$4" - if [[ "$pos" -ne -1 ]]; then + while [[ $moNext -gt ${#3} ]] && [[ $moClosePos -gt $moNext ]]; do + moFurthest=$(($moClosePos + ${#4})) + moFindString moNext "${moResult[1]:$moFurthest}" "$3" + moFindString moClosePos "${moResult[1]:$moFurthest}" "$4" + moNext=$(($moNext + $moFurthest)) + moClosePos=$(($moFurthest + $moClosePos)) + done + + if [[ "$moClosePos" -ne -1 ]]; then # The second delimiter was found - result[2]="${result[1]:$pos + ${#4}}" - result[1]="${result[1]:0:$pos}" + moResult[2]="${moResult[1]:$moClosePos + ${#4}}" + moResult[1]="${moResult[1]:0:$moClosePos}" fi fi fi - local "$1" && moIndirectArray "$1" "${result[@]}" + local "$1" && moIndirectArray "$1" "${moResult[@]}" } diff --git a/tests/subexpressions.env b/tests/subexpressions.env new file mode 100644 index 0000000..09543ec --- /dev/null +++ b/tests/subexpressions.env @@ -0,0 +1,21 @@ +x="repo" +i=2 +repo=( "resque" "hub" "rip" ) +quote() { + echo "'${MO_FUNCTION_ARGS[@]}'" +} +double_quote() { + echo "\"${MO_FUNCTION_ARGS[@]}\"" +} +inter_examp() { + args=($MO_FUNCTION_ARGS) + first_t=${args[0]} + second_t=${args[1]} + first_v=${args[2]} + second_v=${args[3]} + + [[ "$first_t" == "true" ]] && echo -n "$first_v " + [[ "$second_t" == "true" ]] && echo -n "$second_v " +} +exists="hello" +#doesnt="foo" diff --git a/tests/subexpressions.expected b/tests/subexpressions.expected new file mode 100644 index 0000000..f2f80ee --- /dev/null +++ b/tests/subexpressions.expected @@ -0,0 +1,9 @@ +Function: 'repo' +Specific Element: rip +Loop: + "'resque'" resque + "'hub'" hub + "'rip'" rip + +hello world +X: repo diff --git a/tests/subexpressions.template b/tests/subexpressions.template new file mode 100644 index 0000000..ff5339c --- /dev/null +++ b/tests/subexpressions.template @@ -0,0 +1,9 @@ +Function: {{quote (x)}} +Specific Element: {{repo.(i)}} +Loop: +{{#repo}} + {{double_quote (quote (.))}} {{.}} +{{/repo}} + +{{inter_examp (#exists) (^doesnt) (exists) world}} +X: {{x}}