| Update scripts (from donut onwards) are written in a new little |
| scripting language ("edify") that is superficially somewhat similar to |
| the old one ("amend"). This is a brief overview of the new language. |
| |
| - The entire script is a single expression. |
| |
| - All expressions are string-valued. |
| |
| - String literals appear in double quotes. \n, \t, \", and \\ are |
| understood, as are hexadecimal escapes like \x4a. |
| |
| - String literals consisting of only letters, numbers, colons, |
| underscores, slashes, and periods don't need to be in double quotes. |
| |
| - The following words are reserved: |
| |
| if then else endif |
| |
| They have special meaning when unquoted. (In quotes, they are just |
| string literals.) |
| |
| - When used as a boolean, the empty string is "false" and all other |
| strings are "true". |
| |
| - All functions are actually macros (in the Lisp sense); the body of |
| the function can control which (if any) of the arguments are |
| evaluated. This means that functions can act as control |
| structures. |
| |
| - Operators (like "&&" and "||") are just syntactic sugar for builtin |
| functions, so they can act as control structures as well. |
| |
| - ";" is a binary operator; evaluating it just means to first evaluate |
| the left side, then the right. It can also appear after any |
| expression. |
| |
| - Comments start with "#" and run to the end of the line. |
| |
| |
| |
| Some examples: |
| |
| - There's no distinction between quoted and unquoted strings; the |
| quotes are only needed if you want characters like whitespace to |
| appear in the string. The following expressions all evaluate to the |
| same string. |
| |
| "a b" |
| a + " " + b |
| "a" + " " + "b" |
| "a\x20b" |
| a + "\x20b" |
| concat(a, " ", "b") |
| "concat"(a, " ", "b") |
| |
| As shown in the last example, function names are just strings, |
| too. They must be string *literals*, however. This is not legal: |
| |
| ("con" + "cat")(a, " ", b) # syntax error! |
| |
| |
| - The ifelse() builtin takes three arguments: it evaluates exactly |
| one of the second and third, depending on whether the first one is |
| true. There is also some syntactic sugar to make expressions that |
| look like if/else statements: |
| |
| # these are all equivalent |
| ifelse(something(), "yes", "no") |
| if something() then yes else no endif |
| if something() then "yes" else "no" endif |
| |
| The else part is optional. |
| |
| if something() then "yes" endif # if something() is false, |
| # evaluates to false |
| |
| ifelse(condition(), "", abort()) # abort() only called if |
| # condition() is false |
| |
| The last example is equivalent to: |
| |
| assert(condition()) |
| |
| |
| - The && and || operators can be used similarly; they evaluate their |
| second argument only if it's needed to determine the truth of the |
| expression. Their value is the value of the last-evaluated |
| argument: |
| |
| file_exists("/data/system/bad") && delete("/data/system/bad") |
| |
| file_exists("/data/system/missing") || create("/data/system/missing") |
| |
| get_it() || "xxx" # returns value of get_it() if that value is |
| # true, otherwise returns "xxx" |
| |
| |
| - The purpose of ";" is to simulate imperative statements, of course, |
| but the operator can be used anywhere. Its value is the value of |
| its right side: |
| |
| concat(a;b;c, d, e;f) # evaluates to "cdf" |
| |
| A more useful example might be something like: |
| |
| ifelse(condition(), |
| (first_step(); second_step();), # second ; is optional |
| alternative_procedure()) |