class Cucumber::CucumberExpressions::CucumberExpressionParser

def parse(expression)

def parse(expression)
  # text := whitespace | ')' | '}' | .
  parse_text = lambda do |_, tokens, current|
    token = tokens[current]
    case token.type
    when TokenType::WHITE_SPACE, TokenType::TEXT, TokenType::END_PARAMETER, TokenType::END_OPTIONAL
      return 1, [Node.new(NodeType::TEXT, nil, token.text, token.start, token.end)]
    when TokenType::ALTERNATION
      raise AlternationNotAllowedInOptional.new(expression, token)
    when TokenType::BEGIN_PARAMETER, TokenType::START_OF_LINE, TokenType::END_OF_LINE, TokenType::BEGIN_OPTIONAL
    else
      # If configured correctly this will never happen
      return 0, nil
    end
    # If configured correctly this will never happen
    return 0, nil
  end
  # name := whitespace | .
  parse_name = lambda do |_, tokens, current|
    token = tokens[current]
    case token.type
    when TokenType::WHITE_SPACE, TokenType::TEXT
      return 1, [Node.new(NodeType::TEXT, nil, token.text, token.start, token.end)]
    when TokenType::BEGIN_PARAMETER, TokenType::END_PARAMETER, TokenType::BEGIN_OPTIONAL, TokenType::END_OPTIONAL, TokenType::ALTERNATION
      raise InvalidParameterTypeNameInNode.new(expression, token)
    when TokenType::START_OF_LINE, TokenType::END_OF_LINE
      # If configured correctly this will never happen
      return 0, nil
    else
      # If configured correctly this will never happen
      return 0, nil
    end
  end
  # parameter := '{' + name* + '}'
  parse_parameter = parse_between(
      NodeType::PARAMETER,
      TokenType::BEGIN_PARAMETER,
      TokenType::END_PARAMETER,
      [parse_name]
  )
  # optional := '(' + option* + ')'
  # option := optional | parameter | text
  optional_sub_parsers = []
  parse_optional = parse_between(
      NodeType::OPTIONAL,
      TokenType::BEGIN_OPTIONAL,
      TokenType::END_OPTIONAL,
      optional_sub_parsers
  )
  optional_sub_parsers << parse_optional << parse_parameter << parse_text
  # alternation := alternative* + ( '/' + alternative* )+
  parse_alternative_separator = lambda do |_, tokens, current|
    unless looking_at(tokens, current, TokenType::ALTERNATION)
      return 0, nil
    end
    token = tokens[current]
    return 1, [Node.new(NodeType::ALTERNATIVE, nil, token.text, token.start, token.end)]
  end
  alternative_parsers = [
      parse_alternative_separator,
      parse_optional,
      parse_parameter,
      parse_text,
  ]
  # alternation := (?<=left-boundary) + alternative* + ( '/' + alternative* )+ + (?=right-boundary)
  # left-boundary := whitespace | } | ^
  # right-boundary := whitespace | { | $
  # alternative: = optional | parameter | text
  parse_alternation = lambda do |expr, tokens, current|
    previous = current - 1
    unless looking_at_any(tokens, previous, [TokenType::START_OF_LINE, TokenType::WHITE_SPACE, TokenType::END_PARAMETER])
      return 0, nil
    end
    consumed, ast = parse_tokens_until(expr, alternative_parsers, tokens, current, [TokenType::WHITE_SPACE, TokenType::END_OF_LINE, TokenType::BEGIN_PARAMETER])
    sub_current = current + consumed
    unless ast.map { |astNode| astNode.type }.include? NodeType::ALTERNATIVE
      return 0, nil
    end
    start = tokens[current].start
    _end = tokens[sub_current].start
    # Does not consume right hand boundary token
    return consumed, [Node.new(NodeType::ALTERNATION, split_alternatives(start, _end, ast), nil, start, _end)]
  end
  #
  # cucumber-expression :=  ( alternation | optional | parameter | text )*
  #
  parse_cucumber_expression = parse_between(
      NodeType::EXPRESSION,
      TokenType::START_OF_LINE,
      TokenType::END_OF_LINE,
      [parse_alternation, parse_optional, parse_parameter, parse_text]
  )
  tokenizer = CucumberExpressionTokenizer.new
  tokens = tokenizer.tokenize(expression)
  _, ast = parse_cucumber_expression.call(expression, tokens, 0)
  ast[0]
end