Class: Kotoshu::Grammar::PatternMatchers::DoubleNegativeMatcher

Inherits:
BaseMatcher
  • Object
show all
Defined in:
lib/kotoshu/grammar/pattern_matchers/double_negative_matcher.rb

Overview

Matcher for double negative rules.

Detects when multiple negative words appear within a certain distance, with support for declared exception phrases. The canonical exception is the "not only...but also" correlative conjunction: both "not" and "also" are flagged as negatives, but the construction is grammatical and must not fire.

Exception phrases use the literal ... separator to indicate arbitrary text between the prefix and suffix:

"not only...but also"

means "not only" at some position, then any text, then "but also". Every negative inside such a region is suppressed.

Instance Method Summary collapse

Methods inherited from BaseMatcher

#initialize

Constructor Details

This class inherits a constructor from Kotoshu::Grammar::PatternMatchers::BaseMatcher

Instance Method Details

#match(tokens, rule) ⇒ Array<Hash>

Match tokens against the double negative pattern.

Parameters:

  • tokens (Array<Hash>)

    Array of token hashes

  • rule (Rule)

    The rule being checked

Returns:

  • (Array<Hash>)

    Array of error hashes



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/kotoshu/grammar/pattern_matchers/double_negative_matcher.rb', line 27

def match(tokens, rule)
  errors = []
  exceptions = rule.exceptions || {}
  exception_phrases = exceptions['phrases'] || []

  conditions = @pattern['conditions'] || []
  distance_condition = conditions.find { |c| c['type'] == 'distance_check' }
  max_distance = distance_condition&.dig('max_distance') || 15

  regions = exception_regions(tokens, exception_phrases)

  negative_indices = tokens.each_with_index.filter_map do |token, idx|
    word = token[:token]&.downcase
    next unless negative?(word)
    next if regions.any? { |range| range.include?(idx) }

    idx
  end

  negative_indices.each_cons(2) do |idx1, idx2|
    pos1 = tokens[idx1][:position]
    pos2 = tokens[idx2][:position]
    distance = pos2 - pos1
    next if distance > max_distance

    error = build_error(tokens, idx1, idx2, rule)
    errors << error if error
  end
  errors
end