Appendix A. Implementation Hints
The normative sections on expansion describe each operator with a separate expansion process for the sake of descriptive clarity. In actual implementations, we expect the expressions to be processed left-to-right using a common algorithm that has only minor variations in process per operator. This non-normative appendix describes one such algorithm.
Initialize an empty result string and its non-error state.
Scan the template and copy literals to the result string (as in Section 3.1) until an expression is indicated by a "{", an error is indicated by the presence of a non-literals character other than "{", or the template ends. When it ends, return the result string and its current error or non-error state.
-
If an expression is found, scan the template to the next "}" and extract the characters in between the braces.
-
If the template ends before a "}", then append the "{" and extracted characters to the result string and return with an error status indicating the expression is malformed.
Examine the first character of the extracted expression for an operator.
-
If the expression ended (i.e., is "{}"), an operator is found that is unknown or unimplemented, or the character is not in the varchar set (Section 2.3), then append "{", the extracted expression, and "}" to the result string, remember that the result is in an error state, and then go back to scan the remainder of the template.
-
If a known and implemented operator is found, store the operator and skip to the next character to begin the varspec-list.
-
Otherwise, store the operator as NUL (simple string expansion).
Use the following value table to determine the processing behavior by expression type operator. The entry for "first" is the string to append to the result first if any of the expression's variables are defined. The entry for "sep" is the separator to append to the result before any second (or subsequent) defined variable expansion. The entry for "named" is a boolean for whether or not the expansion includes the variable or key name when no explode modifier is given. The entry for "ifemp" is a string to append to the name if its corresponding value is empty. The entry for "allow" indicates what characters to allow unencoded within the value expansion: (U) means any character not in the unreserved set will be encoded; (U+R) means any character not in the union of (unreserved / reserved / pct-encoding) will be encoded; and, for both cases, each disallowed character is first encoded as its sequence of octets in UTF-8 and then each such octet is encoded as a pct-encoded triplet.
.------------------------------------------------------------------.
| NUL + . / ; ? & # |
|------------------------------------------------------------------|
| first | "" "" "." "/" ";" "?" "&" "#" |
| sep | "," "," "." "/" ";" "&" "&" "," |
| named | false false false false true true true false |
| ifemp | "" "" "" "" "" "=" "=" "" |
| allow | U U+R U U U U U U+R |
`------------------------------------------------------------------'
With the above table in mind, process the variable-list as follows:
For each varspec, extract a variable name and optional modifier from the expression by scanning the variable-list until a character not in the varname set is found or the end of the expression is reached.
-
If it is the end of the expression and the varname is empty, go back to scan the remainder of the template.
-
If it is not the end of the expression and the last character found indicates a modifier ("" or ":"), remember that modifier. If it is an explode (""), scan the next character. If it is a prefix (":"), continue scanning the next one to four characters for the max-length represented as a decimal integer and then, if it is still not the end of the expression, scan the next character.
-
If it is not the end of the expression and the last character found is not a comma (","), append "{", the stored operator (if any), the scanned varname and modifier, the remaining expression, and "}" to the result string, remember that the result is in an error state, and then go back to scan the remainder of the template.
Lookup the value for the scanned variable name, and then
-
If the varname is unknown or corresponds to a variable with an undefined value (Section 2.3), then skip to the next varspec.
-
If this is the first defined variable for this expression, append the first string for this expression type to the result string and remember that it has been done. Otherwise, append the sep string to the result string.
-
If this variable's value is a string, then
-
if named is true, append the varname to the result string using the same encoding process as for literals, and
-
if the value is empty, append the ifemp string to the result string and skip to the next varspec;
-
otherwise, append "=" to the result string.
-
-
if a prefix modifier is present and the prefix length is less than the value string length in number of Unicode characters, append that number of characters from the beginning of the value string to the result string, after pct-encoding any characters that are not in the allow set, while taking care not to split multi-octet or pct-encoded triplet characters that represent a single Unicode code point;
-
otherwise, append the value to the result string after pct-encoding any characters that are not in the allow set.
-
-
else if no explode modifier is given, then
-
if named is true, append the varname to the result string using the same encoding process as for literals, and
-
if the value is empty, append the ifemp string to the result string and skip to the next varspec;
-
otherwise, append "=" to the result string; and
-
-
if this variable's value is a list, append each defined list member to the result string, after pct-encoding any characters that are not in the allow set, with a comma (",") appended to the result between each defined list member;
-
if this variable's value is an associative array or any other form of paired (name, value) structure, append each pair with a defined value to the result string as "name,value", after pct-encoding any characters that are not in the allow set, with a comma (",") appended to the result between each defined pair.
-
-
else if an explode modifier is given, then
-
if named is true, then for each defined list member or array (name, value) pair with a defined value, do:
-
if this is not the first defined member/value, append the sep string to the result string;
-
if this is a list, append the varname to the result string using the same encoding process as for literals;
-
if this is a pair, append the name to the result string using the same encoding process as for literals;
-
if the member/value is empty, append the ifemp string to the result string; otherwise, append "=" and the member/value to the result string, after pct-encoding any member/value characters that are not in the allow set.
-
-
else if named is false, then
-
if this is a list, append each defined list member to the result string, after pct-encoding any characters that are not in the allow set, with the sep string appended to the result between each defined list member.
-
if this is an array of (name, value) pairs, append each pair with a defined value to the result string as "name=value", after pct-encoding any characters that are not in the allow set, with the sep string appended to the result between each defined pair.
-
-
When the variable-list for this expression is exhausted, go back to scan the remainder of the template.