tcepsni 0.2.0 → 0.3.1
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 +4 -4
- data/CHANGELOG.md +16 -0
- data/README.md +2 -2
- data/lib/tcepsni/parser.rb +165 -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 +4 -229
- data/sig/tcepsni.gen.rbs +57 -22
- data/sig/tcepsni.rbs +29 -14
- data/tcepsni.gemspec +13 -1
- metadata +15 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40d96c10a7e7729b0c37648bedd7b693dff04602d9bc3ef9794e695d48986864
|
4
|
+
data.tar.gz: f6316e6ceba71e2a7cee7d99140357a08e52c598e31324016159d301ffb791e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e3235426ddaeed30dd132dfc2f904259abe5da867aa4e17aa683d43d42112d8ded52575e4cb3e218c39fcc0f4f1a84175594279f5e07e221da08a5d03afe79f7
|
7
|
+
data.tar.gz: 3e79b0c3e3f29d5044cdc7051e980db49d1a49fd54a7fa8299386056f38864887fe04238319177d5fa9f0bbe4bd07c773a9c4fe279b2875b80fdf7e9f5bcbac8
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,22 @@
|
|
2
2
|
|
3
3
|
## Unreleased
|
4
4
|
|
5
|
+
## 0.3.1 - 2024-07-21
|
6
|
+
|
7
|
+
Update gemspec fields etc.
|
8
|
+
There should be no API changes.
|
9
|
+
|
10
|
+
## 0.3.0 - 2023-11-18
|
11
|
+
|
12
|
+
### Changed
|
13
|
+
|
14
|
+
- Introduced plugin system.
|
15
|
+
For example, if the source contains `Pathname` objects, use the `vendors` option like `Tcepsni.parse(str, vendors: [::Tcepsni::Vendor::Pathname])`.
|
16
|
+
|
17
|
+
### Removed
|
18
|
+
|
19
|
+
- `Tcepsni::Class#string_scanner?`, `Tcepsni::Class#pathname?`, `Tcepsni::Class#ipaddr?`, `Tcepsni::Class#active_support_ordered_options?`, and `Tcepsni::Class#rails_paths_root?`.
|
20
|
+
|
5
21
|
## 0.2.0 - 2023-10-20
|
6
22
|
|
7
23
|
### Added
|
data/README.md
CHANGED
@@ -15,7 +15,7 @@ Tcepsni has a parsing method `Tcepsni.parse(str)`, which returns a parsed Ruby o
|
|
15
15
|
## Development
|
16
16
|
|
17
17
|
After checking out the repo, run `bin/setup` to install dependencies.
|
18
|
-
Then, run `rake test
|
18
|
+
Then, run `rake test` to run the tests.
|
19
19
|
You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
20
20
|
|
21
21
|
To install this gem onto your local machine, run `bundle exec rake install`.
|
@@ -27,7 +27,7 @@ Bug reports and pull requests are welcome.
|
|
27
27
|
|
28
28
|
## License
|
29
29
|
|
30
|
-
Copyright 2023 gemmaro.
|
30
|
+
Copyright 2023, 2024 gemmaro.
|
31
31
|
|
32
32
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
|
33
33
|
You may obtain a copy of the License at [Apache Software Foundation - Apache License, Version 2.0][asl].
|
@@ -0,0 +1,165 @@
|
|
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
|
+
@scanner.pos = pos
|
31
|
+
return vendor.parse(self)
|
32
|
+
rescue Error
|
33
|
+
next
|
34
|
+
end
|
35
|
+
@scanner.pos = pos
|
36
|
+
parse_object
|
37
|
+
elsif (digits = @scanner.scan(/\d+/))
|
38
|
+
int = Integer(digits)
|
39
|
+
if @scanner.skip('.')
|
40
|
+
int2 = parse_integer
|
41
|
+
Float("#{int}.#{int2}")
|
42
|
+
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)/)
|
43
|
+
Time.new(Integer(int), @scanner[:month], @scanner[:day],
|
44
|
+
@scanner[:hour], @scanner[:minute], @scanner[:second],
|
45
|
+
@scanner[:zone])
|
46
|
+
else
|
47
|
+
int
|
48
|
+
end
|
49
|
+
else
|
50
|
+
parse_class
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
alias parse parse_expression
|
55
|
+
|
56
|
+
def parse_integer
|
57
|
+
int = @scanner.scan(/\d+/) or raise Error, 'no digits'
|
58
|
+
Integer(int)
|
59
|
+
end
|
60
|
+
|
61
|
+
def parse_identifier
|
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
|
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
|
81
|
+
result = []
|
82
|
+
until @scanner.eos?
|
83
|
+
begin
|
84
|
+
result << parse_expression
|
85
|
+
rescue StandardError
|
86
|
+
break
|
87
|
+
end
|
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 = begin
|
98
|
+
parse_expression
|
99
|
+
rescue StandardError
|
100
|
+
break
|
101
|
+
end
|
102
|
+
@scanner.skip('=>') or raise Error, 'no hash arrow'
|
103
|
+
value = parse_expression
|
104
|
+
result[key] = value
|
105
|
+
@scanner.skip(', ') or break
|
106
|
+
end
|
107
|
+
@scanner.skip('}') or raise Error, 'no hash closer'
|
108
|
+
result
|
109
|
+
end
|
110
|
+
|
111
|
+
def parse_class
|
112
|
+
namespaces = [parse_capitalized_identifier]
|
113
|
+
until @scanner.eos?
|
114
|
+
@scanner.skip('::') or break
|
115
|
+
@scanner.eos? and raise Error, 'incomplete class'
|
116
|
+
namespaces << parse_capitalized_identifier
|
117
|
+
end
|
118
|
+
*namespaces, name = namespaces
|
119
|
+
Tcepsni::Class.new(name:, namespaces:)
|
120
|
+
end
|
121
|
+
|
122
|
+
def parse_capitalized_identifier
|
123
|
+
id = @scanner.scan(/[A-Z][A-Za-z0-9_]*/) or raise Error, "no capitalized identifier: #{@scanner.inspect}"
|
124
|
+
id.intern
|
125
|
+
end
|
126
|
+
|
127
|
+
def parse_object
|
128
|
+
klass = parse_class
|
129
|
+
if klass.encoding?
|
130
|
+
@scanner.skip(':') or raise Error, 'no colon separator in encoding object'
|
131
|
+
@scanner.skip(/(?<name>[^>]+)>/) or raise Error, 'invalid encoding name'
|
132
|
+
Encoding.find(@scanner[:name])
|
133
|
+
else
|
134
|
+
memory_reference = parse_memory_reference
|
135
|
+
attributes = parse_attributes
|
136
|
+
Tcepsni::Object.new(klass:, memory_reference:, attributes:)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def parse_attributes
|
141
|
+
attributes = {}
|
142
|
+
unless @scanner.skip('>')
|
143
|
+
until @scanner.eos?
|
144
|
+
@scanner.skip(' @') or break
|
145
|
+
key = parse_identifier
|
146
|
+
@scanner.skip('=') or raise Error, 'no attribute keyval equal char'
|
147
|
+
attributes[key.intern] = parse_expression
|
148
|
+
unless @scanner.skip(',')
|
149
|
+
@scanner.skip('>') or raise Error, "no object closer: #{@scanner.inspect}"
|
150
|
+
break
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
attributes
|
155
|
+
end
|
156
|
+
|
157
|
+
def parse_memory_reference
|
158
|
+
@scanner.skip(/:(?<memory_reference>0x[0-9a-f]{16})/) or raise Error, "no memory reference: #{@scanner.inspect}"
|
159
|
+
Integer(@scanner[:memory_reference])
|
160
|
+
end
|
161
|
+
|
162
|
+
extend Forwardable
|
163
|
+
def_delegators :@scanner, :skip, :[]
|
164
|
+
end
|
165
|
+
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
|
+
attributes = if parser.skip(' ...>')
|
19
|
+
{}
|
20
|
+
else
|
21
|
+
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:, total:, scanned:, 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
|
+
Error = Class.new(StandardError)
|
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:).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
|
data/tcepsni.gemspec
CHANGED
@@ -9,10 +9,22 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.summary = 'inspected Ruby objects parser'
|
10
10
|
spec.description = 'Tcepsni gem is a parser library for inspected strings of Ruby objects.'
|
11
11
|
spec.license = 'Apache-2.0'
|
12
|
+
|
13
|
+
spec.homepage = 'https://codeberg.org/gemmaro/tcepsni'
|
14
|
+
|
12
15
|
spec.required_ruby_version = '>= 3.1.0'
|
13
16
|
|
14
17
|
spec.files = Dir['lib/**/*.rb'] + Dir['sig/**/*.rbs'] +
|
15
18
|
['CHANGELOG.md', 'tcepsni.gemspec', 'README.md', 'LICENSE.txt']
|
16
19
|
spec.require_paths = ['lib']
|
17
|
-
|
20
|
+
|
21
|
+
spec.metadata = {
|
22
|
+
'rubygems_mfa_required' => 'true',
|
23
|
+
'bug_tracker_uri' => 'https://codeberg.org/gemmaro/tcepsni/issues',
|
24
|
+
'changelog_uri' => 'https://codeberg.org/gemmaro/tcepsni/src/branch/main/CHANGELOG.md',
|
25
|
+
'documentation_uri' => 'https://www.rubydoc.info/gems/tcepsni',
|
26
|
+
'homepage_uri' => spec.homepage,
|
27
|
+
'source_code_uri' => spec.homepage,
|
28
|
+
'wiki_uri' => 'https://codeberg.org/gemmaro/tcepsni/wiki'
|
29
|
+
}
|
18
30
|
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.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- gemmaro
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-07-21 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,15 +21,27 @@ 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
|
27
33
|
- tcepsni.gemspec
|
28
|
-
homepage:
|
34
|
+
homepage: https://codeberg.org/gemmaro/tcepsni
|
29
35
|
licenses:
|
30
36
|
- Apache-2.0
|
31
37
|
metadata:
|
32
38
|
rubygems_mfa_required: 'true'
|
39
|
+
bug_tracker_uri: https://codeberg.org/gemmaro/tcepsni/issues
|
40
|
+
changelog_uri: https://codeberg.org/gemmaro/tcepsni/src/branch/main/CHANGELOG.md
|
41
|
+
documentation_uri: https://www.rubydoc.info/gems/tcepsni
|
42
|
+
homepage_uri: https://codeberg.org/gemmaro/tcepsni
|
43
|
+
source_code_uri: https://codeberg.org/gemmaro/tcepsni
|
44
|
+
wiki_uri: https://codeberg.org/gemmaro/tcepsni/wiki
|
33
45
|
post_install_message:
|
34
46
|
rdoc_options: []
|
35
47
|
require_paths:
|