tcepsni 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 83d6d7256e483e7259f7ca6a630969fdea2b00a94674130f33af6fc8992d50e1
4
- data.tar.gz: a4c891f49b92e1fa79fc36759665357eb47e5de1fe774f6a9e4c6c1e904d2a89
3
+ metadata.gz: 3695b6a1a12979fa76824819d4f839ce4e3b9a2af040f97eca9e474609c5c1f2
4
+ data.tar.gz: 373da53b2e230d3510f2043f25b8ae7cf017fae689fdfbaa92a388be4155ebcd
5
5
  SHA512:
6
- metadata.gz: 584b8d5ca524b82d8b1ff0e0ba04a8b76768c9ca64488699f2ad737bf337790c50fc9c938bdad75fe0630480b24c125bff80629e820e26a556c11d13e599463b
7
- data.tar.gz: 678dd3a11bdcaacc47f7a26d545dd5b4466b2e1f471b4b8f233c23c816658c25bc01a1c0bae2b688eb411f68518718fe571863917bccc108b8fa51b230d6da3d
6
+ metadata.gz: f646aecf1094e4e171581b52ca0e48dccddc4464bf158f4c30b93e8df7ef7b8989b240e9c61abf25a7cb65c2c9a29ec6978529f627a1c360f0fede299624d297
7
+ data.tar.gz: 573e5e82c5c7a0b476ee93255fe6b6fc7992febcee911e54ee1b852c9acacca29950bc67183ec01cbed7c3cf3cfcce3edd21137f1c04400bff42959d6f1d72c8
data/CHANGELOG.md CHANGED
@@ -2,6 +2,35 @@
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
+
24
+ ## 0.1.1 - 2023-10-02
25
+
26
+ ### Changed
27
+
28
+ - Object's memory reference attribute has changed to integer from string
29
+
30
+ ### Fixed
31
+
32
+ - String scanner parser now supports string with quotes
33
+
5
34
  ## 0.1.0 - 2023-10-01
6
35
 
7
36
  - Initial release
@@ -1,3 +1,3 @@
1
1
  module Tcepsni
2
- VERSION = '0.1.0'
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
@@ -12,32 +16,53 @@ module Tcepsni
12
16
  class << self
13
17
  private
14
18
 
19
+ # If it returned nil when failed, it collides with normal nil value.
15
20
  def parse_expression(scanner)
16
- if (integer = scanner.scan(/\d+/))
17
- Integer(integer)
18
- elsif scanner.skip('nil')
21
+ if scanner.skip(/nil\b/)
19
22
  nil
20
- elsif scanner.skip('true')
23
+ elsif scanner.skip(/true\b/)
21
24
  true
22
- elsif scanner.skip('false')
25
+ elsif scanner.skip(/false\b/)
23
26
  false
24
- elsif scanner.skip(/:(?<name>[A-Za-z_][A-Za-z0-9_]*)/)
25
- scanner[:name].intern
27
+ elsif scanner.skip(':')
28
+ parse_identifier(scanner)
26
29
  elsif scanner.skip('"')
27
30
  parse_string(scanner)
28
31
  elsif scanner.skip('[')
29
32
  parse_array(scanner)
30
33
  elsif scanner.skip('{')
31
34
  parse_hash(scanner)
32
- elsif (name = scanner.scan(/[A-Z][A-Za-z0-9_]*/))
33
- parse_class(scanner, name: name.intern)
34
35
  elsif scanner.skip('#<')
35
36
  parse_object(scanner)
36
37
  else
37
- raise Error, "invalid format: #{ scanner.inspect }"
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
38
53
  end
39
54
  end
40
55
 
56
+ def parse_integer(scanner)
57
+ int = scanner.scan(/\d+/) or raise Error, 'no digits'
58
+ Integer(int)
59
+ end
60
+
61
+ def parse_identifier(scanner)
62
+ id = scanner.scan(/[A-Za-z_][A-Za-z0-9_]*/) or raise Error, 'no identifier'
63
+ id.intern
64
+ end
65
+
41
66
  def parse_string(scanner)
42
67
  result = ''
43
68
  until scanner.eos?
@@ -53,13 +78,12 @@ module Tcepsni
53
78
  end
54
79
 
55
80
  def parse_array(scanner)
56
- result = [parse_expression(scanner)]
81
+ result = []
57
82
  until scanner.eos?
83
+ result << parse_expression(scanner) rescue break
58
84
  scanner.skip(', ') or break
59
- scanner.eos? and raise Error, 'incomplete array'
60
- result << parse_expression(scanner)
61
85
  end
62
- scanner.skip(']') or raise Error, 'no array closer'
86
+ scanner.skip(']') or raise Error, "no array closer: #{scanner.inspect}"
63
87
  result
64
88
  end
65
89
 
@@ -76,42 +100,90 @@ module Tcepsni
76
100
  result
77
101
  end
78
102
 
79
- def parse_class(scanner, name: nil)
80
- unless name
81
- name = scanner.scan(/[A-Z][A-Za-z0-9_]*/) or raise Error, 'no class name'
82
- name = name.intern
83
- end
84
- namespaces = [name]
103
+ def parse_class(scanner)
104
+ namespaces = [parse_capitalized_identifier(scanner)]
85
105
  until scanner.eos?
86
106
  scanner.skip('::') or break
87
107
  scanner.eos? and raise Error, 'incomplete class'
88
- namespace = scanner.scan(/[A-Z][A-Za-z0-9_]*/) or raise Error, 'no namespace'
89
- namespaces << namespace.intern
108
+ namespaces << parse_capitalized_identifier(scanner)
90
109
  end
91
110
  *namespaces, name = namespaces
92
111
  Tcepsni::Class.new(name: name, namespaces: namespaces)
93
112
  end
94
113
 
114
+ def parse_capitalized_identifier(scanner)
115
+ id = scanner.scan(/[A-Z][A-Za-z0-9_]*/) or raise Error, "no capitalized identifier: #{scanner.inspect}"
116
+ id.intern
117
+ end
118
+
95
119
  def parse_object(scanner)
96
120
  klass = parse_class(scanner)
97
121
  if klass.string_scanner?
98
- scanner.skip(%r{ (?<pos>\d+)/(?<total>\d+) "(?<scanned>[^"]+)" @ "(?<rest>[^"]+)">}) or raise Error, 'invalid string scanner'
99
- Tcepsni::StringScanner.new(pos: Integer(scanner[:pos]), total: Integer(scanner[:total]), scanned: scanner[:scanned], rest: scanner[:rest])
122
+ scanner.skip(' ') or raise Error, 'no space after string scanner class name'
123
+ pos = parse_integer(scanner)
124
+ scanner.skip('/') or raise Error, 'no string scanner progress delimiter slash'
125
+ total = parse_integer(scanner)
126
+ scanner.skip(' "') or raise Error, 'no space after string scanner progress'
127
+ scanned = parse_string(scanner)
128
+ scanner.skip(' @ "') or raise Error, "no string scanner mark in string: #{scanner.inspect}"
129
+ rest = parse_string(scanner)
130
+ scanner.skip('>') or raise Error, 'no string scanner closer'
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)
100
160
  else
101
- scanner.skip(/:(?<memory_reference>0x[0-9a-f]{16})/) or raise Error, "no memory reference: #{scanner.inspect}"
102
- memory_reference = scanner[:memory_reference]
103
- attributes = {}
104
- unless scanner.skip('>')
105
- until scanner.eos?
106
- scanner.skip(/ @(?<key>[A-Za-z_][A-Za-z_0-9]*)=/) or break
107
- key = scanner[:key].intern
108
- value = parse_expression(scanner)
109
- attributes[key] = value
110
- 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
111
178
  end
112
179
  end
113
- Tcepsni::Object.new(klass: klass, memory_reference: memory_reference, attributes: attributes)
114
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])
115
187
  end
116
188
  end
117
189
 
@@ -124,7 +196,27 @@ module Tcepsni
124
196
  end
125
197
 
126
198
  def string_scanner?
127
- [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]
128
220
  end
129
221
  end
130
222
 
@@ -138,14 +230,29 @@ module Tcepsni
138
230
  end
139
231
  end
140
232
 
141
- class StringScanner
142
- attr_reader :pos, :total, :scanned, :rest
233
+ module Vendor
234
+ class StringScanner
235
+ attr_reader :pos, :total, :scanned, :rest
236
+
237
+ def initialize(pos:, total:, scanned:, rest:)
238
+ @pos = pos
239
+ @total = total
240
+ @scanned = scanned
241
+ @rest = rest
242
+ end
243
+ end
143
244
 
144
- def initialize(pos:, total:, scanned:, rest:)
145
- @pos = pos
146
- @total = total
147
- @scanned = scanned
148
- @rest = rest
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
149
256
  end
150
257
  end
151
258
  end
data/sig/tcepsni.gen.rbs CHANGED
@@ -5,17 +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)?)
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
+ def self.parse_identifier: (untyped scanner) -> untyped
9
11
  def self.parse_string: (untyped scanner) -> String
10
- 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)?])
11
- 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_class: (untyped scanner, ?name: nil) -> Class
13
- def self.parse_object: (untyped scanner) -> (Object | StringScanner)
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
+ def self.parse_class: (untyped scanner) -> Class
15
+ def self.parse_capitalized_identifier: (untyped scanner) -> untyped
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
14
19
 
15
20
  class Class
16
21
  # def initialize: (name: Symbol?, namespaces: Array[Symbol]) -> void
17
22
  # def ==: (Class other) -> bool
18
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
19
29
  # attr_reader name: Symbol?
20
30
  # attr_reader namespaces: Array[Symbol]
21
31
  end
@@ -27,11 +37,23 @@ module Tcepsni
27
37
  # attr_reader memory_reference: Integer
28
38
  end
29
39
 
30
- class StringScanner
31
- # def initialize: (pos: Integer, total: Integer, scanned: String, rest: String) -> void
32
- # attr_reader pos: Integer
33
- # attr_reader total: Integer
34
- # attr_reader scanned: String
35
- # 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
36
58
  end
37
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.0
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:
@@ -45,7 +45,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
45
45
  - !ruby/object:Gem::Version
46
46
  version: '0'
47
47
  requirements: []
48
- rubygems_version: 3.4.10
48
+ rubygems_version: 3.3.26
49
49
  signing_key:
50
50
  specification_version: 4
51
51
  summary: inspected Ruby objects parser