///|
/// Pattern matching support for `match`.

///|
fn match_pattern(value : Value, pattern : Pattern) -> Array[(String, Value)]? {
  match pattern {
    Pattern::Wildcard => Some([])
    Pattern::Literal(lit) => {
      let lit_value = match lit {
        Literal::None => Value::None
        Literal::Bool(v) => Value::Bool(v)
        Literal::Int(v) => Value::Int(v)
        Literal::Float(v) => Value::Float(v)
        Literal::Complex(real, imag) => Value::Complex(real, imag)
        Literal::Str(v) => Value::Str(v)
        Literal::Bytes(v) => Value::Bytes(v)
      }
      if eq_value(value, lit_value) {
        Some([])
      } else {
        None
      }
    }
    Pattern::Name(name) => Some([(name, value)])
    Pattern::TypeCapture(type_name~, name~) => {
      fn value_matches_type_name(value : Value, type_name : String) -> Bool {
        fn last_qual_segment(text : String) -> String {
          let mut start = 0
          let chars = text.to_array()
          for i = 0; i < chars.length(); i = i + 1 {
            if chars[i] == '.' {
              start = i + 1
            }
          }
          substring(text, start, text.length()).to_string()
        }

        fn class_matches_name(cls : ClassValue, want : String) -> Bool {
          if cls.name == want {
            return true
          }
          for base in cls.bases {
            match base {
              Value::Class(base_cls) =>
                if class_matches_name(base_cls, want) {
                  return true
                }
              _ => ()
            }
          }
          false
        }

        let want_simple = last_qual_segment(type_name)
        match value {
          Value::Instance(inst) => class_matches_name(inst.class, want_simple)
          _ => type_name_from_value(value) == want_simple
        }
      }

      if value_matches_type_name(value, type_name) {
        if name == "_" {
          Some([])
        } else {
          Some([(name, value)])
        }
      } else {
        None
      }
    }
    Pattern::Tuple(patterns) =>
      match value {
        Value::Tuple(values) | Value::List(values) =>
          if values.length() != patterns.length() {
            None
          } else {
            let bindings : Array[(String, Value)] = []
            for i = 0; i < patterns.length(); i = i + 1 {
              match match_pattern(values[i], patterns[i]) {
                Some(inner) =>
                  for pair in inner {
                    bindings.push(pair)
                  }
                None => return None
              }
            }
            Some(bindings)
          }
        _ => None
      }
  }
}