semdiff 0.1.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.
@@ -0,0 +1,238 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tempfile'
4
+
5
+ module Semdiff
6
+ module IOUtils
7
+ include ::Typeguard::TypeModel::Builder
8
+
9
+ FILE_PREFIX = 'source'
10
+ FILE_SUFFIX = '.rb'
11
+ DIR_PREFIX = 'yard_'
12
+ DIR_SUFFIX = '_db'
13
+ RUBY_VERSION_F = RUBY_VERSION.to_f
14
+
15
+ def with_types_desugared(*content)
16
+ with_temp_files(*content) do |files|
17
+ with_temp_dir do |dir|
18
+ system(*yardoc_cmd(files, dir))
19
+ program_node = Prism.parse_file(files.first.path).value
20
+ types = YardBuilder.new(dir, false).build
21
+ node_types = TypeVisitor.new(types).visit(program_node)
22
+ yield program_node, node_types
23
+ end
24
+ end
25
+ end
26
+
27
+ def with_prism_and_yard_types(*contents)
28
+ with_temp_files(*contents) do |files|
29
+ with_temp_dir do |dir|
30
+ system(*yardoc_cmd(files, dir))
31
+ parse_lex_results = files.map do |f|
32
+ Prism.parse_lex_file(f.path)
33
+ end
34
+ types = YardBuilder.new(dir, false).build
35
+ yield files, dir, parse_lex_results, types
36
+ end
37
+ end
38
+ end
39
+
40
+ def with_full_prism_desugared(*contents)
41
+ with_temp_files(*contents) do |files|
42
+ with_temp_dir do |dir|
43
+ system(*yardoc_cmd(files, dir))
44
+ program_nodes = files.map do |f|
45
+ Prism.parse_file(f.path).value.accept(Prism::DesugarCompiler.new)
46
+ end
47
+ types = YardBuilder.new(dir, false).build
48
+ yield files, dir, program_nodes, types
49
+ end
50
+ end
51
+ end
52
+
53
+ def with_prism_desugared(*contents)
54
+ with_temp_files(*contents) do |files|
55
+ program_node = files.map do |f|
56
+ Prism.parse_file(f.path).value.accept(Prism::DesugarCompiler.new)
57
+ end
58
+ yield files, program_node
59
+ end
60
+ end
61
+
62
+ def with_full_prism(*contents)
63
+ with_temp_files(*contents) do |files|
64
+ with_temp_dir do |dir|
65
+ system(*yardoc_cmd(files, dir))
66
+ program_node = Prism.parse_file(files.first.path).value
67
+ types = YardBuilder.new(dir, false).build
68
+ yield files, dir, program_node, types
69
+ end
70
+ end
71
+ end
72
+
73
+ def with_prism(*contents)
74
+ with_temp_files(*contents) do |files|
75
+ program_node = files.map do |f|
76
+ Prism.parse_file(f.path).value
77
+ end
78
+ yield files, program_node
79
+ end
80
+ end
81
+
82
+ def with_yard_types(*contents)
83
+ with_temp_files(*contents) do |files|
84
+ with_temp_dir do |dir|
85
+ system(*yardoc_cmd(files, dir))
86
+ types = YardBuilder.new(dir, false).build
87
+ yield files, dir, types
88
+ end
89
+ end
90
+ end
91
+
92
+ def with_yardoc_and_prism(*contents)
93
+ with_temp_files(*contents) do |files|
94
+ with_temp_dir do |dir|
95
+ system(*yardoc_cmd(files, dir))
96
+ parse_lex_results = files.map do |f|
97
+ Prism.parse_lex_file(f.path)
98
+ end
99
+ yield files, dir, parse_lex_results
100
+ end
101
+ end
102
+ end
103
+
104
+ def with_prism_parse_lex(*contents)
105
+ with_temp_files(*contents) do |files|
106
+ parse_lex_results = files.map do |f|
107
+ Prism.parse_lex_file(f.path)
108
+ end
109
+ yield files, parse_lex_results
110
+ end
111
+ end
112
+
113
+ def with_yardoc(*contents)
114
+ with_temp_dir do |dir|
115
+ with_temp_files(*contents) do |files|
116
+ system(*yardoc_cmd(files, dir))
117
+ yield files, dir
118
+ end
119
+ end
120
+ end
121
+
122
+ def with_rubocop_ast_and_yard_types(*contents)
123
+ with_temp_files(*contents) do |files|
124
+ with_temp_dir do |dir|
125
+ system(*yardoc_cmd(files, dir))
126
+ ast_results = files.map do |f|
127
+ RuboCop::AST::ProcessedSource.from_file(
128
+ f.path,
129
+ RUBY_VERSION_F,
130
+ parser_engine: :parser_prism
131
+ )
132
+ end
133
+ types = YardBuilder.new(dir, false).build
134
+ yield files, dir, ast_results, types
135
+ end
136
+ end
137
+ end
138
+
139
+ def with_rubocop_ast_bypass(*file_prism_pairs)
140
+ ast_results = file_prism_pairs.map do |f, prism_result|
141
+ source = File.read(f.path)
142
+ RuboCop::AST::ProcessedSource.new(
143
+ source,
144
+ RUBY_VERSION_F,
145
+ f.path,
146
+ parser_engine: :parser_prism,
147
+ prism_result: prism_result
148
+ )
149
+ end
150
+ yield ast_results
151
+ end
152
+
153
+ def with_rubocop_ast(*contents)
154
+ with_temp_files(*contents) do |files|
155
+ ast_results = files.map do |f|
156
+ RuboCop::AST::ProcessedSource.from_file(
157
+ f.path,
158
+ RUBY_VERSION_F,
159
+ parser_engine: :parser_prism
160
+ )
161
+ end
162
+ yield files, ast_results
163
+ end
164
+ end
165
+
166
+ def with_diff_files(contents, names, output_directory: nil)
167
+ prefix = 'unparsed_'
168
+ if output_directory
169
+ FileUtils.mkdir_p(output_directory)
170
+ b, a = contents.zip(names).map do |c, n|
171
+ path = File.join(output_directory, "#{prefix}#{n}")
172
+ File.write(path, c)
173
+ File.open(path, 'r')
174
+ end
175
+ begin
176
+ yield b, a
177
+ ensure
178
+ b.close
179
+ a.close
180
+ end
181
+ else
182
+ b_temp, a_temp = contents.zip(names).map do |c, n|
183
+ temp_file(c, prefix: '', suffix: "_#{prefix}#{n}")
184
+ end
185
+ begin
186
+ yield b_temp, a_temp
187
+ ensure
188
+ File.unlink(b_temp.path)
189
+ File.unlink(a_temp.path)
190
+ end
191
+ end
192
+ end
193
+
194
+ def with_temp_files(*contents, prefix: FILE_PREFIX, suffix: FILE_SUFFIX)
195
+ files = contents.map.with_index do |content, i|
196
+ temp_file(content, prefix: "#{prefix}#{i}_", suffix: suffix)
197
+ end
198
+ begin
199
+ yield files
200
+ ensure
201
+ files.each { |f| File.unlink(f.path) }
202
+ end
203
+ end
204
+
205
+ def with_temp_dir(prefix: DIR_PREFIX, suffix: DIR_SUFFIX)
206
+ dir = temp_dir(prefix: prefix, suffix: suffix)
207
+ begin
208
+ yield dir
209
+ ensure
210
+ FileUtils.remove_entry_secure(dir)
211
+ end
212
+ end
213
+
214
+ def temp_file(content, prefix: FILE_PREFIX, suffix: FILE_SUFFIX)
215
+ f = Tempfile.create([prefix, suffix])
216
+ f.write(content)
217
+ f.close
218
+ f
219
+ end
220
+
221
+ def temp_dir(prefix: DIR_PREFIX, suffix: DIR_SUFFIX)
222
+ Dir.mktmpdir([prefix, suffix])
223
+ end
224
+
225
+ def yardoc_cmd(files, db_dir)
226
+ [
227
+ 'bundle', 'exec', 'yardoc',
228
+ *files.map(&:path),
229
+ '--db', db_dir,
230
+ '--quiet',
231
+ '--no-output',
232
+ '--no-cache',
233
+ '--fail-on-warning',
234
+ '--embed-mixins'
235
+ ]
236
+ end
237
+ end
238
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Semdiff
4
+ VERSION = '0.1.0'
5
+ end
data/lib/semdiff.rb ADDED
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Semdiff
4
+ # typeguard gem for type specifications
5
+ require 'typeguard'
6
+
7
+ # external
8
+ require 'prism'
9
+ # suppress warning (re: https://github.com/glebm/i18n-tasks/pull/613):
10
+ # warning: parser/current is loading parser/ruby34, which recognizes 3.4.0-dev-compliant syntax, but you are running 3.4.1.
11
+ # Please see https://github.com/whitequark/parser#compatibility-with-ruby-mri.
12
+ prev = $VERBOSE
13
+ $VERBOSE = nil
14
+ require 'unparser'
15
+ $VERBOSE = prev
16
+
17
+ # utils
18
+ require_relative 'semdiff/utils/compiler_utils'
19
+ require_relative 'semdiff/utils/io_utils'
20
+
21
+ # prism compilers and visitors
22
+ require_relative 'semdiff/aliasing_compiler'
23
+ require_relative 'semdiff/constants_compiler'
24
+ require_relative 'semdiff/identity_compiler'
25
+ require_relative 'semdiff/structures_compiler'
26
+ require_relative 'semdiff/algebra_compiler'
27
+ require_relative 'semdiff/type_visitor'
28
+
29
+ # command line module
30
+ require_relative 'semdiff/cli/cli'
31
+ end
data/sig/semdiff.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Semdiff
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,152 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: semdiff
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Tesorion
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-07-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: optparse
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: prism
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rbs
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: typeguard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: unparser
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: yard
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description:
98
+ email:
99
+ - QmanageDevelopment@tesorion.nl
100
+ executables:
101
+ - semdiff
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".editorconfig"
106
+ - ".rubocop.yml"
107
+ - ".rubocop_todo.yml"
108
+ - ".ruby-version"
109
+ - Gemfile
110
+ - LICENSE
111
+ - README.md
112
+ - Rakefile
113
+ - bin/semdiff
114
+ - lib/semdiff.rb
115
+ - lib/semdiff/algebra_compiler.rb
116
+ - lib/semdiff/aliasing_compiler.rb
117
+ - lib/semdiff/cli/cli.rb
118
+ - lib/semdiff/constants_compiler.rb
119
+ - lib/semdiff/identity_compiler.rb
120
+ - lib/semdiff/structures_compiler.rb
121
+ - lib/semdiff/type_visitor.rb
122
+ - lib/semdiff/utils/compiler_utils.rb
123
+ - lib/semdiff/utils/io_utils.rb
124
+ - lib/semdiff/version.rb
125
+ - sig/semdiff.rbs
126
+ homepage: https://github.com/tesorion/semdiff
127
+ licenses:
128
+ - MIT
129
+ metadata:
130
+ homepage_uri: https://github.com/tesorion/semdiff
131
+ source_code_uri: https://github.com/tesorion/semdiff
132
+ rubygems_mfa_required: 'true'
133
+ post_install_message:
134
+ rdoc_options: []
135
+ require_paths:
136
+ - lib
137
+ required_ruby_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: 2.7.0
142
+ required_rubygems_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ requirements: []
148
+ rubygems_version: 3.3.7
149
+ signing_key:
150
+ specification_version: 4
151
+ summary: Diff two files with AST normalizations
152
+ test_files: []