// Port of sqlglot/parser.py: function calls, lambdas, schemas, column definitions,
// constraints, brackets.

///|
pub fn Parser::parse_function_args(
  self : Parser,
  alias? : Bool = false,
) -> Array[Expr] raise SqlglotError {
  self.parse_csv(() => self.parse_lambda(alias~))
}

///|
pub fn Parser::parse_connector_function(
  self : Parser,
  connector : Kind,
) -> Expr? raise SqlglotError {
  let args = self.parse_function_args(alias=false)
  if args.is_empty() {
    self.raise_error("Expected at least one argument")
  }
  Some(mk1(Paren, combine_conditions(args, connector, copy=false)))
}

///|
pub fn Parser::parse_function_call(
  self : Parser,
  functions? : Map[String, FuncBuilder],
  anonymous? : Bool = false,
  optional_parens? : Bool = true,
  any_token? : Bool = false,
) -> Expr? raise SqlglotError {
  match self.fns.hooks.parse_function_call {
    Some(f) => f(self, functions, anonymous, optional_parens, any_token)
    None =>
      self.parse_function_call_base(
        functions?,
        anonymous~,
        optional_parens~,
        any_token~,
      )
  }
}

///|
pub fn Parser::parse_function_call_base(
  self : Parser,
  functions? : Map[String, FuncBuilder],
  anonymous? : Bool = false,
  optional_parens? : Bool = true,
  any_token? : Bool = false,
) -> Expr? raise SqlglotError {
  if !self.curr.ok() {
    return None
  }
  let comments = self.curr.comments
  let prev = self.prev
  let token = self.curr
  let token_type = self.curr.token_type
  let this_text = self.curr.text
  let upper = py_upper(self.curr.text)
  let after_dot = prev.token_type == DOT
  let no_paren_parser = self.fns.no_paren_function_parsers.get(upper)
  match no_paren_parser {
    Some(parser) if optional_parens &&
      !self.cfg.invalid_func_name_tokens.contains(token_type) &&
      !after_dot => {
      self.advance()
      return self.parse_window(parser(self))
    }
    _ => ()
  }
  if self.next.token_type != L_PAREN {
    if optional_parens &&
      self.cfg.no_paren_functions.contains(token_type) &&
      !after_dot {
      self.advance()
      return Some(self.expression(mk0(self.cfg.no_paren_functions[token_type])))
    }
    return None
  }
  if any_token {
    if self.cfg.reserved_tokens.contains(token_type) {
      return None
    }
  } else if !self.cfg.func_tokens.contains(token_type) {
    return None
  }
  self.advance(times=2)
  let func_parser = self.fns.function_parsers.get(upper)
  let mut result : Expr? = None
  match func_parser {
    Some(parser) if !anonymous => result = parser(self)
    _ => {
      match self.cfg.subquery_predicates.get(token_type) {
        Some(subquery_predicate) => {
          let mut expr : Expr? = None
          if self.cfg.subquery_tokens.contains(self.curr.token_type) {
            expr = self.parse_select()
            self.match_r_paren()
          } else if prev.ok() &&
            (prev.token_type == LIKE || prev.token_type == ILIKE) {
            self.advance(times=-1)
            expr = self.parse_bitwise()
          }
          match expr {
            Some(e) =>
              return Some(
                self.expression(mk1(subquery_predicate, e), comments~),
              )
            None => ()
          }
        }
        None => ()
      }
      let functions = match functions {
        Some(f) => f
        None => self.fns.functions
      }
      let function = functions.get(upper)
      let mut known_function = function is Some(_) && !anonymous
      let alias = !known_function ||
        self.cfg.functions_with_aliased_args.contains(upper)
      let mut args = self.parse_function_args(alias~)
      let post_func_comments = if self.curr.ok() {
        Some(self.curr.comments)
      } else {
        None
      }
      match post_func_comments {
        Some(pfc) if known_function && !pfc.is_empty() =>
          if pfc.iter().any(c => c.trim_start().has_prefix(sqlglot_anonymous)) {
            known_function = false
          }
        _ => ()
      }
      if alias && known_function {
        args = self.kv_to_prop_eq(args)
      }
      if known_function {
        let func_builder = function.unwrap()
        let func = func_builder(args, self)
        let func = self.validate_expression(func, nargs=args.length())
        if self.dialect.cfg.preserve_original_names {
          func.get_meta()["name"] = Str(this_text)
        }
        result = Some(func)
      } else {
        let this : Value = if token_type == IDENTIFIER {
          Node(
            mk(Identifier, [("this", this_text), ("quoted", true)]).update_positions_from_token(
              token,
            ),
          )
        } else {
          Str(this_text)
        }
        result = Some(
          self.expression(
            mk(Anonymous, [("this", this), ("expressions", args)]),
          ),
        )
      }
      result = Some(result.unwrap().update_positions_from_token(token))
    }
  }
  match result {
    Some(r) => r.add_comments(Some(comments))
    None => ()
  }
  if func_parser is Some(_) {
    self.match_(R_PAREN, expression?=result) |> ignore
  } else {
    self.match_r_paren(expression?=result)
  }
  self.parse_window(result)
}

///|
pub fn Parser::to_prop_eq(
  self : Parser,
  expression : Expr,
  index : Int,
) -> Expr raise SqlglotError {
  match self.fns.hooks.to_prop_eq {
    Some(f) => f(self, expression, index)
    None => expression
  }
}

///|
pub fn Parser::kv_to_prop_eq(
  self : Parser,
  expressions : Array[Expr],
  parse_map? : Bool = false,
) -> Array[Expr] raise SqlglotError {
  let transformed = []
  for index, e in expressions {
    let mut e = e
    if e.kind.is_any(self.cfg.key_value_definitions) {
      if e.kind.is_a(Alias) {
        e = self.expression(
          mk(PropertyEQ, [("this", e.arg("alias")), ("expression", e.this())]),
        )
      }
      if !e.kind.is_a(PropertyEQ) {
        let this = if parse_map {
          e.this()
        } else {
          match e.this() {
            Some(t) => Some(to_identifier(t.name()))
            None => Some(to_identifier(""))
          }
        }
        e = self.expression(
          mk(PropertyEQ, [("this", this), ("expression", e.expression())]),
        )
      }
      match e.this() {
        Some(t) if t.kind.is_a(Column) => t.replace(t.this()) |> ignore
        _ => ()
      }
    } else {
      e = self.to_prop_eq(e, index)
    }
    transformed.push(e)
  }
  transformed
}

///|
pub fn Parser::parse_function_properties(
  self : Parser,
) -> Expr? raise SqlglotError {
  match self.fns.hooks.parse_function_properties {
    Some(f) => f(self)
    None => self.parse_function_properties_base()
  }
}

///|
pub fn Parser::parse_function_properties_base(
  self : Parser,
) -> Expr? raise SqlglotError {
  let properties = []
  while true {
    let mut keyword = ""
    let prop = if self.match_text_keys(self.fns.property_parsers) {
      keyword = self.prev_upper()
      self.fns.property_parsers[keyword](self, Map([]))
    } else if self.match_(DEFAULT) &&
      self.match_text_keys(self.fns.property_parsers) {
      keyword = self.prev_upper()
      self.fns.property_parsers[keyword](self, { "default": true })
    } else {
      break
    }
    if !prop_value_is_some(prop) {
      self.raise_error("Failed to parse property '\{keyword}'")
      break
    }
    match prop {
      Some(Node(p)) => properties.push(p)
      Some(List(l)) =>
        for x in l {
          match x {
            Node(p) => properties.push(p)
            _ => ()
          }
        }
      _ => ()
    }
  }
  if properties.is_empty() {
    None
  } else {
    Some(self.expression(mk(Properties, [("expressions", properties)])))
  }
}

///|
pub fn Parser::parse_user_defined_function_expression(
  self : Parser,
) -> Expr? raise SqlglotError {
  match self.fns.hooks.parse_user_defined_function_expression {
    Some(f) => f(self)
    None => self.parse_statement()
  }
}

///|
pub fn Parser::parse_function_parameter(
  self : Parser,
) -> Expr? raise SqlglotError {
  match self.fns.hooks.parse_function_parameter {
    Some(f) => f(self)
    None => self.parse_column_def(self.parse_id_var(), computed_column=false)
  }
}

///|
pub fn Parser::parse_user_defined_function(
  self : Parser,
  kind? : TokenType,
) -> Expr? raise SqlglotError {
  match self.fns.hooks.parse_user_defined_function {
    Some(f) => f(self, kind)
    None => self.parse_user_defined_function_base(kind?)
  }
}

///|
pub fn Parser::parse_user_defined_function_base(
  self : Parser,
  kind? : TokenType,
) -> Expr? raise SqlglotError {
  ignore(kind)
  let this = self.parse_table_parts(schema=true)
  if !self.match_(L_PAREN) {
    return this
  }
  let expressions = self.parse_csv(() => self.parse_function_parameter())
  self.match_r_paren()
  Some(
    self.expression(
      mk(UserDefinedFunction, [
        ("this", this),
        ("expressions", expressions),
        ("wrapped", true),
      ]),
    ),
  )
}

///|
pub fn Parser::parse_macro_overloads(
  self : Parser,
  this : Expr,
  first_body : Expr,
  first_is_table : Bool,
) -> Expr? raise SqlglotError {
  let exprs = this.expressions()
  let overloads = [
    self.expression(
      mk(MacroOverload, [
        ("this", first_body),
        ("expressions", if exprs.is_empty() { None } else { Some(exprs) }),
        ("is_table", first_is_table),
      ]),
    ),
  ]
  this.set("expressions", null_arg)
  this.set("wrapped", false)
  while self.match_(COMMA) {
    if !self.match_(L_PAREN) {
      break
    }
    let params = self.parse_csv(() => self.parse_function_parameter())
    self.match_r_paren()
    if !self.match_(ALIAS) {
      break
    }
    let is_table = self.match_(TABLE)
    let body = self.parse_expression()
    let macro_ = mk(MacroOverload, [
      ("this", body),
      ("expressions", params),
      ("is_table", is_table),
    ])
    overloads.push(self.expression(macro_))
  }
  Some(self.expression(mk(MacroOverloads, [("expressions", overloads)])))
}

///|
pub fn Parser::parse_introducer(
  self : Parser,
  token : Token,
) -> Expr? raise SqlglotError {
  match self.parse_primary() {
    Some(literal) =>
      Some(
        self.expression(
          mk(Introducer, [("this", token.text), ("expression", literal)]),
          token~,
        ),
      )
    None => Some(self.identifier_expression(token~))
  }
}

///|
pub fn Parser::parse_session_parameter(
  self : Parser,
) -> Expr? raise SqlglotError {
  let mut kind : String? = None
  let mut this = expr_or(self.parse_id_var(), () => self.parse_primary())
  if this is Some(t) && self.match_(DOT) {
    kind = Some(t.name())
    this = expr_or(self.parse_var(), () => self.parse_primary())
  }
  Some(self.expression(mk(SessionParameter, [("this", this), ("kind", kind)])))
}

///|
pub fn Parser::parse_lambda_arg(self : Parser) -> Expr? raise SqlglotError {
  match self.fns.hooks.parse_lambda_arg {
    Some(f) => f(self)
    None => self.parse_id_var()
  }
}

///|
pub fn Parser::parse_lambda(
  self : Parser,
  alias? : Bool = false,
) -> Expr? raise SqlglotError {
  match self.fns.hooks.parse_lambda {
    Some(f) => f(self, alias)
    None => self.parse_lambda_base(alias~)
  }
}

///|
pub fn Parser::parse_lambda_base(
  self : Parser,
  alias? : Bool = false,
) -> Expr? raise SqlglotError {
  let next_token_type = self.next.token_type
  if self.cfg.lambda_arg_terminators.contains(next_token_type) {
    match self.parse_atom() {
      Some(atom) => return Some(atom)
      None => ()
    }
  }
  let index = self.index
  if self.match_(L_PAREN) {
    let expressions = self.parse_csv(() => self.parse_lambda_arg())
    if !self.match_(R_PAREN) {
      self.retreat(index)
    } else if self.match_keys(self.fns.lambdas) {
      return self.fns.lambdas[self.prev.token_type](self, expressions)
    } else {
      self.retreat(index)
    }
  } else if self.cfg.typed_lambda_args ||
    self.fns.lambdas.contains(next_token_type) {
    let expressions = [self.parse_lambda_arg()].filter_map(x => x)
    if self.match_keys(self.fns.lambdas) {
      return self.fns.lambdas[self.prev.token_type](self, expressions)
    }
    self.retreat(index)
  }
  let this = if self.match_(DISTINCT) {
    Some(
      self.expression(
        mk(Distinct, [
          ("expressions", self.parse_csv(() => self.parse_disjunction())),
        ]),
      ),
    )
  } else {
    self.match_(ALL) |> ignore
    self.parse_select_or_expression(alias~)
  }
  self.parse_limit(
    this=self.parse_respect_or_ignore_nulls(
      self.parse_order(
        this=self.parse_having_max(self.parse_respect_or_ignore_nulls(this)),
      ),
    ),
  )
}

///|
pub fn Parser::parse_schema(
  self : Parser,
  this? : Expr?,
) -> Expr? raise SqlglotError {
  let this : Expr? = match this {
    Some(t) => t
    None => None
  }
  let index = self.index
  if !self.match_(L_PAREN) {
    return this
  }
  if self.match_set(self.cfg.select_start_tokens) {
    self.retreat(index)
    return this
  }
  let args = self.parse_csv(() => {
    expr_or(self.parse_constraint(), () => self.parse_field_def())
  })
  self.match_r_paren()
  Some(self.expression(mk(Schema, [("this", this), ("expressions", args)])))
}

///|
pub fn Parser::parse_field_def(self : Parser) -> Expr? raise SqlglotError {
  self.parse_column_def(self.parse_field(any_token=true))
}

///|
pub fn Parser::parse_column_def(
  self : Parser,
  this : Expr?,
  computed_column? : Bool = true,
) -> Expr? raise SqlglotError {
  match self.fns.hooks.parse_column_def {
    Some(f) => f(self, this, computed_column)
    None => self.parse_column_def_base(this, computed_column~)
  }
}

///|
pub fn Parser::parse_column_def_base(
  self : Parser,
  this : Expr?,
  computed_column? : Bool = true,
) -> Expr? raise SqlglotError {
  let mut this = this
  match this {
    Some(t) if t.kind.is_a(Column) => this = t.this()
    _ => ()
  }
  if !computed_column {
    self.match_(ALIAS) |> ignore
  }
  let mut kind = self.parse_types(schema=true)
  if self.match_text_seq(["FOR", "ORDINALITY"]) {
    return Some(
      self.expression(mk(ColumnDef, [("this", this), ("ordinality", true)])),
    )
  }
  let constraints = []
  if (kind is None && self.match_(ALIAS)) ||
    self.match_texts(["ALIAS", "MATERIALIZED"]) {
    let mut persisted = self.prev_upper() == "MATERIALIZED"
    let expression = self.parse_disjunction()
    if !persisted {
      if self.match_text("PERSISTED") {
        persisted = true
      } else if self.match_texts(["STORED", "VIRTUAL"]) {
        persisted = self.prev_upper() == "STORED"
      }
    }
    let data_type = if self.match_text("AUTO") {
      Some(mk1(Var, "AUTO"))
    } else {
      self.parse_types()
    }
    let constraint_kind = mk(ComputedColumnConstraint, [
      ("this", expression),
      ("persisted", persisted),
      ("data_type", data_type),
      ("not_null", self.match_pair(NOT, NULL)),
    ])
    constraints.push(
      self.expression(mk(ColumnConstraint, [("kind", constraint_kind)])),
    )
  } else if kind is None && self.match_any([IN, OUT], advance=false) {
    let input_ = self.match_(IN)
    let output = self.match_(OUT)
    let in_out_constraint = self.expression(
      mk(InOutColumnConstraint, [("input_", input_), ("output", output)]),
    )
    constraints.push(in_out_constraint)
    kind = self.parse_types()
  } else if kind is Some(_) &&
    self.match_(ALIAS, advance=false) &&
    (
      !self.cfg.wrapped_transform_column_constraint ||
      self.next.token_type == L_PAREN
    ) {
    self.advance()
    let expr = self.parse_disjunction()
    let persisted : Bool = self.match_texts(["STORED", "VIRTUAL"]) &&
      self.prev_upper() == "STORED"
    constraints.push(
      self.expression(
        mk(ColumnConstraint, [
          (
            "kind",
            mk(ComputedColumnConstraint, [
              ("this", expr),
              ("persisted", persisted),
            ]),
          ),
        ]),
      ),
    )
  }
  while true {
    match self.parse_column_constraint() {
      Some(c) => constraints.push(c)
      None => break
    }
  }
  if kind is None && constraints.is_empty() {
    return this
  }
  let mut position : Expr? = None
  if self.match_texts(["FIRST", "AFTER"]) {
    let pos = self.prev.text
    position = Some(
      self.expression(
        mk(ColumnPosition, [("this", self.parse_column()), ("position", pos)]),
      ),
    )
  }
  Some(
    self.expression(
      mk(ColumnDef, [
        ("this", this),
        ("kind", kind),
        ("constraints", constraints),
        ("position", position),
      ]),
    ),
  )
}

///|
pub fn Parser::parse_auto_increment(self : Parser) -> Expr? raise SqlglotError {
  let mut start : Expr? = None
  let mut increment : Expr? = None
  let mut order : Bool? = None
  if self.match_(L_PAREN, advance=false) {
    let args = self.parse_wrapped_csv(() => self.parse_bitwise())
    start = args.get(0)
    increment = args.get(1)
  }
  while true {
    if self.match_text("START") {
      start = self.parse_bitwise()
    } else if self.match_text("INCREMENT") {
      increment = self.parse_bitwise()
    } else if self.match_text("ORDER") {
      order = Some(true)
    } else if self.match_text("NOORDER") {
      order = Some(false)
    } else {
      break
    }
  }
  if start is Some(_) || increment is Some(_) || order is Some(_) {
    return Some(
      mk(GeneratedAsIdentityColumnConstraint, [
        ("start", start),
        ("increment", increment),
        ("this", false),
        ("order", order),
      ]),
    )
  }
  Some(mk0(AutoIncrementColumnConstraint))
}

///|
pub fn Parser::parse_check_constraint(
  self : Parser,
) -> Expr? raise SqlglotError {
  match self.fns.hooks.parse_check_constraint {
    Some(f) => f(self)
    None => self.parse_check_constraint_base()
  }
}

///|
pub fn Parser::parse_check_constraint_base(
  self : Parser,
) -> Expr? raise SqlglotError {
  if !self.match_(L_PAREN, advance=false) {
    return None
  }
  let this = self.parse_wrapped(() => self.parse_assignment())
  Some(
    self.expression(
      mk(CheckColumnConstraint, [
        ("this", this),
        ("enforced", self.match_text("ENFORCED")),
      ]),
    ),
  )
}

///|
pub fn Parser::parse_auto_property(self : Parser) -> Expr? raise SqlglotError {
  if !self.match_text("REFRESH") {
    self.retreat(self.index - 1)
    return None
  }
  Some(self.expression(mk1(AutoRefreshProperty, self.parse_var(upper=true))))
}

///|
pub fn Parser::parse_compress(self : Parser) -> Expr? raise SqlglotError {
  if self.match_(L_PAREN, advance=false) {
    return Some(
      self.expression(
        mk1(
          CompressColumnConstraint,
          self.parse_wrapped_csv(() => self.parse_bitwise()),
        ),
      ),
    )
  }
  Some(self.expression(mk1(CompressColumnConstraint, self.parse_bitwise())))
}

///|
pub fn Parser::parse_generated_as_identity(
  self : Parser,
) -> Expr? raise SqlglotError {
  match self.fns.hooks.parse_generated_as_identity {
    Some(f) => f(self)
    None => self.parse_generated_as_identity_base()
  }
}

///|
pub fn Parser::parse_generated_as_identity_base(
  self : Parser,
) -> Expr? raise SqlglotError {
  let this = if self.match_text_seq(["BY", "DEFAULT"]) {
    let on_null = self.match_pair(ON, NULL)
    self.expression(
      mk(GeneratedAsIdentityColumnConstraint, [
        ("this", false),
        ("on_null", on_null),
      ]),
    )
  } else {
    self.match_text("ALWAYS") |> ignore
    self.expression(mk(GeneratedAsIdentityColumnConstraint, [("this", true)]))
  }
  self.match_(ALIAS) |> ignore
  if self.match_text("ROW") {
    let start = self.match_text("START")
    if !start {
      self.match_(END) |> ignore
    }
    let hidden = self.match_text("HIDDEN")
    return Some(
      self.expression(
        mk(GeneratedAsRowColumnConstraint, [
          ("start", start),
          ("hidden", hidden),
        ]),
      ),
    )
  }
  let identity = self.match_text("IDENTITY")
  if self.match_(L_PAREN) {
    if self.match_text_seq(["START", "WITH"]) {
      this.set("start", self.parse_bitwise())
    }
    if self.match_text_seq(["INCREMENT", "BY"]) {
      this.set("increment", self.parse_bitwise())
    }
    if self.match_text("MINVALUE") {
      this.set("minvalue", self.parse_bitwise())
    }
    if self.match_text("MAXVALUE") {
      this.set("maxvalue", self.parse_bitwise())
    }
    if self.match_text("CYCLE") {
      this.set("cycle", true)
    } else if self.match_text_seq(["NO", "CYCLE"]) {
      this.set("cycle", false)
    }
    if !identity {
      this.set("expression", self.parse_range())
    } else if !this.has("start") && self.match_(NUMBER, advance=false) {
      let args = self.parse_csv(() => self.parse_bitwise())
      this.set("start", args.get(0))
      this.set("increment", args.get(1))
    }
    self.match_r_paren()
  }
  Some(this)
}

///|
pub fn Parser::parse_inline(self : Parser) -> Expr? raise SqlglotError {
  self.match_text("LENGTH") |> ignore
  Some(self.expression(mk1(InlineLengthColumnConstraint, self.parse_bitwise())))
}

///|
pub fn Parser::parse_not_constraint(self : Parser) -> Expr? raise SqlglotError {
  if self.match_text("NULL") {
    return Some(self.expression(mk0(NotNullColumnConstraint)))
  }
  if self.match_text("CASESPECIFIC") {
    return Some(
      self.expression(mk(CaseSpecificColumnConstraint, [("not_", true)])),
    )
  }
  if self.match_text_seq(["FOR", "REPLICATION"]) {
    return Some(self.expression(mk0(NotForReplicationColumnConstraint)))
  }
  self.retreat(self.index - 1)
  None
}

///|
pub fn Parser::parse_column_constraint(
  self : Parser,
) -> Expr? raise SqlglotError {
  match self.fns.hooks.parse_column_constraint {
    Some(f) => f(self)
    None => self.parse_column_constraint_base()
  }
}

///|
pub fn Parser::parse_column_constraint_base(
  self : Parser,
) -> Expr? raise SqlglotError {
  let this = if self.match_(CONSTRAINT) { self.parse_id_var() } else { None }
  let procedure_option_follows = self.match_(WITH, advance=false) &&
    self.next.ok() &&
    self.cfg.procedure_options.contains(py_upper(self.next.text))
  let index = self.index
  if !procedure_option_follows &&
    self.match_text_keys(self.fns.constraint_parsers) {
    let constraint = self.fns.constraint_parsers[self.prev_upper()](self)
    match constraint {
      None => {
        self.retreat(index)
        return None
      }
      Some(c) =>
        return Some(
          self.expression(mk(ColumnConstraint, [("this", this), ("kind", c)])),
        )
    }
  }
  if self.match_text_seq(["CHARACTER", "SET"]) {
    let kind = self.expression(
      mk1(CharacterSetColumnConstraint, self.parse_var_or_string()),
    )
    return Some(
      self.expression(mk(ColumnConstraint, [("this", this), ("kind", kind)])),
    )
  }
  this
}

///|
pub fn Parser::parse_constraint(self : Parser) -> Expr? raise SqlglotError {
  match self.fns.hooks.parse_constraint {
    Some(f) => f(self)
    None => self.parse_constraint_base()
  }
}

///|
pub fn Parser::parse_constraint_base(self : Parser) -> Expr? raise SqlglotError {
  if !self.match_(CONSTRAINT) {
    return self.parse_unnamed_constraint(
      constraints=self.cfg.schema_unnamed_constraints,
    )
  }
  let this = self.parse_id_var()
  Some(
    self.expression(
      mk(Constraint, [
        ("this", this),
        ("expressions", self.parse_unnamed_constraints()),
      ]),
    ),
  )
}

///|
pub fn Parser::parse_unnamed_constraints(
  self : Parser,
) -> Array[Expr] raise SqlglotError {
  let constraints = []
  while true {
    match
      expr_or(self.parse_unnamed_constraint(), () => self.parse_function()) {
      Some(c) => constraints.push(c)
      None => break
    }
  }
  constraints
}

///|
pub fn Parser::parse_unnamed_constraint(
  self : Parser,
  constraints? : @set.Set[String],
) -> Expr? raise SqlglotError {
  let index = self.index
  if self.match_(IDENTIFIER, advance=false) {
    return None
  }
  let matched = match constraints {
    Some(c) => self.match_text_set(c)
    None => self.match_text_keys(self.fns.constraint_parsers)
  }
  if !matched {
    return None
  }
  let constraint_key = self.prev_upper()
  match self.fns.constraint_parsers.get(constraint_key) {
    None => {
      self.raise_error(
        "No parser found for schema constraint \{constraint_key}.",
      )
      None
    }
    Some(parser) => {
      let result = parser(self)
      if result is None {
        self.retreat(index)
      }
      result
    }
  }
}

///|
pub fn Parser::parse_unique_key(self : Parser) -> Expr? raise SqlglotError {
  match self.fns.hooks.parse_unique_key {
    Some(f) => f(self)
    None => self.parse_unique_key_base()
  }
}

///|
pub fn Parser::parse_unique_key_base(self : Parser) -> Expr? raise SqlglotError {
  if self.curr.ok() &&
    self.curr.token_type != IDENTIFIER &&
    self.fns.constraint_parsers.contains(py_upper(self.curr.text)) {
    return None
  }
  self.parse_id_var(any_token=false)
}

///|
pub fn Parser::parse_unique(self : Parser) -> Expr? raise SqlglotError {
  match self.fns.hooks.parse_unique {
    Some(f) => f(self)
    None => self.parse_unique_base()
  }
}

///|
pub fn Parser::parse_unique_base(self : Parser) -> Expr? raise SqlglotError {
  self.match_texts(["KEY", "INDEX"]) |> ignore
  let nulls = self.match_text_seq(["NULLS", "NOT", "DISTINCT"])
  let this = self.parse_schema(this=self.parse_unique_key())
  let index_type : Value = if self.match_(USING) {
    match self.advance_any() {
      Some(_) => Str(self.prev.text)
      None => Bool(false)
    }
  } else {
    Bool(false)
  }
  let on_conflict = self.parse_on_conflict()
  let options = self.parse_key_constraint_options()
  Some(
    self.expression(
      mk(UniqueColumnConstraint, [
        ("nulls", nulls),
        ("this", this),
        ("index_type", index_type),
        ("on_conflict", on_conflict),
        ("options", options),
      ]),
    ),
  )
}

///|
pub fn Parser::parse_key_constraint_options(
  self : Parser,
) -> Array[String] raise SqlglotError {
  let options = []
  while true {
    if !self.curr.ok() {
      break
    }
    if self.match_(ON) {
      let mut action = "None"
      let on = match self.advance_any() {
        Some(_) => self.prev.text
        None => "None"
      }
      if self.match_text_seq(["NO", "ACTION"]) {
        action = "NO ACTION"
      } else if self.match_text("CASCADE") {
        action = "CASCADE"
      } else if self.match_text("RESTRICT") {
        action = "RESTRICT"
      } else if self.match_pair(SET, NULL) {
        action = "SET NULL"
      } else if self.match_pair(SET, DEFAULT) {
        action = "SET DEFAULT"
      } else {
        self.raise_error("Invalid key constraint")
      }
      options.push("ON \{on} \{action}")
    } else {
      match
        self.parse_var_from_options(
          self.cfg.key_constraint_options,
          raise_unmatched=false,
        ) {
        Some(v) => options.push(v.name())
        None => break
      }
    }
  }
  options
}

///|
pub fn Parser::parse_references(
  self : Parser,
  match_? : Bool = true,
) -> Expr? raise SqlglotError {
  if match_ && !self.match_(REFERENCES) {
    return None
  }
  let this = self.parse_table(schema=true)
  let options = self.parse_key_constraint_options()
  Some(self.expression(mk(Reference, [("this", this), ("options", options)])))
}

///|
pub fn Parser::parse_foreign_key(self : Parser) -> Expr? raise SqlglotError {
  match self.fns.hooks.parse_foreign_key {
    Some(f) => f(self)
    None => self.parse_foreign_key_base()
  }
}

///|
pub fn Parser::parse_foreign_key_base(
  self : Parser,
) -> Expr? raise SqlglotError {
  let expressions = if !self.match_(REFERENCES, advance=false) {
    Some(self.parse_wrapped_id_vars())
  } else {
    None
  }
  let reference = self.parse_references()
  let on_options : Array[(String, String)] = []
  while self.match_(ON) {
    if !self.match_any([DELETE, UPDATE]) {
      self.raise_error("Expected DELETE or UPDATE")
    }
    let kind = py_lower(self.prev.text)
    let action = if self.match_text_seq(["NO", "ACTION"]) {
      "NO ACTION"
    } else if self.match_(SET) {
      self.match_any([NULL, DEFAULT]) |> ignore
      "SET " + self.prev_upper()
    } else {
      self.advance()
      self.prev_upper()
    }
    on_options.push((kind, action))
  }
  let e = mk(ForeignKey, [
    ("expressions", expressions),
    ("reference", reference),
    ("options", self.parse_key_constraint_options()),
  ])
  for kv in on_options {
    e.set(kv.0, kv.1)
  }
  Some(self.expression(e))
}

///|
pub fn Parser::parse_primary_key_part(
  self : Parser,
) -> Expr? raise SqlglotError {
  match self.fns.hooks.parse_primary_key_part {
    Some(f) => f(self)
    None => self.parse_field()
  }
}

///|
pub fn Parser::parse_period_for_system_time(
  self : Parser,
) -> Expr? raise SqlglotError {
  if !self.match_text_seq(["FOR", "SYSTEM_TIME"]) {
    self.retreat(self.index - 1)
    return None
  }
  let id_vars = self.parse_wrapped_id_vars()
  Some(
    self.expression(
      mk(PeriodForSystemTimeConstraint, [
        ("this", id_vars.get(0)),
        ("expression", id_vars.get(1)),
      ]),
    ),
  )
}

///|
pub fn Parser::parse_primary_key(
  self : Parser,
  wrapped_optional? : Bool = false,
  in_props? : Bool = false,
  named_primary_key? : Bool = false,
) -> Expr? raise SqlglotError {
  match self.fns.hooks.parse_primary_key {
    Some(f) => f(self, wrapped_optional, in_props, named_primary_key)
    None =>
      self.parse_primary_key_base(
        wrapped_optional~,
        in_props~,
        named_primary_key~,
      )
  }
}

///|
pub fn Parser::parse_primary_key_base(
  self : Parser,
  wrapped_optional? : Bool = false,
  in_props? : Bool = false,
  named_primary_key? : Bool = false,
) -> Expr? raise SqlglotError {
  let desc : Bool? = if self.match_any([ASC, DESC]) {
    Some(self.prev.token_type == DESC)
  } else {
    None
  }
  let mut this : Expr? = None
  if named_primary_key &&
    !self.fns.constraint_parsers.contains(py_upper(self.curr.text)) &&
    self.next.ok() &&
    self.next.token_type == L_PAREN {
    this = self.parse_id_var()
  }
  if !in_props && !self.match_(L_PAREN, advance=false) {
    return Some(
      self.expression(
        mk(PrimaryKeyColumnConstraint, [
          ("desc", desc),
          ("options", self.parse_key_constraint_options()),
        ]),
      ),
    )
  }
  let expressions = self.parse_wrapped_csv(
    () => self.parse_primary_key_part(),
    optional=wrapped_optional,
  )
  let include = self.parse_index_params()
  let options = self.parse_key_constraint_options()
  Some(
    self.expression(
      mk(PrimaryKey, [
        ("this", this),
        ("expressions", expressions),
        ("include", include),
        ("options", options),
      ]),
    ),
  )
}

///|
pub fn Parser::parse_bracket_key_value(
  self : Parser,
  is_map? : Bool = false,
) -> Expr? raise SqlglotError {
  match self.fns.hooks.parse_bracket_key_value {
    Some(f) => f(self, is_map)
    None =>
      self.parse_slice(
        self.parse_alias(self.parse_disjunction(), explicit=true),
      )
  }
}

///|
pub fn Parser::parse_odbc_datetime_literal(
  self : Parser,
) -> Expr raise SqlglotError {
  self.match_(VAR) |> ignore
  let kind = match
    self.cfg.odbc_datetime_literals.get(py_lower(self.prev.text)) {
    Some(k) => k
    None => raise ParseError("Unknown ODBC literal", [])
  }
  let expression = self.expression(mk1(kind, self.parse_string()))
  if !self.match_(R_BRACE) {
    self.raise_error("Expected }")
  }
  expression
}

///|
pub fn build_array_constructor(
  kind : Kind,
  args : Array[Expr],
  bracket_kind : TokenType,
  dialect : Dialect,
) -> Expr {
  let array_exp = mk(kind, [("expressions", args)])
  if kind == Array && dialect.cfg.has_distinct_array_constructors {
    array_exp.set("bracket_notation", bracket_kind == L_BRACKET)
  }
  array_exp
}

///|
pub fn Parser::parse_bracket(
  self : Parser,
  this : Expr?,
) -> Expr? raise SqlglotError {
  match self.fns.hooks.parse_bracket {
    Some(f) => f(self, this)
    None => self.parse_bracket_base(this)
  }
}

///|
pub fn Parser::parse_bracket_base(
  self : Parser,
  this : Expr?,
) -> Expr? raise SqlglotError {
  if !self.match_set(self.cfg.brackets) {
    return this
  }
  let parse_map = if self.cfg.map_keys_are_arbitrary_expressions {
    let i = self.index - 2
    i >= 0 && i < self.tokens.length() && py_upper(self.tokens[i].text) == "MAP"
  } else {
    false
  }
  let bracket_kind = self.prev.token_type
  if bracket_kind == L_BRACE &&
    self.curr.ok() &&
    self.curr.token_type == VAR &&
    self.cfg.odbc_datetime_literals.contains(py_lower(self.curr.text)) {
    return Some(self.parse_odbc_datetime_literal())
  }
  let expressions = self.parse_csv(() => {
    self.parse_bracket_key_value(is_map=bracket_kind == L_BRACE)
  })
  if bracket_kind == L_BRACKET && !self.match_(R_BRACKET) {
    self.raise_error("Expected ]")
  } else if bracket_kind == L_BRACE && !self.match_(R_BRACE) {
    self.raise_error("Expected }")
  }
  let mut this = this
  if bracket_kind == L_BRACE {
    this = Some(
      self.expression(
        mk(Struct, [
          ("expressions", self.kv_to_prop_eq(expressions, parse_map~)),
        ]),
      ),
    )
  } else {
    match this {
      None =>
        this = Some(
          build_array_constructor(
            Array,
            expressions,
            bracket_kind,
            self.dialect,
          ),
        )
      Some(t) => {
        match self.cfg.array_constructors.get(py_upper(t.name())) {
          Some(constructor_type) =>
            return Some(
              build_array_constructor(
                constructor_type,
                expressions,
                bracket_kind,
                self.dialect,
              ),
            )
          None => ()
        }
        let expressions = gen_apply_index_offset(
          t,
          expressions,
          -self.dialect.cfg.index_offset,
          self.dialect,
        )
        let comments = t.pop_comments()
        this = Some(
          self.expression(
            mk(Bracket, [("this", t), ("expressions", expressions)]),
            comments~,
          ),
        )
      }
    }
  }
  self.add_comments(this)
  self.parse_bracket(this)
}

///|
pub fn Parser::parse_slice(
  self : Parser,
  this : Expr?,
) -> Expr? raise SqlglotError {
  if !self.match_(COLON) {
    return this
  }
  let end = if self.match_pair(DASH, COLON, advance=false) {
    self.advance()
    Some(mk1(Neg, literal_number("1")))
  } else {
    self.parse_assignment()
  }
  let step = if self.match_(COLON) { self.parse_unary() } else { None }
  Some(
    self.expression(
      mk(Slice, [("this", this), ("expression", end), ("step", step)]),
    ),
  )
}

///|
pub fn Parser::identifier_expression(
  self : Parser,
  token? : Token,
  quoted? : Bool,
) -> Expr raise SqlglotError {
  let token = match token {
    Some(t) => t
    None => self.prev
  }
  self.expression(
    mk(Identifier, [("this", token.text), ("quoted", quoted)]),
    token~,
  )
}

///|
pub fn Parser::parse_distinct_arg_function(
  self : Parser,
  kind : Kind,
  distinct_index? : Int = 0,
) -> Expr? raise SqlglotError {
  let is_distinct = self.match_(DISTINCT)
  if !is_distinct {
    self.match_(ALL) |> ignore
  }
  let args = [self.parse_lambda()].filter_map(x => x)
  if self.match_(COMMA) {
    args.append(self.parse_function_args())
  }
  if is_distinct && distinct_index < args.length() {
    args[distinct_index] = self.expression(
      mk(Distinct, [("expressions", [args[distinct_index]])]),
    )
  }
  Some(from_arg_list(kind, args))
}