///|
/// Maximum simultaneous occurrences per key, capped at two. Either uses max,
/// sequence uses addition. This avoids docopt's exponential branch expansion.
fn cardinalities(tree : Pattern) -> Map[String, Int32] {
let result = Map([])
match tree {
Word(key) => result[key] = 1
Required(xs) | Optional(xs) =>
for x in xs {
for k, n in cardinalities(x) {
result[k] = Int32::min(2, result.get(k).unwrap_or(0) + n)
}
}
Either(xs) =>
for x in xs {
for k, n in cardinalities(x) {
result[k] = Int32::max(result.get(k).unwrap_or(0), n)
}
}
Repeat(x) =>
for k, _ in cardinalities(x) {
result[k] = 2
}
Shortcut => ()
}
result
}
///|
fn is_argument(key : String) -> Bool {
(key.has_prefix("<") && key.has_suffix(">")) ||
(key == key.to_upper() && key != key.to_lower())
}
///|
/// Default result types depend on grammar cardinality, not current argv.
pub fn Parser::defaults(self : Parser) -> Values raise DocoptError {
let values = Map([])
for key, count in cardinalities(self.tree) {
let spec = find_spec(self.specs, key).map(fn(i) { self.specs[i] })
let value = match spec {
Some(s) =>
if s.takes_value {
if count > 1 {
Texts(s.default_value.map(words).unwrap_or([]))
} else {
s.default_value.map(fn(v) { Text(v) }).unwrap_or(Missing)
}
} else if count > 1 {
Count(0)
} else {
Flag(false)
}
None =>
if is_argument(key) {
if count > 1 {
Texts([])
} else {
Missing
}
} else if count > 1 {
Count(0)
} else {
Flag(false)
}
}
values[key] = value
}
Values::new(values)
}