case LEX_T_DECREMENT:
ds_put_cstr(s, "--");
break;
+ case LEX_T_COLON:
+ ds_put_char(s, ':');
+ break;
default:
OVS_NOT_REACHED();
}
lex_token_init(token);
token->type = LEX_T_INTEGER;
memset(&token->value, 0, sizeof token->value);
+
+ /* Find the extent of an "integer" token, which can be in decimal or
+ * hexadecimal, or an Ethernet address or IPv4 or IPv6 address, as 'start'
+ * through 'end'.
+ *
+ * Special cases we handle here are:
+ *
+ * - The ellipsis token "..", used as e.g. 123..456. A doubled dot
+ * is never valid syntax as part of an "integer", so we stop if
+ * we encounter two dots in a row.
+ *
+ * - Syntax like 1.2.3.4:1234 to indicate an IPv4 address followed by a
+ * port number should be considered three tokens: 1.2.3.4 : 1234.
+ * The obvious approach is to allow just dots or just colons within a
+ * given integer, but that would disallow IPv4-mapped IPv6 addresses,
+ * e.g. ::ffff:192.0.2.128. However, even in those addresses, a
+ * colon never follows a dot, so we stop if we encounter a colon
+ * after a dot.
+ *
+ * (There is no corresponding way to parse an IPv6 address followed
+ * by a port number: ::1:2:3:4:1234 is unavoidably ambiguous.)
+ */
const char *start = p;
const char *end = start;
- while (isalnum((unsigned char) *end) || *end == ':'
+ bool saw_dot = false;
+ while (isalnum((unsigned char) *end)
+ || (*end == ':' && !saw_dot)
|| (*end == '.' && end[1] != '.')) {
+ if (*end == '.') {
+ saw_dot = true;
+ }
end++;
}
size_t len = end - start;
p = lex_parse_macro(p, token);
break;
+ case ':':
+ if (p[1] != ':') {
+ token->type = LEX_T_COLON;
+ p++;
+ break;
+ }
+ /* IPv6 address beginning with "::". Fall through. */
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
- case ':':
p = lex_parse_integer(p, token);
break;
192.168.0.0/255.0.0.0 => error("Value contains unmasked 1-bits.")
192.168.0.0/32
192.168.0.0/255.255.255.255 => 192.168.0.0/32
+1.2.3.4:5 => 1.2.3.4 : 5
::
::1
00:01:02:03:04:x => error("Invalid numeric constant.")
# Test that operators are tokenized as expected, even without white space.
-(){}[[]]==!=<<=>>=!&&||..,;=<->-- => ( ) { } [[ ]] == != < <= > >= ! && || .. , ; = <-> --
+(){}[[]]==!=<<=>>=!&&||..,;=<->--: => ( ) { } [[ ]] == != < <= > >= ! && || .. , ; = <-> -- :
& => error("`&' is only valid as part of `&&'.")
| => error("`|' is only valid as part of `||'.")
- => error("`-' is only valid as part of `--'.")