marshal-parser 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: 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