// conn_expr/grammar.pest
// Copyright 2022 Matti Hänninen
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
connection_expr = {
SOI ~ (
tunneled_connection_expr
| remote_connection_expr
| local_connection_expr
| host_key_expr
) ~ end
}
// XXX(soija) The first octet of the server's IPv4 address could be get confused
// with the tunnel host's optional port, if it's left out. Therefore
// we need this slightly messy rule.
tunneled_connection_expr = {
( user ~ "@" )? ~ (
addr ~ ":" ~ remote_connection_expr
| addr_and_port ~ ":" ~ remote_connection_expr
)
}
remote_connection_expr = { addr ~ ":" ~ local_connection_expr }
local_connection_expr = { port_set }
host_key_expr = { host_key_leading_char ~ host_key_char* }
host_key_leading_char = _{ ASCII_ALPHA }
host_key_char = _{ ASCII_ALPHANUMERIC | "-" | "_" }
user = { user_char+ }
user_char = _{
ASCII_ALPHANUMERIC
| "!" | "$" | "&" | "'" | "(" | ")" | "*" | "+" | "," | "-" | "." | ";" | "="
| "_" | "~"
}
port_set_expr = { SOI ~ port_set ~ end }
port_set = { ( port_range | port ) ~ ( "," ~ ( port_range | port ) )* }
port_range = { port ~ "-" ~ port }
port = { ASCII_NONZERO_DIGIT ~ ASCII_DIGIT* }
addr_and_port = { addr ~ ":" ~ port_set }
addr_expr = { SOI ~ addr ~ end }
addr = { "[" ~ ipv6_addr ~ "]" | ipv4_addr | domain_addr }
domain_addr = { domain_label ~ ( "." ~ domain_label )* ~ "."? }
domain_label = { ASCII_ALPHA ~ ( "-"* ~ ASCII_ALPHANUMERIC+ )* }
ipv6_addr = @{
( h16 ~ ":" ){6} ~ ls32
| "::" ~ ( h16 ~ ":" ){5} ~ ls32
| h16? ~ "::" ~ ( h16 ~ ":" ){4} ~ ls32
| ( h16 ~ ( ":" ~ h16 ){,1} )? ~ "::" ~ ( h16 ~ ":" ){3} ~ ls32
| ( h16 ~ ( ":" ~ h16 ){,2} )? ~ "::" ~ ( h16 ~ ":" ){2} ~ ls32
| ( h16 ~ ( ":" ~ h16 ){,3} )? ~ "::" ~ h16 ~ ":" ~ ls32
| ( h16 ~ ( ":" ~ h16 ){,4} )? ~ "::" ~ ls32
| ( h16 ~ ( ":" ~ h16 ){,5} )? ~ "::" ~ h16
| ( h16 ~ ( ":" ~ h16 ){,6} )? ~ "::"
}
ls32 = { h16 ~ ":" ~ h16 | ipv4_addr }
h16 = { ASCII_HEX_DIGIT{1,4} }
// XXX(soija) Strictly speaking the trailing zero octets of an IPv4 address are
// optional. However Rust's net::IpAddr has issues with this.
ipv4_addr = @{ dec_octet ~ ( "." ~ dec_octet ){3} }
dec_octet = {
"25" ~ '0'..'5'
| "2" ~ '0'..'4' ~ ASCII_DIGIT
| "1" ~ ASCII_DIGIT ~ ASCII_DIGIT
| '1'..'9' ~ ASCII_DIGIT
| ASCII_DIGIT
}
end = _{ EOI }