marshal-parser 0.1.0 → 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: 54bc27a08a2e25692ac3b0ccf449b6db7df3af095eff14a2c94c91545502721f
4
- data.tar.gz: 0b803be1bde2b3d51d5023c51db1042b9b41eebc341f1d9c29e9c7c2e39e9aa8
3
+ metadata.gz: 84d834a71863bd08ec4f6a4027ddac9603dd83bf5cfa7c0f7c81b540e43950b9
4
+ data.tar.gz: b2327a3176bc5b31db011b61d79d13aa2b973a98596bd9a5586376617a1407cb
5
5
  SHA512:
6
- metadata.gz: fca493db638e427e393d252b32e01a7be43bc4f9e2090d2b23492e5997c3b671d3734534054039dee19aeb8667b9291e74e098d4c85a3a1bbefaf79df7037fd4
7
- data.tar.gz: cc2bcab736f4a53aec056e7d52dbe282100223641c8b57bff64e8fba3ea00995fddbdcfb3a8c5e1c848a148ebe784fb90937e8dff9b7bb3f9948d16e5b317528
6
+ metadata.gz: 505dd53a7648131070bb260a26c1c86a38d057f0e9c84539dd02bef8571f828f7b48e42a463fdb75d9eb0088379f7725c8432748bdf8b829023cf07587638194
7
+ data.tar.gz: 22bce0b7a72769507db2d612d179a2d82fb0376319f63d062ff41a1ce907cb7bfcf1cadc141355e33ad3ab863284d07611c030053f1f1a4c6e094636fcbdf824
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.0] - 2023-12-20
4
+
5
+ ### Added
6
+
7
+ - Added `--hex` flag to print tokens in hexadecimal encoding
8
+ - Added `-r`/`--require` option to load Ruby source file
9
+
10
+ ### Fixed
11
+
12
+ - Fixed parsing of a Hash with default value
13
+ - Fixed parsing of a Regexp with options
14
+
3
15
  ## [0.1.0] - 2023-02-27
4
16
 
5
- - Initial release
17
+ Initial release
data/README.md CHANGED
@@ -31,7 +31,7 @@ Ruby object.
31
31
 
32
32
  The Marshal format is described here <https://ruby-doc.org/core-3.1.0/doc/marshal_rdoc.html>.
33
33
 
34
- There are also a lot of useful articles, for instance:
34
+ There are also a lot of articles that could be useful, for instance:
35
35
  - <https://shopify.engineering/caching-without-marshal-part-one>
36
36
  - <https://iliabylich.github.io/2016/01/25/ruby-marshalling-from-a-to-z.html>
37
37
  - <http://jakegoulding.com/blog/categories/marshal/>
@@ -160,7 +160,7 @@ lexer.run
160
160
  parser = MarshalParser::Parser.new(lexer)
161
161
  ast = parser.parse
162
162
 
163
- puts ast
163
+ pp ast
164
164
  ```
165
165
 
166
166
  This will output:
@@ -171,11 +171,11 @@ This will output:
171
171
 
172
172
  ## Limitations
173
173
 
174
- - Only supports current format version 4.8
175
- - Does not support a deprecated node 'M' (that represents Class or Module)
174
+ - Supports only the current Marshal format version (4.8)
175
+ - Does not support a deprecated node 'M' (that represents 'Class or Module')
176
176
  - Does not support a 'd' node (Data object, that represents wrapped pointers from Ruby extensions)
177
177
  - Doesn't print in annotations object indices (because Ruby is not consistent here and object indices assigning order may
178
- vary depending on dumped class)
178
+ vary depending on a class of a dumped object)
179
179
 
180
180
  ## Similar projects
181
181
 
@@ -11,9 +11,15 @@ module MarshalParser
11
11
  desc "Parse a dump and print tokens. By default reads dump from the stdin."
12
12
  option :file, type: :string, aliases: ["-f"], desc: "Read a dump from file with provided name"
13
13
  option :evaluate, type: :string, aliases: ["-e"], desc: "Ruby expression to dump"
14
+ option :require, type: :string, aliases: ["-r"], desc: "Load the library using require. It is useful when -e is specified"
14
15
  option :annotate, type: :boolean, aliases: ["-a"], desc: "Print a table with annonated tokens"
16
+ option :hex, type: :boolean, aliases: ["-x"], desc: "Print tokens in a hexadecimal encoding"
15
17
 
16
18
  def call(**options)
19
+ if options[:require]
20
+ require options[:require]
21
+ end
22
+
17
23
  dump = \
18
24
  if options[:file]
19
25
  File.read(options[:file])
@@ -28,9 +34,9 @@ module MarshalParser
28
34
 
29
35
  formatter = \
30
36
  if options[:annotate]
31
- MarshalParser::Formatters::Tokens::WithDescription.new(lexer.tokens, dump)
37
+ MarshalParser::Formatters::Tokens::WithDescription.new(lexer.tokens, dump, hex: options[:hex])
32
38
  else
33
- MarshalParser::Formatters::Tokens::OneLine.new(lexer.tokens, dump)
39
+ MarshalParser::Formatters::Tokens::OneLine.new(lexer.tokens, dump, hex: options[:hex])
34
40
  end
35
41
 
36
42
  puts formatter.string
@@ -41,6 +47,7 @@ module MarshalParser
41
47
  desc "Parse a dump and print AST. By default reads dump from the stdin and uses S-expressions format."
42
48
  option :file, type: :string, aliases: ["-f"], desc: "Read a dump from file with provided name"
43
49
  option :evaluate, type: :string, aliases: ["-e"], desc: "Ruby expression to dump"
50
+ option :require, type: :string, aliases: ["-r"], desc: "Load the library using require. It is useful when -e is specified"
44
51
  option :"only-tokens", type: :boolean, aliases: ["-o"], desc: "Print only tokens"
45
52
  option :annotate, type: :boolean, aliases: ["-a"], desc: "Print annotations"
46
53
  option :width, type: :string, aliases: ["-w"],
@@ -49,6 +56,10 @@ module MarshalParser
49
56
  option :compact, type: :boolean, aliases: ["-c"], desc: "Don't print node attributes"
50
57
 
51
58
  def call(**options)
59
+ if options[:require]
60
+ require options[:require]
61
+ end
62
+
52
63
  dump = \
53
64
  if options[:file]
54
65
  File.read(options[:file])
@@ -4,16 +4,24 @@ module MarshalParser
4
4
  module Formatters
5
5
  module Tokens
6
6
  class OneLine
7
- def initialize(tokens, source_string)
7
+ def initialize(tokens, source_string, hex: nil)
8
8
  @tokens = tokens
9
9
  @source_string = source_string
10
+ @hex = hex
10
11
  end
11
12
 
12
13
  def string
13
- @tokens.map do |token|
14
- string = @source_string[token.index, token.length]
15
- string =~ /[^[:print:]]/ ? string.dump : string
16
- end.join(" ")
14
+ unless @hex
15
+ @tokens.map do |token|
16
+ string = @source_string[token.index, token.length]
17
+ string =~ /[^[:print:]]/ ? string.dump : string
18
+ end.join(" ")
19
+ else
20
+ @tokens.map do |token|
21
+ string = @source_string[token.index, token.length]
22
+ string = string.bytes.map { |b| "%02X" % b }.join(" ")
23
+ end.join(" ")
24
+ end
17
25
  end
18
26
  end
19
27
  end
@@ -4,14 +4,15 @@ module MarshalParser
4
4
  module Formatters
5
5
  module Tokens
6
6
  class WithDescription
7
- def initialize(tokens, source_string)
7
+ def initialize(tokens, source_string, hex: nil)
8
8
  @tokens = tokens
9
9
  @source_string = source_string
10
+ @hex = hex
10
11
  end
11
12
 
12
13
  def string
13
14
  @tokens.map do |token|
14
- string = @source_string[token.index, token.length].dump
15
+ string = token_to_string(token)
15
16
  description = self.class.token_description(token.id)
16
17
  value = token.value ? " (#{token.value})" : ""
17
18
 
@@ -19,6 +20,15 @@ module MarshalParser
19
20
  end.join("\n")
20
21
  end
21
22
 
23
+ private def token_to_string(token)
24
+ unless @hex
25
+ @source_string[token.index, token.length].dump
26
+ else
27
+ string = @source_string[token.index, token.length]
28
+ string.bytes.map { |b| "%02X" % b }.join(" ")
29
+ end
30
+ end
31
+
22
32
  def self.token_description(token)
23
33
  case token
24
34
  when Lexer::VERSION then "Version"
@@ -28,7 +38,7 @@ module MarshalParser
28
38
  when Lexer::OBJECT_WITH_MARSHAL_DUMP_PREFIX then "Object with #marshal_dump and #marshal_load"
29
39
  when Lexer::STRING_PREFIX then "String beginning"
30
40
  when Lexer::HASH_PREFIX then "Hash beginning"
31
- when Lexer::HASH_WITH_DEFAULT_VALUE_PREFIX then "Hash beginning (with defaul value)"
41
+ when Lexer::HASH_WITH_DEFAULT_VALUE_PREFIX then "Hash beginning (with default value)"
32
42
  when Lexer::REGEXP_PREFIX then "Regexp beginning"
33
43
  when Lexer::STRUCT_PREFIX then "Struct beginning"
34
44
  when Lexer::TRUE then "true"
@@ -48,6 +58,7 @@ module MarshalParser
48
58
  when Lexer::FLOAT then "Float string representation"
49
59
  when Lexer::INTEGER then "Integer encoded"
50
60
  when Lexer::BIG_INTEGER then "Big Integer encoded"
61
+ when Lexer::BYTE then "Byte"
51
62
  when Lexer::STRING then "String characters"
52
63
  when Lexer::SYMBOL then "Symbol characters"
53
64
  when Lexer::PLUS_SIGN then "Sign '+'"
@@ -30,6 +30,7 @@ module MarshalParser
30
30
  FLOAT,
31
31
  INTEGER,
32
32
  BIG_INTEGER,
33
+ BYTE,
33
34
  STRING,
34
35
  SYMBOL,
35
36
  PLUS_SIGN,
@@ -147,6 +148,12 @@ module MarshalParser
147
148
  elements = (1..count).map { read }
148
149
  end
149
150
 
151
+ def read_byte
152
+ value = @dump[@index].ord
153
+ @index += 1
154
+ @tokens << Token.new(BYTE, @index - 1, 1, value)
155
+ end
156
+
150
157
  def read_integer
151
158
  index_base = @index
152
159
 
@@ -266,7 +273,7 @@ module MarshalParser
266
273
 
267
274
  def read_regexp
268
275
  read_string # read Regexp's source
269
- read_integer # read flags
276
+ read_byte # read flags
270
277
  end
271
278
 
272
279
  def read_struct
@@ -130,7 +130,6 @@ module MarshalParser
130
130
  end
131
131
 
132
132
  default_value_node = build_ast_node
133
- assert_node_type default_value_node, IntegerNode
134
133
 
135
134
  HashWithDefaultValueNode.new(token, size, key_and_value_nodes, default_value_node)
136
135
 
@@ -649,7 +648,7 @@ module MarshalParser
649
648
  assert_token_type prefix, Lexer::REGEXP_PREFIX
650
649
  assert_token_type string_length, Lexer::INTEGER
651
650
  assert_token_type string, Lexer::STRING
652
- assert_token_type options, Lexer::INTEGER
651
+ assert_token_type options, Lexer::BYTE
653
652
 
654
653
  @prefix = prefix
655
654
  @string_length = string_length
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MarshalParser
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: marshal-parser
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
  - Andrew Konchin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-04-06 00:00:00.000000000 Z
11
+ date: 2023-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-cli
@@ -78,7 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
78
78
  - !ruby/object:Gem::Version
79
79
  version: '0'
80
80
  requirements: []
81
- rubygems_version: 3.4.6
81
+ rubygems_version: 3.4.10
82
82
  signing_key:
83
83
  specification_version: 4
84
84
  summary: Parser of the Ruby Marshal format