April 2008 The following documents the modifications to the haserl parser when --enable-bash-extensions is enabled. The following was written by Scott, and should not be considered as a core part of haserl. (Ask on the mailing list if you need help) -------------------------------------------------------------- Haserl supports four tags by default. These are generic and suitable for use with a great variety of shells. If, like me, you write your scripts with vi (vim) with the syntax highlighting enabled, then you will have noted how badly this looks in the editor. I also prefer the tags to be a bit more intelligent, as I find it makes the source that much more readable. To this end I have added the following tags (again, these are designed with bash in mind, may not work with other sh-type shells, and are not at all supported if you are using LUA/LUAC.) Tag version: Expands to: ------------------------ ------------------------ <%if list %> if [[ list ]] ; then <%elif list %> elif [[ list ]] ; then <%else [comment] %> else [# comment] <%endif [comment] %> fi [# comment] <%case word %> case word in <%when pattern %> pattern) <%otherwise [comment] %> *) : [comment] ;; <%endcase [comment] %> esac [# comment] <%while list %> while list ; do <%endwhile %> done [# comment] <%until list %> until list ; do <%enduntil [comment] %> done [# comment] <%for name in word %> for name in word ; do <%endfor [comment] %> done [# comment] <%unless list %> if [[ ! list ]] ; then <%elun list %> elif [[ ! list ]] ; then <%unelse [comment] %> else [#comment] <%endunless [comment %> fi [# comment] To simplify parsing, and to reduce confusion when reading your source, unique words are used. For example, the use of endwhile, enduntil and endfor instead of done. Also, for clarity I have used endif/endcase instead of fi/esac. ------ UNLESS ------ Note the last command, the unless...endunless. I find it is often more clear to express an unless condition than an if not condition. For example, and to show the streamlining of code these tags provide: <% if [[ ! IsLogged ]] ; then %> something <% fi %> This is the improved (IMHO) version: <%unless IsLogged %> something <%endunless %> Personally, I find no use for "else unless", but I decided to include it for consistency. -------------------- CONDITIONAL COMMANDS -------------------- There is one syntactic problem, concerning the use of the <%if %>, <%while %>, <%until %> and <%unless %> tags. The default action is to create a test-style condition; however, sometime you need to do this: <% if grep "text" file &>/dev/null ; then %> If you simply write this: <%if grep "text" file &>/dev/null %> Then it is expanded to: if [[ grep "text" file &>/dev/null ]] %> That is obviously incorrect. To allow for this the parser provides a bit of syntactic sugar. It checks the first character of the expression list and if it is, then it rewrites the list as a command. For example: <%if |grep "text" file &>/dev/null %> Becomes: if grep "text" file &>/dev/null ; then -------------- CASE STATMENTS -------------- The case statement provided another challenge, namely how to handle the pesky ;; required at the end of each case. I am not a C person, so personally I find the next instance of a case to be sufficient to terminate the first. Given that the shell version of case can accept multiple conditions, there is really no reason to worry with cases falling through as they do in C. In other words, I'm all for ditching the ;; construct altogether. So I did. The enhanced <%case %> tag operates without an explicit ;; being required. It is perhaps easier to show than explain, so here's an example: <%case "$FRUIT" %> <%when apple %> echo "It's an apple!" <%when orange %> echo "It's an orange!" <%otherwise something else %> echo "Not what I expected." <%endcase %> The parser renders this as follows: case "$FRUIT" in "\000\012\004") : ;; apple) echo "It's an apple!" ;; orange) echo "It's an orange!" ;; *) # something else echo "Not what I expected." ;; esac Note the odd first case. The parser inserts this so ensure that each subsequent when/otherwise/endcase can be preceded by ;;. This eliminates the need to remember the ;;, and I believe makes for cleaner code. There is, of course, a small performance penalty for always having to evaluate this extra case. In real life I find this not to be a problem, however, if you are processing inside a loop and expect a large chunk of items, then it is something to be aware of.