class Datasets::CLDRPlurals::RuleParser

Syntax: unicode.org/reports/tr35/tr35-numbers.html#Plural_rules_syntax

Public Class Methods

new(rule, data) click to toggle source
# File lib/datasets/cldr-plurals.rb, line 103
def initialize(rule, data)
  @rule = rule
  @data = data
  @scanner = StringScanner.new(@data)
end

Public Instance Methods

parse() click to toggle source
# File lib/datasets/cldr-plurals.rb, line 109
def parse
  @rule.condition = parse_condition
  skip_whitespaces
  if @scanner.scan(/@integer/)
    @rule.integer_samples = parse_sample_list
  end
  skip_whitespaces
  if @scanner.scan(/@decimal/)
    @rule.decimal_samples = parse_sample_list
  end
end

Private Instance Methods

parse_and() click to toggle source
# File lib/datasets/cldr-plurals.rb, line 160
def parse_and
  skip_whitespaces
  @scanner.scan(/and/)
end
parse_and_condition() click to toggle source
# File lib/datasets/cldr-plurals.rb, line 145
def parse_and_condition
  skip_whitespaces
  relation = parse_relation
  return nil if relation.nil?
  relations = [relation]
  while parse_and
    relations << parse_relation
  end
  if relations.size == 1
    relation
  else
    [:and, *relations]
  end
end
parse_condition() click to toggle source
# File lib/datasets/cldr-plurals.rb, line 126
def parse_condition
  and_condition = parse_and_condition
  return nil if and_condition.nil?
  and_conditions = [and_condition]
  while parse_or
    and_conditions << parse_and_condition
  end
  if and_conditions.size == 1
    and_condition
  else
    [:or, *and_conditions]
  end
end
parse_equal() click to toggle source
# File lib/datasets/cldr-plurals.rb, line 231
def parse_equal
  skip_whitespaces
  @scanner.scan(/=/)
end
parse_expr() click to toggle source
# File lib/datasets/cldr-plurals.rb, line 264
def parse_expr
  operand = parse_operand
  operator = parse_expr_operator
  if operator
    value = parse_value
    if value.nil?
      raise Error, "no value for #{operator}: #{@scanner.inspect}"
    end
    [operator, operand, value]
  else
    operand
  end
end
parse_expr_operator() click to toggle source
# File lib/datasets/cldr-plurals.rb, line 283
def parse_expr_operator
  skip_whitespaces
  if @scanner.scan(/(?:mod|%)/)
    :mod
  else
    nil
  end
end
parse_in() click to toggle source
# File lib/datasets/cldr-plurals.rb, line 226
def parse_in
  skip_whitespaces
  @scanner.scan(/in/)
end
parse_in_relation() click to toggle source
# File lib/datasets/cldr-plurals.rb, line 201
def parse_in_relation
  position = @scanner.pos
  skip_whitespaces
  expr = parse_expr
  if parse_not
    if parse_in
      operator = :not_in
    else
      @scanner.ops = position
      return nil
    end
  elsif parse_in
    operator = :in
  elsif parse_equal
    operator = :equal
  elsif parse_not_equal
    operator = :not_equal
  else
    @scanner.pos = position
    return nil
  end
  range_list = parse_range_list
  [operator, expr, range_list]
end
parse_is() click to toggle source
# File lib/datasets/cldr-plurals.rb, line 191
def parse_is
  skip_whitespaces
  @scanner.scan(/is/)
end
parse_is_relation() click to toggle source
# File lib/datasets/cldr-plurals.rb, line 171
def parse_is_relation
  position = @scanner.pos
  skip_whitespaces
  expr = parse_expr
  unless parse_is
    @scanner.pos = position
    return nil
  end
  if parse_not
    operator = :is_not
  else
    operator = :is
  end
  value = parse_value
  if value.nil?
    raise Error, "no value for #{operator}: #{@scanner.inspect}"
  end
  [operator, expr, value]
end
parse_not() click to toggle source
# File lib/datasets/cldr-plurals.rb, line 196
def parse_not
  skip_whitespaces
  @scanner.scan(/not/)
end
parse_not_equal() click to toggle source
# File lib/datasets/cldr-plurals.rb, line 236
def parse_not_equal
  skip_whitespaces
  @scanner.scan(/!=/)
end
parse_operand() click to toggle source
# File lib/datasets/cldr-plurals.rb, line 278
def parse_operand
  skip_whitespaces
  @scanner.scan(/[niftvwce]/)
end
parse_or() click to toggle source
# File lib/datasets/cldr-plurals.rb, line 140
def parse_or
  skip_whitespaces
  @scanner.scan(/or/)
end
parse_range() click to toggle source
# File lib/datasets/cldr-plurals.rb, line 302
def parse_range
  position = @scanner.pos
  range_start = parse_value
  skip_whitespaces
  unless @scanner.scan(/\.\./)
    @scanner.pos = position
    return nil
  end
  range_end = parse_value
  range_start..range_end
end
parse_range_list() click to toggle source
# File lib/datasets/cldr-plurals.rb, line 292
def parse_range_list
  ranges = [parse_range || parse_value]
  loop do
    skip_whitespaces
    break unless @scanner.scan(/,/)
    ranges << (parse_range || parse_value)
  end
  ranges
end
parse_relation() click to toggle source
# File lib/datasets/cldr-plurals.rb, line 165
def parse_relation
  parse_is_relation or
    parse_in_relation or
    parse_within_relation
end
parse_sample_list() click to toggle source
# File lib/datasets/cldr-plurals.rb, line 321
def parse_sample_list
  samples = [parse_sample_range]
  loop do
    position = @scanner.pos
    skip_whitespaces
    break unless @scanner.scan(/,/)
    sample_range = parse_sample_range
    unless sample_range
      @scanner.pos = position
      break
    end
    samples << sample_range
  end
  skip_whitespaces
  if @scanner.scan(/,/)
    skip_whitespaces
    # U+2026 HORIZONTAL ELLIPSIS
    unless @scanner.scan(/\u2026|\.\.\./)
      raise Error, "no ellipsis: #{@scanner.inspect}"
    end
    samples << :elipsis
  end
  samples
end
parse_sample_range() click to toggle source
# File lib/datasets/cldr-plurals.rb, line 346
def parse_sample_range
  value = parse_sample_value
  return nil if value.nil?
  skip_whitespaces
  if @scanner.scan(/~/)
    range_end = parse_sample_value
    value..range_end
  else
    value
  end
end
parse_sample_value() click to toggle source
# File lib/datasets/cldr-plurals.rb, line 358
def parse_sample_value
  value = parse_value
  return nil if value.nil?
  if @scanner.scan(/\./)
    skip_whitespaces
    decimal = @scanner.scan(/[0-9]+/)
    if decimal.nil?
      raise Error, "no decimal: #{@scanner.inspect}"
    end
    value += Float("0.#{decimal}")
    skip_whitespaces
  end
  if @scanner.scan(/[ce]/)
    # Workardoun for a spec bug. "e1" should be accepted.
    #
    # Spec:
    #   sampleValue     = value ('.' digit+)? ([ce] digitPos digit+)?
    #   digit           = [0-9]
    #   digitPos        = [1-9]
    e = @scanner.scan(/[1-9][0-9]*/)
    value *= 10 * Integer(e, 10)
  end
  value
end
parse_value() click to toggle source
# File lib/datasets/cldr-plurals.rb, line 314
def parse_value
  skip_whitespaces
  value = @scanner.scan(/\d+/)
  return nil if value.nil?
  Integer(value, 10)
end
parse_within() click to toggle source
# File lib/datasets/cldr-plurals.rb, line 259
def parse_within
  skip_whitespaces
  @scanner.scan(/within/)
end
parse_within_relation() click to toggle source
# File lib/datasets/cldr-plurals.rb, line 241
def parse_within_relation
  position = @scanner.pos
  skip_whitespaces
  expr = parse_expr
  have_not = parse_not
  unless parse_within
    @scanner.pos = position
    return nil
  end
  if have_not
    operator = :not_within
  else
    operator = :within
  end
  range_list = parse_range_list
  [operator, expr, range_list]
end
skip_whitespaces() click to toggle source
# File lib/datasets/cldr-plurals.rb, line 122
def skip_whitespaces
  @scanner.skip(/\p{Pattern_White_Space}+/)
end