///|
pub fn Parser::arm_type(self : Parser, depth : Int) -> Type raise XdrError {
if self.peek() == "void" {
self.expect("void")
self.expect(";")
TVoid
} else {
let field = self.declaration(depth)
self.expect(";")
field.ty
}
}
///|
pub fn Parser::compound(
self : Parser,
kind : String,
depth : Int,
) -> Type raise XdrError {
if depth > self.schema.limits.max_depth {
raise Invalid(self.offset(), "schema nesting limit")
}
if kind == "enum" {
self.expect("{")
let values : Array[(String, Int)] = []
let seen_values : Map[Int, Bool] = Map([])
while self.peek() != "}" {
let name = self.identifier()
self.expect("=")
let value = self.value()
if seen_values.contains(value) {
raise Invalid(self.offset(), "duplicate enum value")
}
seen_values[value] = true
self.add_constant(name, value)
values.push((name, value))
if self.peek() != "," {
break
}
self.expect(",")
}
self.expect("}")
if values.is_empty() {
raise Invalid(self.offset(), "empty enum")
}
return TEnum(values)
}
if kind == "struct" {
self.expect("{")
let fields : Array[Field] = []
let seen_fields : Map[String, Bool] = Map([])
while self.peek() != "}" {
let field = self.declaration(depth + 1)
self.expect(";")
if seen_fields.contains(field.name) {
raise Invalid(self.offset(), "duplicate struct member")
}
seen_fields[field.name] = true
fields.push(field)
}
self.expect("}")
if fields.is_empty() {
raise Invalid(self.offset(), "empty struct")
}
return TStruct(fields)
}
if kind != "union" {
raise Invalid(self.offset(), "unknown compound type")
}
self.expect("switch")
self.expect("(")
let discriminant = self.declaration(depth + 1)
self.expect(")")
self.expect("{")
let arms : Array[Arm] = []
let seen : Map[Int, Bool] = Map([])
let mut fallback : Type? = None
while self.peek() != "}" {
if self.peek() == "default" {
if fallback is Some(_) {
raise Invalid(self.offset(), "duplicate default arm")
}
self.expect("default")
self.expect(":")
fallback = Some(self.arm_type(depth + 1))
} else {
let labels : Array[Int] = []
while self.peek() == "case" {
self.expect("case")
let label = self.value()
self.expect(":")
if seen.contains(label) {
raise Invalid(self.offset(), "duplicate union case")
}
seen[label] = true
labels.push(label)
}
if labels.is_empty() {
raise Invalid(self.offset(), "expected union case or default")
}
arms.push({ labels, ty: self.arm_type(depth + 1), })
}
}
self.expect("}")
if arms.is_empty() && fallback is None {
raise Invalid(self.offset(), "empty union")
}
TUnion(discriminant.ty, arms, fallback)
}