Class: JSON5::RubyValueBuilder

Inherits:
Object
  • Object
show all
Defined in:
lib/json5/ruby_value_builder.rb

Constant Summary collapse

VALID_DUPLICATE_POLICIES =
%i[last first error preserve].freeze
VALID_NUMBER_MODES =
%i[native float lexeme].freeze
VALID_STRING_MODES =
%i[ruby code_units].freeze

Instance Method Summary collapse

Constructor Details

#initialize(number_mode: :native, string_mode: :ruby, lone_surrogate: :error, duplicate_keys: :last, limits: Limits.default, diagnostics: nil, warn_duplicate_keys: false, freeze_values: false, source: nil, filename: nil) ⇒ RubyValueBuilder

Returns a new instance of RubyValueBuilder.



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/json5/ruby_value_builder.rb', line 9

def initialize(number_mode: :native, string_mode: :ruby, lone_surrogate: :error,
               duplicate_keys: :last, limits: Limits.default, diagnostics: nil,
               warn_duplicate_keys: false, freeze_values: false, source: nil, filename: nil)
  validate_option!(VALID_NUMBER_MODES, number_mode, :number_mode)
  validate_option!(VALID_STRING_MODES, string_mode, :string_mode)
  validate_option!(%i[error replace], lone_surrogate, :lone_surrogate)
  validate_option!(VALID_DUPLICATE_POLICIES, duplicate_keys, :duplicate_keys)
  unless warn_duplicate_keys == true || warn_duplicate_keys == false
    raise ArgumentError, "warn_duplicate_keys must be true or false"
  end
  @number_mode = number_mode
  @string_mode = string_mode
  @lone_surrogate = lone_surrogate
  @duplicate_keys = duplicate_keys
  @limits = limits
  @diagnostics = DiagnosticSink.build(diagnostics, filename: filename)
  @warn_duplicate_keys = warn_duplicate_keys
  @freeze_values = freeze_values
  @source = source
  @filename = filename
  @value_tokens = {}
  @key_tokens = {}
end

Instance Method Details

#append_element(elements, element, _comma = nil) ⇒ Object



157
158
159
160
# File 'lib/json5/ruby_value_builder.rb', line 157

def append_element(elements, element, _comma = nil)
  enforce_entry_limit(elements.length + 1)
  elements << element
end

#append_member(members, member, _comma = nil) ⇒ Object



141
142
143
144
# File 'lib/json5/ruby_value_builder.rb', line 141

def append_member(members, member, _comma = nil)
  enforce_entry_limit(members.length + 1)
  members << member
end

#array(values, *_punctuation) ⇒ Object



146
147
148
149
150
# File 'lib/json5/ruby_value_builder.rb', line 146

def array(values, *_punctuation)
  enforce_entry_limit(values.length)
  result = values
  @freeze_values ? result.freeze : result
end

#element_list(element) ⇒ Object



152
153
154
155
# File 'lib/json5/ruby_value_builder.rb', line 152

def element_list(element)
  enforce_entry_limit(1)
  [element]
end

#literal(value, result) ⇒ Object



73
74
75
76
# File 'lib/json5/ruby_value_builder.rb', line 73

def literal(value, result)
  take_value_token(value)
  result
end

#member(name, _colon, value) ⇒ Object



132
133
134
# File 'lib/json5/ruby_value_builder.rb', line 132

def member(name, _colon, value)
  [name, value]
end

#member_list(member) ⇒ Object



136
137
138
139
# File 'lib/json5/ruby_value_builder.rb', line 136

def member_list(member)
  enforce_entry_limit(1)
  [member]
end

#member_name(value) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/json5/ruby_value_builder.rb', line 78

def member_name(value)
  token = take_value_token(value)
  if @string_mode == :code_units
    key = value.is_a?(ECMAString) ? value : ECMAString.new(value.raw.codepoints)
    remember_key(key, token)
    return key
  end
  raw = value.is_a?(NumberValue) ? value.raw : value
  if raw.is_a?(String)
    key = raw.to_s.dup.freeze
    remember_key(key, token)
    return key
  end

  begin
    key = value.to_utf8(lone_surrogate: :error).freeze
    remember_key(key, token)
    key
  rescue ConversionError => error
    if @duplicate_keys == :preserve
      remember_key(value, token)
      return value
    end
    raise contextual_error(ConversionError.new(
      "lone surrogate cannot be used as a Ruby Hash key",
      code: "lone_surrogate_key"
    ), token)
  end
end

#number(value) ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/json5/ruby_value_builder.rb', line 57

def number(value)
  token = take_value_token(value)
  number_value = value.is_a?(NumberValue) ? value : NumberValue.new(value)
  result = case @number_mode
  when :lexeme
    number_value
  when :float
    convert_float(number_value)
  else
    number_value.to_native
  end
  @freeze_values ? result.freeze : result
rescue JSON5::Error => error
  raise contextual_error(error, token)
end

#object(members, *_punctuation) ⇒ Object



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/json5/ruby_value_builder.rb', line 108

def object(members, *_punctuation)
  enforce_entry_limit(members.length)
  if @duplicate_keys == :preserve
    members.each { |key, _value| @key_tokens.delete(key.object_id) }
    return ObjectValue.new(members)
  end

  result = {}
  members.each do |key, value|
    token = @key_tokens.delete(key.object_id)
    if result.key?(key)
      emit_duplicate_key_warning(token) if @warn_duplicate_keys && %i[last first].include?(@duplicate_keys)
      case @duplicate_keys
      when :first
        next
      when :error
        raise contextual_error(DuplicateKeyError.new("duplicate object member name", code: "duplicate_key"), token)
      end
    end
    result[key] = value
  end
  @freeze_values ? result.freeze : result
end

#observe_token(token) ⇒ Object



33
34
35
36
37
# File 'lib/json5/ruby_value_builder.rb', line 33

def observe_token(token)
  @last_token = token
  value = token.value
  @value_tokens[value.object_id] = token if value
end

#retain_punctuation_tokens?Boolean

Returns:

  • (Boolean)


39
40
41
# File 'lib/json5/ruby_value_builder.rb', line 39

def retain_punctuation_tokens?
  false
end

#string(value) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/json5/ruby_value_builder.rb', line 43

def string(value)
  token = take_value_token(value)
  result = case @string_mode
  when :code_units
    value
  else
    emit_lone_surrogate_warning(value, token) if @lone_surrogate == :replace
    value.to_utf8(lone_surrogate: @lone_surrogate)
  end
  @freeze_values ? result.freeze : result
rescue ConversionError => error
  raise contextual_error(error, token)
end