Spec-Zone.ru › Ruby 3.2

класс SyntaxSuggest::CodeFrontier

Родитель:
Объект

В алгоритме есть три основных этапа:

  1. Очистка/форматирование исходного кода

  2. Поиск невалидных блоков

  3. Форматирование невалидных блоков в осмысленный вид

Граница кода является критической частью второго шага

## Где мы были

После создания блока кода он добавляется на границу. Затем он сортируется по отступу, и граница может быть отфильтрована. Крупные блоки, полностью охватывающие меньший блок, приведут к удалению меньшего блока.

CodeFrontier#<<(block) # Adds block to frontier
CodeFrontier#pop # Removes block from frontier

## Куда мы можем пойти

Внутри граница отслеживает «непосещённые» строки, доступные через `next_indent_line` при вызове. Этот метод возвращает строку кода с максимальным отступом.

Возвращённая строка кода может использоваться для создания CodeBlock, а затем этот блок кода добавляется обратно на границу. Затем строки удаляются из «непосещённых», чтобы мы не создавали один и тот же блок дважды.

CodeFrontier#next_indent_line # Shows next line
CodeFrontier#register_indent_block(block) # Removes lines from unvisited

## Когда останавливаться

Граница знает, как проверять весь документ на синтаксические ошибки. Когда блоки добавляются на границу, они удаляются из документа. Когда все блоки кода, содержащие синтаксические ошибки, добавлены на границу, документ будет парситься без синтаксических ошибок, и поиск можно остановить.

CodeFrontier#holds_all_syntax_errors? # Returns true when frontier holds all syntax errors

## Фильтрация ложных срабатываний

После завершения поиска на границе могут быть несколько блоков, которые не содержат синтаксическую ошибку. Чтобы ограничить результат наименьшим подмножеством «невалидных блоков», вызовите:

CodeFrontier#detect_invalid_blocks

Методы класса (public)

combination(array) Показать исходный код
# File lib/syntax_suggest/code_frontier.rb, line 162
def self.combination(array)
  guesses = []
  1.upto(array.length).each do |size|
    guesses.concat(array.combination(size).to_a)
  end
  guesses
end

Пример:

combination([:a, :b, :c, :d])
# => [[:a], [:b], [:c], [:d], [:a, :b], [:a, :c], [:a, :d], [:b, :c], [:b, :d], [:c, :d], [:a, :b, :c], [:a, :b, :d], [:a, :c, :d], [:b, :c, :d], [:a, :b, :c, :d]]
new(code_lines:, unvisited: UnvisitedLines.new(code_lines: code_lines)) Показать исходный код
# File lib/syntax_suggest/code_frontier.rb, line 53
def initialize(code_lines:, unvisited: UnvisitedLines.new(code_lines: code_lines))
  @code_lines = code_lines
  @unvisited = unvisited
  @queue = PriorityEngulfQueue.new

  @check_next = true
end

Методы экземпляра (public)

<<(block) Показать исходный код
# File lib/syntax_suggest/code_frontier.rb, line 148
def <<(block)
  @unvisited.visit_block(block)

  @queue.push(block)

  @check_next = true if block.invalid?

  self
end

Добавление блока на границу

Этот метод гарантирует, что граница всегда отсортирована (по порядку отступов) и что строки каждого блока кода удаляются из хэша отступов, чтобы мы не переоценивали одну и ту же строку несколько раз.

count() Показать исходный код
# File lib/syntax_suggest/code_frontier.rb, line 61
def count
  @queue.length
end
detect_invalid_blocks() Показать исходный код
# File lib/syntax_suggest/code_frontier.rb, line 172
def detect_invalid_blocks
  self.class.combination(@queue.to_a.select(&:invalid?)).detect do |block_array|
    holds_all_syntax_errors?(block_array, can_cache: false)
  end || []
end

Учитывая, что мы знаем, что синтаксическая ошибка существует где-то в нашей границе, мы хотим найти наименьший возможный набор блоков, содержащих все синтаксические ошибки.

expand?() Показать исходный код
# File lib/syntax_suggest/code_frontier.rb, line 111
def expand?
  return false if @queue.empty?
  return true if @unvisited.empty?

  frontier_indent = @queue.peek.current_indent
  unvisited_indent = next_indent_line.indent

  if ENV["SYNTAX_SUGGEST_DEBUG"]
    puts "```"
    puts @queue.peek.to_s
    puts "```"
    puts "  @frontier indent:  #{frontier_indent}"
    puts "  @unvisited indent: #{unvisited_indent}"
  end

  # Expand all blocks before moving to unvisited lines
  frontier_indent >= unvisited_indent
end
holds_all_syntax_errors?(block_array = @queue, can_cache: true) Показать исходный код
# File lib/syntax_suggest/code_frontier.rb, line 89
def holds_all_syntax_errors?(block_array = @queue, can_cache: true)
  return false if can_cache && can_skip_check?

  without_lines = block_array.to_a.flat_map do |block|
    block.lines
  end

  SyntaxSuggest.valid_without?(
    without_lines: without_lines,
    code_lines: @code_lines
  )
end

Возвращает true, если документ валиден со всеми удалёнными строками. По умолчанию проверяет все блоки, присутствующие в массиве границы, но также может быть использован для произвольных массивов блоков кода.

next_indent_line() Показать исходный код
# File lib/syntax_suggest/code_frontier.rb, line 107
def next_indent_line
  @unvisited.peek
end
pop() Показать исходный код
# File lib/syntax_suggest/code_frontier.rb, line 103
def pop
  @queue.pop
end

Возвращает блок кода с наибольшим возможным отступом.

register_engulf_block(block) Показать исходный код
# File lib/syntax_suggest/code_frontier.rb, line 140
def register_engulf_block(block)
end

Когда один элемент полностью охватывает другой, мы удаляем меньший блок с границы. Это предотвращает двойные расширения и всяческое странное поведение. Однако эта гарантия довольно дорогостоящая.

register_indent_block(block) Показать исходный код
# File lib/syntax_suggest/code_frontier.rb, line 132
def register_indent_block(block)
  @unvisited.visit_block(block)
  self
end

Отслеживает, какие строки были добавлены в блоки и какие ещё не посещены.

Методы экземпляра (private)

can_skip_check?() Показать исходный код
# File lib/syntax_suggest/code_frontier.rb, line 74
        def can_skip_check?
  check_next = @check_next
  @check_next = false

  if check_next
    false
  else
    true
  end
end

Оптимизация производительности

Парсинг с помощью риппера дорогостоящий. Если мы знаем, что у нас нет блоков с невалидным синтаксисом, то мы знаем, что ещё не нашли неправильный синтаксис.

Когда невалидный блок добавляется на границу, проверяется состояние документа.

Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API