tcepsni 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/lib/tcepsni/parser.rb +161 -0
- data/lib/tcepsni/vendor/activesupport/ordered_options.rb +28 -0
- data/lib/tcepsni/vendor/ipaddr.rb +20 -0
- data/lib/tcepsni/vendor/pathname.rb +21 -0
- data/lib/tcepsni/vendor/rails/paths/root.rb +37 -0
- data/lib/tcepsni/vendor/string_scanner.rb +35 -0
- data/lib/tcepsni/version.rb +1 -1
- data/lib/tcepsni.rb +3 -228
- data/sig/tcepsni.gen.rbs +57 -22
- data/sig/tcepsni.rbs +29 -14
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3c7e2dad3ee934784b32e2263cfd23a6a478ec4d648b7e02f1a02267238296e5
|
4
|
+
data.tar.gz: 5db7c50e53eea93481004efd7d2f359db19fe8099356bfd980564f3b861d52d1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fc943477c66e694f76a6711ec01a2f82893c756fe7e30b1f00e8ef82d04c63699899b28252e2d046d148af68195a049bf2ca79b8b90bde359474ac610d9d3804
|
7
|
+
data.tar.gz: 8daf45aa53eb860b306357831c0c86a17d0e847fba6cfa8a051fe932a6d355ccfee936aee3ef48949b05a418013b7e140ce37ff57108ae653f768c97359c2f94
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,17 @@
|
|
2
2
|
|
3
3
|
## Unreleased
|
4
4
|
|
5
|
+
## 0.3.0 - 2023-11-18
|
6
|
+
|
7
|
+
### Changed
|
8
|
+
|
9
|
+
- Introduced plugin system.
|
10
|
+
For example, if the source contains `Pathname` objects, use the `vendors` option like `Tcepsni.parse(str, vendors: [::Tcepsni::Vendor::Pathname])`.
|
11
|
+
|
12
|
+
### Removed
|
13
|
+
|
14
|
+
- `Tcepsni::Class#string_scanner?`, `Tcepsni::Class#pathname?`, `Tcepsni::Class#ipaddr?`, `Tcepsni::Class#active_support_ordered_options?`, and `Tcepsni::Class#rails_paths_root?`.
|
15
|
+
|
5
16
|
## 0.2.0 - 2023-10-20
|
6
17
|
|
7
18
|
### Added
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'strscan'
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module Tcepsni
|
5
|
+
class Parser
|
6
|
+
def initialize(source, vendors:)
|
7
|
+
@scanner = ::StringScanner.new(source)
|
8
|
+
@vendors = vendors.flat_map { |vendor| [*vendor.dependencies, vendor] }.uniq
|
9
|
+
end
|
10
|
+
|
11
|
+
# If it returned nil when failed, it collides with normal nil value.
|
12
|
+
def parse_expression
|
13
|
+
if @scanner.skip(/nil\b/)
|
14
|
+
nil
|
15
|
+
elsif @scanner.skip(/true\b/)
|
16
|
+
true
|
17
|
+
elsif @scanner.skip(/false\b/)
|
18
|
+
false
|
19
|
+
elsif @scanner.skip(':')
|
20
|
+
parse_identifier
|
21
|
+
elsif @scanner.skip('"')
|
22
|
+
parse_string
|
23
|
+
elsif @scanner.skip('[')
|
24
|
+
parse_array
|
25
|
+
elsif @scanner.skip('{')
|
26
|
+
parse_hash
|
27
|
+
elsif @scanner.skip('#<')
|
28
|
+
pos = @scanner.pos
|
29
|
+
@vendors.select { |vendor| vendor.object? }.each do |vendor|
|
30
|
+
begin
|
31
|
+
@scanner.pos = pos
|
32
|
+
return vendor.parse(self)
|
33
|
+
rescue Error
|
34
|
+
next
|
35
|
+
end
|
36
|
+
end
|
37
|
+
@scanner.pos = pos
|
38
|
+
parse_object
|
39
|
+
else
|
40
|
+
if (digits = @scanner.scan(/\d+/))
|
41
|
+
int = Integer(digits)
|
42
|
+
if @scanner.skip('.')
|
43
|
+
int2 = parse_integer
|
44
|
+
Float("#{int}.#{int2}")
|
45
|
+
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)/)
|
46
|
+
Time.new(Integer(int), @scanner[:month], @scanner[:day],
|
47
|
+
@scanner[:hour], @scanner[:minute], @scanner[:second],
|
48
|
+
@scanner[:zone])
|
49
|
+
else
|
50
|
+
int
|
51
|
+
end
|
52
|
+
else
|
53
|
+
parse_class
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
alias parse parse_expression
|
59
|
+
|
60
|
+
def parse_integer
|
61
|
+
int = @scanner.scan(/\d+/) or raise Error, 'no digits'
|
62
|
+
Integer(int)
|
63
|
+
end
|
64
|
+
|
65
|
+
def parse_identifier
|
66
|
+
id = @scanner.scan(/[A-Za-z_][A-Za-z0-9_]*/) or raise Error, 'no identifier'
|
67
|
+
id.intern
|
68
|
+
end
|
69
|
+
|
70
|
+
def parse_string
|
71
|
+
result = ''
|
72
|
+
until @scanner.eos?
|
73
|
+
if @scanner.skip('"')
|
74
|
+
return result
|
75
|
+
elsif @scanner.skip('\\"')
|
76
|
+
result << '"'
|
77
|
+
else
|
78
|
+
result << @scanner.getch
|
79
|
+
end
|
80
|
+
end
|
81
|
+
result
|
82
|
+
end
|
83
|
+
|
84
|
+
def parse_array
|
85
|
+
result = []
|
86
|
+
until @scanner.eos?
|
87
|
+
result << parse_expression rescue break
|
88
|
+
@scanner.skip(', ') or break
|
89
|
+
end
|
90
|
+
@scanner.skip(']') or raise Error, "no array closer: #{@scanner.inspect}"
|
91
|
+
result
|
92
|
+
end
|
93
|
+
|
94
|
+
def parse_hash
|
95
|
+
result = {}
|
96
|
+
until @scanner.eos?
|
97
|
+
key = parse_expression rescue break
|
98
|
+
@scanner.skip('=>') or raise Error, 'no hash arrow'
|
99
|
+
value = parse_expression
|
100
|
+
result[key] = value
|
101
|
+
@scanner.skip(', ') or break
|
102
|
+
end
|
103
|
+
@scanner.skip('}') or raise Error, 'no hash closer'
|
104
|
+
result
|
105
|
+
end
|
106
|
+
|
107
|
+
def parse_class
|
108
|
+
namespaces = [parse_capitalized_identifier]
|
109
|
+
until @scanner.eos?
|
110
|
+
@scanner.skip('::') or break
|
111
|
+
@scanner.eos? and raise Error, 'incomplete class'
|
112
|
+
namespaces << parse_capitalized_identifier
|
113
|
+
end
|
114
|
+
*namespaces, name = namespaces
|
115
|
+
Tcepsni::Class.new(name: name, namespaces: namespaces)
|
116
|
+
end
|
117
|
+
|
118
|
+
def parse_capitalized_identifier
|
119
|
+
id = @scanner.scan(/[A-Z][A-Za-z0-9_]*/) or raise Error, "no capitalized identifier: #{@scanner.inspect}"
|
120
|
+
id.intern
|
121
|
+
end
|
122
|
+
|
123
|
+
def parse_object
|
124
|
+
klass = parse_class
|
125
|
+
if klass.encoding?
|
126
|
+
@scanner.skip(':') or raise Error, 'no colon separator in encoding object'
|
127
|
+
@scanner.skip(/(?<name>[^>]+)>/) or raise Error, 'invalid encoding name'
|
128
|
+
Encoding.find(@scanner[:name])
|
129
|
+
else
|
130
|
+
memory_reference = parse_memory_reference
|
131
|
+
attributes = parse_attributes
|
132
|
+
Tcepsni::Object.new(klass: klass, memory_reference: memory_reference, attributes: attributes)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def parse_attributes
|
137
|
+
attributes = {}
|
138
|
+
unless @scanner.skip('>')
|
139
|
+
until @scanner.eos?
|
140
|
+
@scanner.skip(' @') or break
|
141
|
+
key = parse_identifier
|
142
|
+
@scanner.skip('=') or raise Error, 'no attribute keyval equal char'
|
143
|
+
attributes[key.intern] = parse_expression
|
144
|
+
unless @scanner.skip(',')
|
145
|
+
@scanner.skip('>') or raise Error, "no object closer: #{@scanner.inspect}"
|
146
|
+
break
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
attributes
|
151
|
+
end
|
152
|
+
|
153
|
+
def parse_memory_reference
|
154
|
+
@scanner.skip(/:(?<memory_reference>0x[0-9a-f]{16})/) or raise Error, "no memory reference: #{@scanner.inspect}"
|
155
|
+
Integer(@scanner[:memory_reference])
|
156
|
+
end
|
157
|
+
|
158
|
+
extend Forwardable
|
159
|
+
def_delegators :@scanner, :skip, :[]
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'active_support/ordered_options'
|
2
|
+
|
3
|
+
module Tcepsni
|
4
|
+
module Vendor
|
5
|
+
module ActiveSupport
|
6
|
+
class OrderedOptions
|
7
|
+
def self.parse(parser)
|
8
|
+
parser.skip('ActiveSupport::OrderedOptions {') or raise Error, 'no space between ActiveSupport::OrderedOptions class name and hash opener'
|
9
|
+
hash = parser.parse_hash
|
10
|
+
parser.skip('>') or raise Error, "no ActiveSupport ordered options closer: #{parser.scanner.inspect}"
|
11
|
+
options = ::ActiveSupport::OrderedOptions.new
|
12
|
+
hash.each do |key, val|
|
13
|
+
options[key] = val
|
14
|
+
end
|
15
|
+
options
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.dependencies
|
19
|
+
[]
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.object?
|
23
|
+
true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
|
3
|
+
module Tcepsni
|
4
|
+
module Vendor
|
5
|
+
class IPAddr
|
6
|
+
def self.parse(parser)
|
7
|
+
parser.skip(/IPAddr: IPv[46]:(?<addr>[^>]+)>/) or raise Error, 'invalid IP address format error'
|
8
|
+
::IPAddr.new(parser[:addr])
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.object?
|
12
|
+
true
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.dependencies
|
16
|
+
[]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module Tcepsni
|
4
|
+
module Vendor
|
5
|
+
class Pathname
|
6
|
+
def self.parse(parser)
|
7
|
+
parser.skip('Pathname:') or raise Error, 'no colon between Pathname class name and path string'
|
8
|
+
parser.skip(/(?<path>[^>]+)>/) or raise Error, 'invalid path string and closer'
|
9
|
+
::Pathname.new(parser[:path])
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.object?
|
13
|
+
true
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.dependencies
|
17
|
+
[]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'rails/paths'
|
2
|
+
|
3
|
+
module Tcepsni
|
4
|
+
module Vendor
|
5
|
+
module Rails
|
6
|
+
module Paths
|
7
|
+
class Root
|
8
|
+
attr_reader :memory_reference, :attributes
|
9
|
+
|
10
|
+
def initialize(memory_reference, attributes)
|
11
|
+
@memory_reference = memory_reference
|
12
|
+
@attributes = attributes
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.parse(parser)
|
16
|
+
parser.skip('Rails::Paths::Root') or raise Error, 'this is not Rails::Paths::Root'
|
17
|
+
memory_reference = parser.parse_memory_reference
|
18
|
+
if parser.skip(' ...>')
|
19
|
+
attributes = {}
|
20
|
+
else
|
21
|
+
attributes = parser.parse_attributes
|
22
|
+
end
|
23
|
+
Tcepsni::Vendor::Rails::Paths::Root.new(memory_reference, attributes)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.object?
|
27
|
+
true
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.dependencies
|
31
|
+
[::Tcepsni::Vendor::Pathname]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Tcepsni
|
2
|
+
module Vendor
|
3
|
+
class StringScanner
|
4
|
+
attr_reader :pos, :total, :scanned, :rest
|
5
|
+
|
6
|
+
def initialize(pos:, total:, scanned:, rest:)
|
7
|
+
@pos = pos
|
8
|
+
@total = total
|
9
|
+
@scanned = scanned
|
10
|
+
@rest = rest
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.parse(parser)
|
14
|
+
parser.skip('StringScanner ') or raise Error, 'no space after string scanner class name'
|
15
|
+
pos = parser.parse_integer
|
16
|
+
parser.skip('/') or raise Error, 'no string scanner progress delimiter slash'
|
17
|
+
total = parser.parse_integer
|
18
|
+
parser.skip(' "') or raise Error, 'no space after string scanner progress'
|
19
|
+
scanned = parser.parse_string
|
20
|
+
parser.skip(' @ "') or raise Error, "no string scanner mark in string: #{parser.scanner.inspect}"
|
21
|
+
rest = parser.parse_string
|
22
|
+
parser.skip('>') or raise Error, 'no string scanner closer'
|
23
|
+
Tcepsni::Vendor::StringScanner.new(pos: pos, total: total, scanned: scanned, rest: rest)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.dependencies
|
27
|
+
[]
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.object?
|
31
|
+
true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/tcepsni/version.rb
CHANGED
data/lib/tcepsni.rb
CHANGED
@@ -1,190 +1,11 @@
|
|
1
|
-
require 'strscan'
|
2
|
-
require 'pathname'
|
3
|
-
require 'ipaddr'
|
4
|
-
require 'active_support/ordered_options'
|
5
|
-
require 'rails/paths'
|
6
1
|
require_relative 'tcepsni/version'
|
2
|
+
require_relative 'tcepsni/parser'
|
7
3
|
|
8
4
|
module Tcepsni
|
9
5
|
class Error < StandardError; end
|
10
6
|
|
11
|
-
def self.parse(str)
|
12
|
-
|
13
|
-
parse_expression(scanner)
|
14
|
-
end
|
15
|
-
|
16
|
-
class << self
|
17
|
-
private
|
18
|
-
|
19
|
-
# If it returned nil when failed, it collides with normal nil value.
|
20
|
-
def parse_expression(scanner)
|
21
|
-
if scanner.skip(/nil\b/)
|
22
|
-
nil
|
23
|
-
elsif scanner.skip(/true\b/)
|
24
|
-
true
|
25
|
-
elsif scanner.skip(/false\b/)
|
26
|
-
false
|
27
|
-
elsif scanner.skip(':')
|
28
|
-
parse_identifier(scanner)
|
29
|
-
elsif scanner.skip('"')
|
30
|
-
parse_string(scanner)
|
31
|
-
elsif scanner.skip('[')
|
32
|
-
parse_array(scanner)
|
33
|
-
elsif scanner.skip('{')
|
34
|
-
parse_hash(scanner)
|
35
|
-
elsif scanner.skip('#<')
|
36
|
-
parse_object(scanner)
|
37
|
-
else
|
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
|
53
|
-
end
|
54
|
-
end
|
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
|
-
|
66
|
-
def parse_string(scanner)
|
67
|
-
result = ''
|
68
|
-
until scanner.eos?
|
69
|
-
if scanner.skip('"')
|
70
|
-
return result
|
71
|
-
elsif scanner.skip('\\"')
|
72
|
-
result << '"'
|
73
|
-
else
|
74
|
-
result << scanner.getch
|
75
|
-
end
|
76
|
-
end
|
77
|
-
result
|
78
|
-
end
|
79
|
-
|
80
|
-
def parse_array(scanner)
|
81
|
-
result = []
|
82
|
-
until scanner.eos?
|
83
|
-
result << parse_expression(scanner) rescue break
|
84
|
-
scanner.skip(', ') or break
|
85
|
-
end
|
86
|
-
scanner.skip(']') or raise Error, "no array closer: #{scanner.inspect}"
|
87
|
-
result
|
88
|
-
end
|
89
|
-
|
90
|
-
def parse_hash(scanner)
|
91
|
-
result = {}
|
92
|
-
until scanner.eos?
|
93
|
-
key = parse_expression(scanner) rescue break
|
94
|
-
scanner.skip('=>') or raise Error, 'no hash arrow'
|
95
|
-
value = parse_expression(scanner)
|
96
|
-
result[key] = value
|
97
|
-
scanner.skip(', ') or break
|
98
|
-
end
|
99
|
-
scanner.skip('}') or raise Error, 'no hash closer'
|
100
|
-
result
|
101
|
-
end
|
102
|
-
|
103
|
-
def parse_class(scanner)
|
104
|
-
namespaces = [parse_capitalized_identifier(scanner)]
|
105
|
-
until scanner.eos?
|
106
|
-
scanner.skip('::') or break
|
107
|
-
scanner.eos? and raise Error, 'incomplete class'
|
108
|
-
namespaces << parse_capitalized_identifier(scanner)
|
109
|
-
end
|
110
|
-
*namespaces, name = namespaces
|
111
|
-
Tcepsni::Class.new(name: name, namespaces: namespaces)
|
112
|
-
end
|
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
|
-
|
119
|
-
def parse_object(scanner)
|
120
|
-
klass = parse_class(scanner)
|
121
|
-
if klass.string_scanner?
|
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)
|
160
|
-
else
|
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
|
178
|
-
end
|
179
|
-
end
|
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])
|
187
|
-
end
|
7
|
+
def self.parse(str, vendors: [])
|
8
|
+
Parser.new(str, vendors: vendors).parse
|
188
9
|
end
|
189
10
|
|
190
11
|
class Class
|
@@ -195,29 +16,9 @@ module Tcepsni
|
|
195
16
|
@namespaces = namespaces
|
196
17
|
end
|
197
18
|
|
198
|
-
def string_scanner?
|
199
|
-
@name == :StringScanner && @namespaces.empty?
|
200
|
-
end
|
201
|
-
|
202
|
-
def pathname?
|
203
|
-
@name == :Pathname
|
204
|
-
end
|
205
|
-
|
206
19
|
def encoding?
|
207
20
|
@name == :Encoding && @namespaces.empty?
|
208
21
|
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]
|
220
|
-
end
|
221
22
|
end
|
222
23
|
|
223
24
|
class Object
|
@@ -229,30 +30,4 @@ module Tcepsni
|
|
229
30
|
@memory_reference = memory_reference
|
230
31
|
end
|
231
32
|
end
|
232
|
-
|
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
|
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
|
256
|
-
end
|
257
|
-
end
|
258
33
|
end
|
data/sig/tcepsni.gen.rbs
CHANGED
@@ -4,18 +4,27 @@
|
|
4
4
|
module Tcepsni
|
5
5
|
VERSION: String
|
6
6
|
|
7
|
-
# def self.parse: (String str) ->
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
def
|
15
|
-
def
|
16
|
-
|
17
|
-
def
|
18
|
-
def
|
7
|
+
# def self.parse: (String str) -> untyped
|
8
|
+
|
9
|
+
class Parser
|
10
|
+
extend Forwardable
|
11
|
+
@scanner: StringScanner
|
12
|
+
@vendors: Array[Array[untyped]]
|
13
|
+
|
14
|
+
# def initialize: (String source, vendors: Array[untyped]?) -> void
|
15
|
+
# def parse_expression: -> untyped
|
16
|
+
alias parse parse_expression
|
17
|
+
# def parse_integer: -> Integer
|
18
|
+
# def parse_identifier: -> Symbol
|
19
|
+
# def parse_string: -> String
|
20
|
+
# def parse_array: -> Array[untyped]
|
21
|
+
# def parse_hash: -> Hash[untyped, untyped]
|
22
|
+
# def parse_class: -> Class
|
23
|
+
# def parse_capitalized_identifier: -> Symbol
|
24
|
+
# def parse_object: -> Object
|
25
|
+
# def parse_attributes: -> Hash[Symbol, untyped]
|
26
|
+
# def parse_memory_reference: -> Integer
|
27
|
+
end
|
19
28
|
|
20
29
|
class Class
|
21
30
|
# def initialize: (name: Symbol?, namespaces: Array[Symbol]) -> void
|
@@ -31,29 +40,55 @@ module Tcepsni
|
|
31
40
|
end
|
32
41
|
|
33
42
|
class Object
|
34
|
-
# def initialize: (klass: Class, attributes: Hash[Symbol,
|
43
|
+
# def initialize: (klass: Class, attributes: Hash[Symbol, untyped], memory_reference: Integer) -> void
|
35
44
|
# attr_reader klass: Class
|
36
|
-
# attr_reader attributes: Hash[Symbol,
|
45
|
+
# attr_reader attributes: Hash[Symbol, untyped]
|
37
46
|
# attr_reader memory_reference: Integer
|
38
47
|
end
|
39
48
|
|
40
49
|
module Vendor
|
41
50
|
class StringScanner
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
51
|
+
attr_reader pos: untyped
|
52
|
+
attr_reader total: untyped
|
53
|
+
attr_reader scanned: untyped
|
54
|
+
attr_reader rest: untyped
|
55
|
+
def initialize: (pos: untyped, total: untyped, scanned: untyped, rest: untyped) -> void
|
56
|
+
def self.parse: (untyped parser) -> StringScanner
|
57
|
+
def self.dependencies: -> Array[untyped]
|
58
|
+
def self.object?: -> true
|
47
59
|
end
|
48
60
|
|
49
61
|
module Rails
|
50
62
|
module Paths
|
51
63
|
class Root
|
52
|
-
|
53
|
-
|
54
|
-
|
64
|
+
attr_reader memory_reference: untyped
|
65
|
+
attr_reader attributes: Hash[untyped, untyped]
|
66
|
+
def initialize: (untyped memory_reference, Hash[untyped, untyped] attributes) -> void
|
67
|
+
def self.parse: (untyped parser) -> Root
|
68
|
+
def self.object?: -> true
|
69
|
+
def self.dependencies: -> [singleton(Pathname)]
|
55
70
|
end
|
56
71
|
end
|
57
72
|
end
|
73
|
+
|
74
|
+
module ActiveSupport
|
75
|
+
class OrderedOptions
|
76
|
+
def self.parse: (untyped parser) -> untyped
|
77
|
+
def self.dependencies: -> Array[untyped]
|
78
|
+
def self.object?: -> true
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class IPAddr
|
83
|
+
def self.parse: (untyped parser) -> IPAddr
|
84
|
+
def self.object?: -> true
|
85
|
+
def self.dependencies: -> Array[untyped]
|
86
|
+
end
|
87
|
+
|
88
|
+
class Pathname
|
89
|
+
def self.parse: (untyped parser) -> Pathname
|
90
|
+
def self.object?: -> true
|
91
|
+
def self.dependencies: -> Array[untyped]
|
92
|
+
end
|
58
93
|
end
|
59
94
|
end
|
data/sig/tcepsni.rbs
CHANGED
@@ -4,11 +4,23 @@ module Tcepsni
|
|
4
4
|
class Error < StandardError
|
5
5
|
end
|
6
6
|
|
7
|
-
|
8
|
-
Array[parsed] | Hash[parsed, parsed] |
|
9
|
-
Class | Object | StringScanner
|
7
|
+
def self.parse: (String str) -> untyped
|
10
8
|
|
11
|
-
|
9
|
+
class Parser
|
10
|
+
def initialize: (String source, vendors: Array[untyped]?) -> void
|
11
|
+
def parse_expression: -> untyped
|
12
|
+
alias parse parse_expression
|
13
|
+
def parse_integer: -> Integer
|
14
|
+
def parse_identifier: -> Symbol
|
15
|
+
def parse_string: -> String
|
16
|
+
def parse_array: -> Array[untyped]
|
17
|
+
def parse_hash: -> Hash[untyped, untyped]
|
18
|
+
def parse_class: -> Class
|
19
|
+
def parse_capitalized_identifier: -> Symbol
|
20
|
+
def parse_object: -> Object
|
21
|
+
def parse_attributes: -> Hash[Symbol, untyped]
|
22
|
+
def parse_memory_reference: -> Integer
|
23
|
+
end
|
12
24
|
|
13
25
|
class Class
|
14
26
|
attr_reader name: Symbol
|
@@ -25,28 +37,31 @@ module Tcepsni
|
|
25
37
|
|
26
38
|
class Object
|
27
39
|
attr_reader klass: ::Class
|
28
|
-
attr_reader attributes: Hash[Symbol,
|
40
|
+
attr_reader attributes: Hash[Symbol, untyped]
|
29
41
|
attr_reader memory_reference: Integer
|
30
|
-
def initialize: (klass: Class, attributes: Hash[Symbol,
|
42
|
+
def initialize: (klass: Class, attributes: Hash[Symbol, untyped], memory_reference: Integer) -> void
|
31
43
|
end
|
32
44
|
|
33
45
|
module Vendor
|
34
46
|
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
47
|
end
|
41
48
|
|
42
49
|
module Rails
|
43
50
|
module Paths
|
44
51
|
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
52
|
end
|
49
53
|
end
|
50
54
|
end
|
55
|
+
|
56
|
+
module ActiveSupport
|
57
|
+
class OrderedOptions
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class IPAddr
|
62
|
+
end
|
63
|
+
|
64
|
+
class Pathname
|
65
|
+
end
|
51
66
|
end
|
52
67
|
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.
|
4
|
+
version: 0.3.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-
|
11
|
+
date: 2023-11-18 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:
|
@@ -21,6 +21,12 @@ files:
|
|
21
21
|
- LICENSE.txt
|
22
22
|
- README.md
|
23
23
|
- lib/tcepsni.rb
|
24
|
+
- lib/tcepsni/parser.rb
|
25
|
+
- lib/tcepsni/vendor/activesupport/ordered_options.rb
|
26
|
+
- lib/tcepsni/vendor/ipaddr.rb
|
27
|
+
- lib/tcepsni/vendor/pathname.rb
|
28
|
+
- lib/tcepsni/vendor/rails/paths/root.rb
|
29
|
+
- lib/tcepsni/vendor/string_scanner.rb
|
24
30
|
- lib/tcepsni/version.rb
|
25
31
|
- sig/tcepsni.gen.rbs
|
26
32
|
- sig/tcepsni.rbs
|