Class: Kotoshu::Models::AffixRule

Inherits:
Object
  • Object
show all
Defined in:
lib/kotoshu/core/models/affix_rule.rb

Overview

Note:

This class is immutable and frozen on initialization.

Affix rule model for Hunspell-style affix processing.

Affix rules define how prefixes and suffixes can be added or removed from words to generate morphological variants.

This is a value object that represents a single affix rule.

Examples:

Creating a prefix rule

rule = Models::AffixRule.new(
  type: :prefix,
  flag: "A",
  strip: "",
  add: "re",
  condition: "."
)
rule.prefix?   # => true
rule.suffix?   # => false

Constant Summary collapse

TYPES =

Affix rule types.

{
  prefix: "PFX",
  suffix: "SFX"
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(type:, flag:, strip:, add:, condition: ".", cross_product: false) ⇒ AffixRule

Create a new AffixRule.

Parameters:

  • type (Symbol)

    The affix type (:prefix or :suffix)

  • flag (String)

    The flag character

  • strip (String)

    Characters to strip

  • add (String)

    Characters to add

  • condition (String, Regexp) (defaults to: ".")

    Condition for applying

  • cross_product (Boolean) (defaults to: false)

    Whether this is cross-product

Raises:

  • (ArgumentError)


57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/kotoshu/core/models/affix_rule.rb', line 57

def initialize(type:, flag:, strip:, add:, condition: ".", cross_product: false)
  raise ArgumentError, "Invalid type: #{type}" unless %i[prefix suffix].include?(type)
  raise ArgumentError, "Flag cannot be empty" if flag.nil? || flag.empty?

  @type = type
  @flag = flag.dup.freeze
  @strip = strip.dup.freeze
  @add = add.dup.freeze
  @condition = condition.is_a?(Regexp) ? condition : compile_condition(condition)
  @cross_product = cross_product

  freeze
end

Instance Attribute Details

#addString (readonly)

Returns Characters to add to the word.

Returns:

  • (String)

    Characters to add to the word



35
36
37
# File 'lib/kotoshu/core/models/affix_rule.rb', line 35

def add
  @add
end

#conditionString, Regexp (readonly)

Returns Condition for applying this rule.

Returns:

  • (String, Regexp)

    Condition for applying this rule



38
39
40
# File 'lib/kotoshu/core/models/affix_rule.rb', line 38

def condition
  @condition
end

#cross_productBoolean (readonly)

Returns Whether this is a cross-product rule.

Returns:

  • (Boolean)

    Whether this is a cross-product rule



41
42
43
# File 'lib/kotoshu/core/models/affix_rule.rb', line 41

def cross_product
  @cross_product
end

#flagString (readonly)

Returns The flag character identifying this rule.

Returns:

  • (String)

    The flag character identifying this rule



29
30
31
# File 'lib/kotoshu/core/models/affix_rule.rb', line 29

def flag
  @flag
end

#stripString (readonly)

Returns Characters to strip from the word.

Returns:

  • (String)

    Characters to strip from the word



32
33
34
# File 'lib/kotoshu/core/models/affix_rule.rb', line 32

def strip
  @strip
end

#typeSymbol (readonly)

Returns The affix type (:prefix or :suffix).

Returns:

  • (Symbol)

    The affix type (:prefix or :suffix)



26
27
28
# File 'lib/kotoshu/core/models/affix_rule.rb', line 26

def type
  @type
end

Class Method Details

.from_hunspell(line, type) ⇒ AffixRule

Create an affix rule from a Hunspell affix line.

Examples:

Parsing a Hunspell prefix rule

AffixRule.from_hunspell("PFX A Y 1 re .", :prefix)

Parsing a Hunspell suffix rule

AffixRule.from_hunspell("SFX V N 2 ive e", :suffix)

Parameters:

  • line (String)

    The affix line

  • type (Symbol)

    The rule type (:prefix or :suffix)

Returns:



239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/kotoshu/core/models/affix_rule.rb', line 239

def self.from_hunspell(line, type)
  parts = line.split
  return nil if parts.length < 5

  flag = parts[1]
  cross_product = parts[2] == "Y"
  strip = parts[3] == "0" ? "" : parts[3]
  add = parts[4]
  condition = parts[5] || "."

  new(
    type: type,
    flag: flag,
    strip: strip,
    add: add,
    condition: condition,
    cross_product: cross_product
  )
end

Instance Method Details

#<=>(other) ⇒ Integer

Compare rules by flag.

Parameters:

Returns:

  • (Integer)

    Comparison result



178
179
180
181
182
# File 'lib/kotoshu/core/models/affix_rule.rb', line 178

def <=>(other)
  return nil unless other.is_a?(AffixRule)

  @flag <=> other.flag
end

#==(other) ⇒ Boolean Also known as: eql?

Check equality based on all attributes.

Parameters:

Returns:

  • (Boolean)

    True if equal



155
156
157
158
159
160
161
162
163
164
# File 'lib/kotoshu/core/models/affix_rule.rb', line 155

def ==(other)
  return false unless other.is_a?(AffixRule)

  @type == other.type &&
    @flag == other.flag &&
    @strip == other.strip &&
    @add == other.add &&
    @condition == other.condition &&
    @cross_product == other.cross_product
end

#applies_to?(word) ⇒ Boolean

Check if this rule can be applied to a word.

Parameters:

  • word (String)

    The word to check

Returns:

  • (Boolean)

    True if the rule applies



89
90
91
92
93
# File 'lib/kotoshu/core/models/affix_rule.rb', line 89

def applies_to?(word)
  return false if word.nil? || word.empty?

  word.match?(@condition)
end

#apply(word) ⇒ String?

Apply this rule to a word.

Parameters:

  • word (String)

    The word to modify

Returns:

  • (String, nil)

    The modified word, or nil if rule doesn’t apply



99
100
101
102
103
104
105
106
107
108
109
# File 'lib/kotoshu/core/models/affix_rule.rb', line 99

def apply(word)
  return nil unless applies_to?(word)

  if prefix?
    # Strip from beginning, add prefix
    word.start_with?(@strip) ? @add + word[@strip.length..] : nil
  else
    # Strip from end, add suffix
    word.end_with?(@strip) ? word[0...-@strip.length] + @add : nil
  end
end

#hashInteger

Hash based on all attributes.

Returns:

  • (Integer)

    Hash code



170
171
172
# File 'lib/kotoshu/core/models/affix_rule.rb', line 170

def hash
  [@type, @flag, @strip, @add, @cross_product].hash
end

#prefix?Boolean

Check if this is a prefix rule.

Returns:

  • (Boolean)

    True if prefix



74
75
76
# File 'lib/kotoshu/core/models/affix_rule.rb', line 74

def prefix?
  @type == :prefix
end

#remove(word) ⇒ String?

Remove this affix from a word (reverse operation).

Parameters:

  • word (String)

    The word to modify

Returns:

  • (String, nil)

    The stripped word, or nil if affix doesn’t match



115
116
117
118
119
120
121
122
123
124
125
# File 'lib/kotoshu/core/models/affix_rule.rb', line 115

def remove(word)
  return nil unless applies_to?(word)

  if prefix?
    # Remove prefix if it matches
    word.start_with?(@add) ? @strip + word[@add.length..] : nil
  else
    # Remove suffix if it matches
    word.end_with?(@add) ? word[0...-@add.length] + @strip : nil
  end
end

#suffix?Boolean

Check if this is a suffix rule.

Returns:

  • (Boolean)

    True if suffix



81
82
83
# File 'lib/kotoshu/core/models/affix_rule.rb', line 81

def suffix?
  @type == :suffix
end

#to_hHash

Convert to hash.

Returns:

  • (Hash)

    Hash representation



140
141
142
143
144
145
146
147
148
149
# File 'lib/kotoshu/core/models/affix_rule.rb', line 140

def to_h
  {
    type: @type,
    flag: @flag,
    strip: @strip,
    add: @add,
    condition: @condition.is_a?(Regexp) ? @condition.source : @condition,
    cross_product: @cross_product
  }
end

#to_hunspellString

Get the Hunspell representation.

Returns:

  • (String)

    The affix line for Hunspell format



130
131
132
133
134
135
# File 'lib/kotoshu/core/models/affix_rule.rb', line 130

def to_hunspell
  type_code = TYPES[@type]
  cross = @cross_product ? "Y" : "N"
  "#{type_code} #{@flag} #{cross} #{@strip.empty? ? "0" : @strip} " \
  "#{@add} #{@condition.is_a?(Regexp) ? condition_to_s : @condition}"
end