tcepsni 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f68a9bde443479e50e62f0f201a17c986eaad09e686e48adb24fa6a600ce38cb
4
- data.tar.gz: 2e718d175261eb10ded9c3fbcad4926b6c98e5072d0a81d148aa1f40c6f04571
3
+ metadata.gz: 3695b6a1a12979fa76824819d4f839ce4e3b9a2af040f97eca9e474609c5c1f2
4
+ data.tar.gz: 373da53b2e230d3510f2043f25b8ae7cf017fae689fdfbaa92a388be4155ebcd
5
5
  SHA512:
6
- metadata.gz: 232ff2963d9bca49cea25eb7cc90bc6fc08815808058289e61b6ffae5dc8ee1516b43a2d56b64fde23c707034b7d1d1c068c417fc66a1e21503327f32237d5bb
7
- data.tar.gz: aadf113b5b26b20fc9d648ead1b43bf29f1c48eddd977348f0ef195af98dc0ab5fed4654dfd79a7b4f3009411b47a3d97e4834143c3ee99f6dab0b724300135c
6
+ metadata.gz: f646aecf1094e4e171581b52ca0e48dccddc4464bf158f4c30b93e8df7ef7b8989b240e9c61abf25a7cb65c2c9a29ec6978529f627a1c360f0fede299624d297
7
+ data.tar.gz: 573e5e82c5c7a0b476ee93255fe6b6fc7992febcee911e54ee1b852c9acacca29950bc67183ec01cbed7c3cf3cfcce3edd21137f1c04400bff42959d6f1d72c8
data/CHANGELOG.md CHANGED
@@ -2,6 +2,25 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.2.0 - 2023-10-20
6
+
7
+ ### Added
8
+
9
+ - `Time` support
10
+ - `Pathname` support
11
+ - `Encoding` support
12
+ - `IPAddr` support
13
+ - `ActiveSupport::OrderedOptions` support
14
+ - `Rails::Paths::Root` support
15
+ - `Float` support
16
+
17
+ ### Fixed
18
+
19
+ - Parsing the empty array
20
+ - Parsing the object with some attributes
21
+ - Parsing the `nil`, `true`, and `false` literals
22
+ - Detecting `StringScanner`-like object
23
+
5
24
  ## 0.1.1 - 2023-10-02
6
25
 
7
26
  ### Changed
@@ -1,3 +1,3 @@
1
1
  module Tcepsni
2
- VERSION = '0.1.1'
2
+ VERSION = '0.2.0'
3
3
  end
data/lib/tcepsni.rb CHANGED
@@ -1,4 +1,8 @@
1
1
  require 'strscan'
2
+ require 'pathname'
3
+ require 'ipaddr'
4
+ require 'active_support/ordered_options'
5
+ require 'rails/paths'
2
6
  require_relative 'tcepsni/version'
3
7
 
4
8
  module Tcepsni
@@ -14,11 +18,11 @@ module Tcepsni
14
18
 
15
19
  # If it returned nil when failed, it collides with normal nil value.
16
20
  def parse_expression(scanner)
17
- if scanner.skip('nil')
21
+ if scanner.skip(/nil\b/)
18
22
  nil
19
- elsif scanner.skip('true')
23
+ elsif scanner.skip(/true\b/)
20
24
  true
21
- elsif scanner.skip('false')
25
+ elsif scanner.skip(/false\b/)
22
26
  false
23
27
  elsif scanner.skip(':')
24
28
  parse_identifier(scanner)
@@ -31,14 +35,26 @@ module Tcepsni
31
35
  elsif scanner.skip('#<')
32
36
  parse_object(scanner)
33
37
  else
34
- parse_integer(scanner) or parse_class(scanner)
38
+ if (digits = scanner.scan(/\d+/))
39
+ int = Integer(digits)
40
+ if scanner.skip('.')
41
+ int2 = parse_integer(scanner)
42
+ Float("#{int}.#{int2}")
43
+ elsif digits.size == 4 && scanner.skip(/-(?<month>[01]\d)-(?<day>\d\d) (?<hour>[012]\d):(?<minute>[0-5]\d):(?<second>[0-5]\d) (?<zone>[+-]\d\d\d\d)/)
44
+ Time.new(Integer(int), scanner[:month], scanner[:day],
45
+ scanner[:hour], scanner[:minute], scanner[:second],
46
+ scanner[:zone])
47
+ else
48
+ int
49
+ end
50
+ else
51
+ parse_class(scanner)
52
+ end
35
53
  end
36
54
  end
37
55
 
38
- # Returns nil if it's not integer.
39
- # This doesn't raise error for convenience.
40
56
  def parse_integer(scanner)
41
- int = scanner.scan(/\d+/) or return
57
+ int = scanner.scan(/\d+/) or raise Error, 'no digits'
42
58
  Integer(int)
43
59
  end
44
60
 
@@ -62,13 +78,12 @@ module Tcepsni
62
78
  end
63
79
 
64
80
  def parse_array(scanner)
65
- result = [parse_expression(scanner)]
81
+ result = []
66
82
  until scanner.eos?
83
+ result << parse_expression(scanner) rescue break
67
84
  scanner.skip(', ') or break
68
- scanner.eos? and raise Error, 'incomplete array'
69
- result << parse_expression(scanner)
70
85
  end
71
- scanner.skip(']') or raise Error, 'no array closer'
86
+ scanner.skip(']') or raise Error, "no array closer: #{scanner.inspect}"
72
87
  result
73
88
  end
74
89
 
@@ -97,7 +112,7 @@ module Tcepsni
97
112
  end
98
113
 
99
114
  def parse_capitalized_identifier(scanner)
100
- id = scanner.scan(/[A-Z][A-Za-z0-9_]*/) or raise Error, 'no capitalized identifier'
115
+ id = scanner.scan(/[A-Z][A-Za-z0-9_]*/) or raise Error, "no capitalized identifier: #{scanner.inspect}"
101
116
  id.intern
102
117
  end
103
118
 
@@ -105,30 +120,70 @@ module Tcepsni
105
120
  klass = parse_class(scanner)
106
121
  if klass.string_scanner?
107
122
  scanner.skip(' ') or raise Error, 'no space after string scanner class name'
108
- pos = parse_integer(scanner) or raise Error, 'no string scanner pos'
123
+ pos = parse_integer(scanner)
109
124
  scanner.skip('/') or raise Error, 'no string scanner progress delimiter slash'
110
- total = parse_integer(scanner) or raise Error, 'no string scanner total size'
125
+ total = parse_integer(scanner)
111
126
  scanner.skip(' "') or raise Error, 'no space after string scanner progress'
112
127
  scanned = parse_string(scanner)
113
128
  scanner.skip(' @ "') or raise Error, "no string scanner mark in string: #{scanner.inspect}"
114
129
  rest = parse_string(scanner)
115
130
  scanner.skip('>') or raise Error, 'no string scanner closer'
116
- Tcepsni::StringScanner.new(pos: pos, total: total, scanned: scanned, rest: rest)
131
+ Tcepsni::Vendor::StringScanner.new(pos: pos, total: total, scanned: scanned, rest: rest)
132
+ elsif klass.pathname?
133
+ scanner.skip(':') or raise Error, 'no colon between Pathname class name and path string'
134
+ scanner.skip(/(?<path>[^>]+)>/) or raise Error, 'invalid path string and closer'
135
+ Pathname(scanner[:path])
136
+ elsif klass.encoding?
137
+ scanner.skip(':') or raise Error, 'no colon separator in encoding object'
138
+ scanner.skip(/(?<name>[^>]+)>/) or raise Error, 'invalid encoding name'
139
+ Encoding.find(scanner[:name])
140
+ elsif klass.ipaddr?
141
+ scanner.skip(/: IPv[46]:(?<addr>[^>]+)>/) or raise Error, 'invalid IP address format error'
142
+ IPAddr.new(scanner[:addr])
143
+ elsif klass.active_support_ordered_options?
144
+ scanner.skip(' {') or raise Error, 'no space between ActiveSupport::OrderedOptions class name and hash opener'
145
+ hash = parse_hash(scanner)
146
+ scanner.skip('>') or raise Error, "no ActiveSupport ordered options closer: #{scanner.inspect}"
147
+ options = ActiveSupport::OrderedOptions.new
148
+ hash.each do |key, val|
149
+ options[key] = val
150
+ end
151
+ options
152
+ elsif klass.rails_paths_root?
153
+ memory_reference = parse_memory_reference(scanner)
154
+ if scanner.skip(' ...>')
155
+ attributes = {}
156
+ else
157
+ attributes = parse_attributes(scanner)
158
+ end
159
+ Tcepsni::Vendor::Rails::Paths::Root.new(memory_reference, attributes)
117
160
  else
118
- scanner.skip(/:(?<memory_reference>0x[0-9a-f]{16})/) or raise Error, "no memory reference: #{scanner.inspect}"
119
- memory_reference = Integer(scanner[:memory_reference])
120
- attributes = {}
121
- unless scanner.skip('>')
122
- until scanner.eos?
123
- scanner.skip(' @') or break
124
- key = parse_identifier(scanner)
125
- scanner.skip('=') or raise Error, 'no attribute keyval equal char'
126
- attributes[key.intern] = parse_expression(scanner)
127
- scanner.skip(',') or break
161
+ memory_reference = parse_memory_reference(scanner)
162
+ attributes = parse_attributes(scanner)
163
+ Tcepsni::Object.new(klass: klass, memory_reference: memory_reference, attributes: attributes)
164
+ end
165
+ end
166
+
167
+ def parse_attributes(scanner)
168
+ attributes = {}
169
+ unless scanner.skip('>')
170
+ until scanner.eos?
171
+ scanner.skip(' @') or break
172
+ key = parse_identifier(scanner)
173
+ scanner.skip('=') or raise Error, 'no attribute keyval equal char'
174
+ attributes[key.intern] = parse_expression(scanner)
175
+ unless scanner.skip(',')
176
+ scanner.skip('>') or raise Error, "no object closer: #{scanner.inspect}"
177
+ break
128
178
  end
129
179
  end
130
- Tcepsni::Object.new(klass: klass, memory_reference: memory_reference, attributes: attributes)
131
180
  end
181
+ attributes
182
+ end
183
+
184
+ def parse_memory_reference(scanner)
185
+ scanner.skip(/:(?<memory_reference>0x[0-9a-f]{16})/) or raise Error, "no memory reference: #{scanner.inspect}"
186
+ Integer(scanner[:memory_reference])
132
187
  end
133
188
  end
134
189
 
@@ -141,7 +196,27 @@ module Tcepsni
141
196
  end
142
197
 
143
198
  def string_scanner?
144
- [name, *namespaces].include?(:StringScanner)
199
+ @name == :StringScanner && @namespaces.empty?
200
+ end
201
+
202
+ def pathname?
203
+ @name == :Pathname
204
+ end
205
+
206
+ def encoding?
207
+ @name == :Encoding && @namespaces.empty?
208
+ end
209
+
210
+ def ipaddr?
211
+ @name == :IPAddr && @namespaces.empty?
212
+ end
213
+
214
+ def active_support_ordered_options?
215
+ @name == :OrderedOptions && @namespaces == [:ActiveSupport]
216
+ end
217
+
218
+ def rails_paths_root?
219
+ @name == :Root && @namespaces == [:Rails, :Paths]
145
220
  end
146
221
  end
147
222
 
@@ -155,14 +230,29 @@ module Tcepsni
155
230
  end
156
231
  end
157
232
 
158
- class StringScanner
159
- attr_reader :pos, :total, :scanned, :rest
233
+ module Vendor
234
+ class StringScanner
235
+ attr_reader :pos, :total, :scanned, :rest
160
236
 
161
- def initialize(pos:, total:, scanned:, rest:)
162
- @pos = pos
163
- @total = total
164
- @scanned = scanned
165
- @rest = rest
237
+ def initialize(pos:, total:, scanned:, rest:)
238
+ @pos = pos
239
+ @total = total
240
+ @scanned = scanned
241
+ @rest = rest
242
+ end
243
+ end
244
+
245
+ module Rails
246
+ module Paths
247
+ class Root
248
+ attr_reader :memory_reference, :attributes
249
+
250
+ def initialize(memory_reference, attributes)
251
+ @memory_reference = memory_reference
252
+ @attributes = attributes
253
+ end
254
+ end
255
+ end
166
256
  end
167
257
  end
168
258
  end
data/sig/tcepsni.gen.rbs CHANGED
@@ -5,20 +5,27 @@ module Tcepsni
5
5
  VERSION: String
6
6
 
7
7
  # def self.parse: (String str) -> parsed
8
- def self.parse_expression: (untyped scanner) -> ((Array[(Array[(Class | Integer | Object | String | StringScanner | bool)?] | Class | Hash[(Class | Integer | Object | String | StringScanner | bool)?, (Class | Integer | Object | String | StringScanner | bool)?] | Integer | Object | String | StringScanner | bool)?] | Class | Hash[(Class | Integer | Object | String | StringScanner | bool)?, (Array[(Class | Integer | Object | String | StringScanner | bool)?] | Class | Hash[(Class | Integer | Object | String | StringScanner | bool)?, (Class | Integer | Object | String | StringScanner | bool)?] | Integer | Object | String | StringScanner | bool)?] | Integer | Object | String | StringScanner | bool)?)
9
- def self.parse_integer: (untyped scanner) -> Integer?
8
+ def self.parse_expression: (untyped scanner) -> ((Array[(Array[(Class | Float | Integer | Object | String | Time | bool)?] | Hash[Object?, Object?] | Object)?] | Hash[Object?, (Array[Object?] | Hash[Object?, Object?] | Object)?] | Object)?)
9
+ def self.parse_integer: (untyped scanner) -> Integer
10
10
  def self.parse_identifier: (untyped scanner) -> untyped
11
11
  def self.parse_string: (untyped scanner) -> String
12
- def self.parse_array: (untyped scanner) -> (Array[(Array[(Array[untyped] | Class | Hash[(Class | Integer | Object | String | StringScanner | bool)?, untyped] | Integer | Object | String | StringScanner | bool)?] | Class | Hash[(Class | Integer | Object | String | StringScanner | bool)?, (Array[untyped] | Class | Hash[(Class | Integer | Object | String | StringScanner | bool)?, untyped] | Integer | Object | String | StringScanner | bool)?] | Integer | Object | String | StringScanner | bool)?])
13
- def self.parse_hash: (untyped scanner) -> (Hash[(Class | Integer | Object | String | StringScanner | bool)?, (Array[(Array[untyped] | Class | Hash[(Class | Integer | Object | String | StringScanner | bool)?, untyped] | Integer | Object | String | StringScanner | bool)?] | Class | Hash[(Class | Integer | Object | String | StringScanner | bool)?, (Array[untyped] | Class | Hash[(Class | Integer | Object | String | StringScanner | bool)?, untyped] | Integer | Object | String | StringScanner | bool)?] | Integer | Object | String | StringScanner | bool)?])
12
+ def self.parse_array: (untyped scanner) -> (Array[(Array[(Array[untyped] | Class | Float | Hash[(Class | Float | Integer | Object | String | Time | bool)?, untyped] | Integer | Object | String | Time | bool)?] | Hash[Object?, (Array[untyped] | Hash[Object?, untyped] | Object)?] | Object)?])
13
+ def self.parse_hash: (untyped scanner) -> (Hash[Object?, (Array[(Array[untyped] | Hash[Object?, untyped] | Object)?] | Hash[Object?, (Array[untyped] | Hash[Object?, untyped] | Object)?] | Object)?])
14
14
  def self.parse_class: (untyped scanner) -> Class
15
15
  def self.parse_capitalized_identifier: (untyped scanner) -> untyped
16
- def self.parse_object: (untyped scanner) -> (Object | StringScanner)
16
+ def self.parse_object: (untyped scanner) -> (Encoding | IPAddr | Object | Pathname | Vendor::Rails::Paths::Root | Vendor::StringScanner)
17
+ def self.parse_attributes: (untyped scanner) -> (Hash[untyped, (Array[(Array[untyped] | Hash[Object?, untyped] | Object)?] | Hash[Object?, (Array[untyped] | Hash[Object?, untyped] | Object)?] | Object)?])
18
+ def self.parse_memory_reference: (untyped scanner) -> Integer
17
19
 
18
20
  class Class
19
21
  # def initialize: (name: Symbol?, namespaces: Array[Symbol]) -> void
20
22
  # def ==: (Class other) -> bool
21
23
  # def string_scanner?: -> bool
24
+ # def pathname?: -> bool
25
+ # def encoding?: -> bool
26
+ # def ipaddr?: -> bool
27
+ # def active_support_ordered_options?: -> bool
28
+ # def rails_paths_root?: -> bool
22
29
  # attr_reader name: Symbol?
23
30
  # attr_reader namespaces: Array[Symbol]
24
31
  end
@@ -30,11 +37,23 @@ module Tcepsni
30
37
  # attr_reader memory_reference: Integer
31
38
  end
32
39
 
33
- class StringScanner
34
- # def initialize: (pos: Integer, total: Integer, scanned: String, rest: String) -> void
35
- # attr_reader pos: Integer
36
- # attr_reader total: Integer
37
- # attr_reader scanned: String
38
- # attr_reader rest: String
40
+ module Vendor
41
+ class StringScanner
42
+ # def initialize: (pos: Integer, total: Integer, scanned: String, rest: String) -> void
43
+ # attr_reader pos: Integer
44
+ # attr_reader total: Integer
45
+ # attr_reader scanned: String
46
+ # attr_reader rest: String
47
+ end
48
+
49
+ module Rails
50
+ module Paths
51
+ class Root
52
+ # def initialize: (Integer memory_reference, Hash[Symbol, parsed?] attributes) -> void
53
+ # attr_reader memory_reference: Integer
54
+ # attr_reader attributes: Hash[Symbol, (Array[untyped] | Array[untyped] | Class | Hash[untyped, untyped] | Hash[untyped, untyped] | Integer | Object | String | StringScanner | Symbol | bool)?]
55
+ end
56
+ end
57
+ end
39
58
  end
40
59
  end
data/sig/tcepsni.rbs CHANGED
@@ -16,6 +16,11 @@ module Tcepsni
16
16
  def initialize: (name: Symbol?, namespaces: Array[Symbol]) -> void
17
17
  def ==: (Class other) -> bool
18
18
  def string_scanner?: -> bool
19
+ def pathname?: -> bool
20
+ def encoding?: -> bool
21
+ def ipaddr?: -> bool
22
+ def active_support_ordered_options?: -> bool
23
+ def rails_paths_root?: -> bool
19
24
  end
20
25
 
21
26
  class Object
@@ -25,11 +30,23 @@ module Tcepsni
25
30
  def initialize: (klass: Class, attributes: Hash[Symbol, parsed], memory_reference: Integer) -> void
26
31
  end
27
32
 
28
- class StringScanner
29
- attr_reader pos: Integer
30
- attr_reader total: Integer
31
- attr_reader scanned: String
32
- attr_reader rest: String
33
- def initialize: (pos: Integer, total: Integer, scanned: String, rest: String) -> void
33
+ module Vendor
34
+ class StringScanner
35
+ attr_reader pos: Integer
36
+ attr_reader total: Integer
37
+ attr_reader scanned: String
38
+ attr_reader rest: String
39
+ def initialize: (pos: Integer, total: Integer, scanned: String, rest: String) -> void
40
+ end
41
+
42
+ module Rails
43
+ module Paths
44
+ class Root
45
+ attr_reader memory_reference: Integer
46
+ attr_reader attributes: Hash[Symbol, parsed?]
47
+ def initialize: (Integer memory_reference, Hash[Symbol, parsed?] attributes) -> void
48
+ end
49
+ end
50
+ end
34
51
  end
35
52
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tcepsni
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - gemmaro
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-01 00:00:00.000000000 Z
11
+ date: 2023-10-20 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Tcepsni gem is a parser library for inspected strings of Ruby objects.
14
14
  email: