tcepsni 0.1.1 → 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: 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: