Skip to main content

Appendix A. Implementation Hints (附录A. 实现提示)

关于扩展的规范性章节为了描述清晰,使用单独的扩展过程描述每个操作符。在实际实现中,我们期望使用通用算法从左到右处理表达式,每个操作符的过程仅有微小变化。本非规范性附录描述了一种这样的算法。

初始化一个空结果字符串及其非错误状态。

扫描模板并将字面量复制到结果字符串(如第3.1节所述),直到"{"指示表达式、除"{"之外的非字面量字符指示错误,或模板结束。当它结束时,返回结果字符串及其当前错误或非错误状态。

  • 如果找到表达式,扫描模板到下一个"}"并提取大括号之间的字符。
  • 如果模板在"}"之前结束,则将"{"和提取的字符附加到结果字符串并返回错误状态,指示表达式格式错误。

检查提取表达式的第一个字符以查找操作符。

  • 如果表达式结束(即,是"{}")、找到未知或未实现的操作符,或字符不在varchar集中(第2.3节),则将"{"、提取的表达式和"}"附加到结果字符串,记住结果处于错误状态,然后返回扫描模板的其余部分。
  • 如果找到已知且已实现的操作符,则存储操作符并跳到下一个字符以开始varspec-list。
  • 否则,将操作符存储为NUL(简单字符串扩展)。

使用以下值表根据表达式类型操作符确定处理行为。"first"的条目是如果表达式的任何变量已定义,则首先附加到结果的字符串。"sep"的条目是在任何第二个(或后续)已定义变量扩展之前附加到结果的分隔符。"named"的条目是一个布尔值,指示当未给出explode修饰符时,扩展是否包括变量或键名。"ifemp"的条目是如果其对应值为空时要附加到名称的字符串。"allow"的条目指示在值扩展中允许未编码的字符:(U)表示不在未保留集中的任何字符将被编码;(U+R)表示不在(unreserved / reserved / pct-encoding)联合中的任何字符将被编码;对于这两种情况,每个不允许的字符首先编码为其在UTF-8中的八位字节序列,然后将每个这样的八位字节编码为百分号编码的三元组。

.------------------------------------------------------------------.
| NUL + . / ; ? & # |
|------------------------------------------------------------------|
| first "" "" "." "/" ";" "?" "&" "#" |
| sep "," "," "." "/" ";" "&" "&" "," |
| named false false false false true true true false |
| ifemp "" "" "" "" "" "=" "=" "" |
| allow U U+R U U U U U U+R |
`------------------------------------------------------------------'

记住上表,按如下方式处理变量列表:

对于每个varspec,通过扫描变量列表直到找到不在varname集中的字符或到达表达式结束,从表达式中提取变量名称和可选修饰符。

  • 如果是表达式的结束且varname为空,则返回扫描模板的其余部分。
  • 如果不是表达式的结束且找到的最后一个字符指示修饰符(""或":"),则记住该修饰符。如果是explode(""),扫描下一个字符。如果是prefix(":"),继续扫描接下来的一到四个字符以获取表示为十进制整数的max-length,然后,如果仍不是表达式的结束,扫描下一个字符。
  • 如果不是表达式的结束且找到的最后一个字符不是逗号(","),则将"{"、存储的操作符(如果有)、扫描的varname和修饰符、剩余的表达式和"}"附加到结果字符串,记住结果处于错误状态,然后返回扫描模板的其余部分。

查找扫描的变量名称的值,然后

  • 如果varname未知或对应于具有未定义值的变量(第2.3节),则跳到下一个varspec。
  • 如果这是此表达式的第一个已定义变量,则将此表达式类型的first字符串附加到结果字符串并记住已完成。否则,将sep字符串附加到结果字符串。
  • 如果此变量的值是字符串,则
    • 如果named为true,则使用与字面量相同的编码过程将varname附加到结果字符串,并
      • 如果值为空,则将ifemp字符串附加到结果字符串并跳到下一个varspec;
      • 否则,将"="附加到结果字符串。
    • 如果存在prefix修饰符且prefix长度小于值字符串长度(以Unicode字符数计),则将该数量的字符从值字符串的开头附加到结果字符串,在对不在allow集中的任何字符进行百分号编码之后,同时注意不要分割表示单个Unicode代码点的多八位字节或百分号编码的三元组字符;
    • 否则,在对不在allow集中的任何字符进行百分号编码之后,将值附加到结果字符串。
  • 否则,如果未给出explode修饰符,则
    • 如果named为true,则使用与字面量相同的编码过程将varname附加到结果字符串,并
      • 如果值为空,则将ifemp字符串附加到结果字符串并跳到下一个varspec;
      • 否则,将"="附加到结果字符串;并
    • 如果此变量的值是列表,则将每个已定义的列表成员附加到结果字符串,在对不在allow集中的任何字符进行百分号编码之后,在每个已定义列表成员之间向结果附加逗号(",");
    • 如果此变量的值是关联数组或任何其他形式的配对(name, value)结构,则将每个具有定义值的对作为"name,value"附加到结果字符串,在对不在allow集中的任何字符进行百分号编码之后,在每个已定义对之间向结果附加逗号(",")。
  • 否则,如果给出explode修饰符,则
    • 如果named为true,则对于每个已定义的列表成员或具有定义值的数组(name, value)对,执行:
      • 如果这不是第一个已定义的成员/值,则将sep字符串附加到结果字符串;
      • 如果这是列表,则使用与字面量相同的编码过程将varname附加到结果字符串;
      • 如果这是对,则使用与字面量相同的编码过程将name附加到结果字符串;
      • 如果成员/值为空,则将ifemp字符串附加到结果字符串;否则,在对任何不在allow集中的成员/值字符进行百分号编码之后,将"="和成员/值附加到结果字符串。
    • 否则,如果named为false,则
      • 如果这是列表,则将每个已定义的列表成员附加到结果字符串,在对不在allow集中的任何字符进行百分号编码之后,在每个已定义列表成员之间将sep字符串附加到结果。
      • 如果这是(name, value)对数组,则将每个具有定义值的对作为"name=value"附加到结果字符串,在对不在allow集中的任何字符进行百分号编码之后,在每个已定义对之间将sep字符串附加到结果。

当此表达式的变量列表用尽时,返回扫描模板的其余部分。