// AST Parser for Glob patterns.

///|
priv struct Parser {
  tokens : Array[Token]
  mut index : Int
}

///|
/// Converts a flat array of Tokens into a hierarchical AST.
pub fn parse(tokens : Array[Token]) -> Result[AST, GlobError] {
  if tokens.length() == 0 {
    return Err(GlobError::EmptyPattern)
  }
  let parser = Parser::{ tokens, index: 0 }
  match parser.parse_seq() {
    Err(err) => Err(err)
    Ok(ast) =>
      if parser.index == parser.tokens.length() {
        Ok(ast)
      } else {
        match parser.tokens[parser.index] {
          Token::Comma => Err(GlobError::UnexpectedComma)
          Token::RBrace => Err(GlobError::UnexpectedClosingBrace)
          token => Err(GlobError::UnexpectedToken(token_to_string(token)))
        }
      }
  }
}

///|
fn Parser::parse_seq(self : Parser) -> Result[AST, GlobError] {
  let nodes : Array[AST] = []
  while self.index < self.tokens.length() {
    let tok = self.tokens[self.index]
    match tok {
      Token::RBrace => break
      Token::Comma => break
      Token::RBracket => break
      Token::Text(s) => {
        nodes.push(AST::Text(s))
        self.index = self.index + 1
      }
      Token::Question => {
        nodes.push(AST::Question)
        self.index = self.index + 1
      }
      Token::Star => {
        nodes.push(AST::Star)
        self.index = self.index + 1
      }
      Token::GlobStar => {
        nodes.push(AST::GlobStar)
        self.index = self.index + 1
      }
      Token::LBrace =>
        match self.parse_brace() {
          Ok(brace_ast) => nodes.push(brace_ast)
          Err(err) => return Err(err)
        }
      Token::LBracket =>
        match self.parse_bracket() {
          Ok(bracket_ast) => nodes.push(bracket_ast)
          Err(err) => return Err(err)
        }
      _ => {
        nodes.push(AST::Text(token_to_string(tok)))
        self.index = self.index + 1
      }
    }
  }
  if nodes.length() == 1 {
    Ok(nodes[0])
  } else {
    Ok(AST::Seq(nodes))
  }
}

///|
fn Parser::parse_brace(self : Parser) -> Result[AST, GlobError] {
  self.index = self.index + 1 // Consume LBrace
  let options : Array[Array[AST]] = []
  while self.index < self.tokens.length() {
    match self.parse_seq() {
      Ok(opt) =>
        if self.index < self.tokens.length() {
          let tok = self.tokens[self.index]
          if tok == Token::Comma {
            options.push(ast_to_array(opt))
            self.index = self.index + 1
          } else if tok == Token::RBrace {
            options.push(ast_to_array(opt))
            self.index = self.index + 1
            return Ok(AST::Brace(options))
          } else {
            return Err(GlobError::UnclosedBrace)
          }
        } else {
          return Err(GlobError::UnclosedBrace)
        }
      Err(err) => return Err(err)
    }
  }
  Err(GlobError::UnclosedBrace)
}

///|
fn Parser::parse_bracket(self : Parser) -> Result[AST, GlobError] {
  self.index = self.index + 1 // Consume LBracket
  if self.index >= self.tokens.length() {
    return Err(GlobError::UnclosedBracket)
  }

  let negate = if self.tokens[self.index] == Token::Negate {
    self.index = self.index + 1
    true
  } else {
    false
  }

  let elements : Array[CharClassElement] = []
  while self.index < self.tokens.length() {
    let tok = self.tokens[self.index]
    match tok {
      Token::RBracket => {
        self.index = self.index + 1
        return Ok(AST::CharClass(negate, elements))
      }
      Token::Char(c) => {
        elements.push(CharClassElement::Single(c))
        self.index = self.index + 1
      }
      Token::Range(start, end) => {
        if start > end {
          return Err(GlobError::InvalidRange)
        }
        elements.push(CharClassElement::Range(start, end))
        self.index = self.index + 1
      }
      _ => {
        let s = token_to_string(tok)
        let mut j = 0
        while j < s.length() {
          elements.push(
            CharClassElement::Single(s[j].to_int().unsafe_to_char()),
          )
          j = j + 1
        }
        self.index = self.index + 1
      }
    }
  }
  Err(GlobError::UnclosedBracket)
}

///|
fn ast_to_array(ast : AST) -> Array[AST] {
  match ast {
    AST::Seq(arr) => arr
    _ => [ast]
  }
}

///|
fn token_to_string(tok : Token) -> String {
  match tok {
    Token::Text(s) => s
    Token::Question => "?"
    Token::Star => "*"
    Token::GlobStar => "**"
    Token::LBrace => "{"
    Token::RBrace => "}"
    Token::Comma => ","
    Token::LBracket => "["
    Token::RBracket => "]"
    Token::Negate => "!"
    Token::Char(c) => c.to_string()
    Token::Range(start, end) => start.to_string() + "-" + end.to_string()
  }
}