xmp2assert 2 → 3
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/.rubocop.yml +62 -13
- data/exe/xmpcheck.rb +3 -6
- data/lib/xmp2assert/assertions.rb +17 -11
- data/lib/xmp2assert/classifier.rb +18 -30
- data/lib/xmp2assert/converter.rb +187 -57
- data/lib/xmp2assert/namespace.rb +34 -0
- data/lib/xmp2assert/parser.rb +136 -0
- data/lib/xmp2assert/prettier_inspect.rb +2 -1
- data/lib/xmp2assert/quasifile.rb +6 -2
- data/lib/xmp2assert/token.rb +88 -0
- data/lib/xmp2assert/version.rb +1 -1
- data/lib/xmp2assert/xmp2rexp.rb +2 -0
- data/lib/xmp2assert.rb +7 -16
- metadata +7 -4
- /data/{Stylegiude.md → Styleguide.md} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 37e895dead7be33e2d883950ffc4472e4ed4c49b
|
4
|
+
data.tar.gz: 81805143514b795409f5258853471d67794f698a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 974409606e9bc0812d5d01ba91329b51f2a095ced6fc5cf64b765685a4d978bd260e4279274a0402b7ca6480777df07d095cd883b53bd269d70e2333464e5897
|
7
|
+
data.tar.gz: 32860b61ca5ebf164f81e4209b8c66bc8ea243bd48793d75eb0f8f6fc6ff87b09477cb76810a4f1b15f19efc7827ad78fb639ffbe2db8854464c8bd01f6a1ccf
|
data/.rubocop.yml
CHANGED
@@ -37,12 +37,18 @@ Lint/EmptyWhen:
|
|
37
37
|
Rails:
|
38
38
|
Enabled: false
|
39
39
|
|
40
|
+
Style/Alias:
|
41
|
+
Enabled: false
|
42
|
+
|
40
43
|
Style/AlignHash:
|
41
44
|
Enabled: false
|
42
45
|
|
43
46
|
Style/AlignParameters:
|
44
47
|
Enabled: false
|
45
48
|
|
49
|
+
Style/AndOr:
|
50
|
+
Enabled: false
|
51
|
+
|
46
52
|
Style/BracesAroundHashParameters:
|
47
53
|
Enabled: false
|
48
54
|
|
@@ -52,6 +58,9 @@ Style/CaseEquality:
|
|
52
58
|
Style/CaseIndentation:
|
53
59
|
Enabled: false
|
54
60
|
|
61
|
+
Style/ClassAndModuleCamelCase:
|
62
|
+
Enabled: false
|
63
|
+
|
55
64
|
Style/ClassAndModuleChildren:
|
56
65
|
Enabled: false
|
57
66
|
|
@@ -82,6 +91,9 @@ Style/ExtraSpacing:
|
|
82
91
|
Style/FormatString:
|
83
92
|
Enabled: false
|
84
93
|
|
94
|
+
Style/GuardClause:
|
95
|
+
Enabled: false
|
96
|
+
|
85
97
|
Style/IndentHash:
|
86
98
|
Enabled: false
|
87
99
|
|
@@ -94,24 +106,45 @@ Style/IndentationWidth:
|
|
94
106
|
Style/MethodDefParentheses:
|
95
107
|
Enabled: false
|
96
108
|
|
109
|
+
Style/MethodName:
|
110
|
+
Enabled: false
|
111
|
+
|
97
112
|
Style/MultilineIfThen:
|
98
113
|
Enabled: false
|
99
114
|
|
115
|
+
Style/MultilineMethodCallIndentation:
|
116
|
+
Enabled: false
|
117
|
+
|
100
118
|
Style/ParallelAssignment:
|
101
119
|
Enabled: false
|
102
120
|
|
103
121
|
Style/PercentLiteralDelimiters:
|
104
122
|
Enabled: false
|
105
123
|
|
124
|
+
Style/PerlBackrefs:
|
125
|
+
Enabled: false
|
126
|
+
|
106
127
|
Style/RedundantReturn:
|
107
128
|
Enabled: false
|
108
129
|
|
130
|
+
Style/RedundantSelf:
|
131
|
+
Enabled: false
|
132
|
+
|
133
|
+
Style/RegexpLiteral:
|
134
|
+
Enabled: false
|
135
|
+
|
136
|
+
Style/Semicolon:
|
137
|
+
Enabled: false
|
138
|
+
|
109
139
|
Style/SpaceAroundOperators:
|
110
140
|
Enabled: false
|
111
141
|
|
112
142
|
Style/SpaceInsideBrackets:
|
113
143
|
Enabled: false
|
114
144
|
|
145
|
+
Style/SpecialGlobalVars:
|
146
|
+
Enabled: false
|
147
|
+
|
115
148
|
Style/StringLiterals:
|
116
149
|
Enabled: false
|
117
150
|
|
@@ -127,40 +160,45 @@ Style/TrailingCommaInLiteral:
|
|
127
160
|
Style/TrailingUnderscoreVariable:
|
128
161
|
Enabled: false
|
129
162
|
|
163
|
+
Style/VariableInterpolation:
|
164
|
+
Enabled: false
|
165
|
+
|
130
166
|
Style/VariableName:
|
131
167
|
Enabled: false
|
132
168
|
|
133
169
|
Style/WordArray:
|
134
170
|
Enabled: false
|
135
171
|
|
136
|
-
|
137
|
-
|
138
|
-
- 'test/**/*'
|
139
|
-
- '*.gemspec'
|
172
|
+
Style/WhileUntilDo:
|
173
|
+
Enabled: false
|
140
174
|
|
141
|
-
|
175
|
+
Style/WhileUntilModifier:
|
176
|
+
Enabled: false
|
177
|
+
|
178
|
+
Lint/UselessAccessModifier:
|
142
179
|
Exclude:
|
143
|
-
|
144
|
-
- '
|
180
|
+
# I think detecting this file is a rubocop bug.
|
181
|
+
- 'lib/xmp2assert/validator.rb'
|
145
182
|
|
146
|
-
|
183
|
+
Lint/UselessAssignment:
|
147
184
|
Exclude:
|
148
185
|
- 'test/**/*'
|
149
186
|
- '*.gemspec'
|
150
187
|
|
151
|
-
Metrics/
|
188
|
+
Metrics/AbcSize:
|
152
189
|
Exclude:
|
153
190
|
- 'test/**/*'
|
154
191
|
- '*.gemspec'
|
155
192
|
|
156
|
-
|
193
|
+
Metrics/ClassLength:
|
157
194
|
Exclude:
|
158
195
|
- 'test/**/*'
|
159
196
|
- '*.gemspec'
|
160
197
|
|
161
|
-
|
162
|
-
|
163
|
-
-
|
198
|
+
Metrics/BlockLength:
|
199
|
+
ExcludedMethods:
|
200
|
+
- new
|
201
|
+
- sub_test_case
|
164
202
|
|
165
203
|
Metrics/LineLength:
|
166
204
|
AllowURI: true
|
@@ -170,3 +208,14 @@ Metrics/MethodLength:
|
|
170
208
|
CountComments: false
|
171
209
|
Enabled: true
|
172
210
|
Max: 30
|
211
|
+
|
212
|
+
Style/EmptyElse:
|
213
|
+
Enabled: true
|
214
|
+
EnforcedStyle: empty
|
215
|
+
|
216
|
+
Style/SpaceInsideBlockBraces:
|
217
|
+
Enabled: true
|
218
|
+
EnforcedStyle: space
|
219
|
+
SpaceBeforeBlockParameters: false
|
220
|
+
Exclude:
|
221
|
+
- '*.gemspec'
|
data/exe/xmpcheck.rb
CHANGED
@@ -29,6 +29,7 @@ require 'test/unit/autorunner'
|
|
29
29
|
require 'pathname'
|
30
30
|
require 'xmp2assert'
|
31
31
|
|
32
|
+
# This class introduces the test auto-run.
|
32
33
|
class TC_Main < Test::Unit::TestCase
|
33
34
|
include XMP2Assert::Assertions
|
34
35
|
|
@@ -44,12 +45,8 @@ class TC_Main < Test::Unit::TestCase
|
|
44
45
|
else
|
45
46
|
test f.__FILE__ do
|
46
47
|
t, o = XMP2Assert::Converter.convert f
|
47
|
-
if k.include? :'=>'
|
48
|
-
|
49
|
-
end
|
50
|
-
if k.include? :'>>' then
|
51
|
-
assert_capture2e o, f
|
52
|
-
end
|
48
|
+
t.eval binding if k.include? :'=>'
|
49
|
+
assert_capture2e o, f if k.include? :'>>'
|
53
50
|
end
|
54
51
|
end
|
55
52
|
end
|
@@ -30,6 +30,8 @@ require 'erb'
|
|
30
30
|
require 'test/unit'
|
31
31
|
require 'test/unit/assertions'
|
32
32
|
require 'test/unit/assertion-failed-error'
|
33
|
+
require_relative 'namespace'
|
34
|
+
require_relative 'quasifile'
|
33
35
|
require_relative 'xmp2rexp'
|
34
36
|
|
35
37
|
# Helper module that implements assertions.
|
@@ -52,7 +54,8 @@ module XMP2Assert::Assertions
|
|
52
54
|
# As the method name implies the assertion is against both stdin and stderr
|
53
55
|
# at once. This is for convenience.
|
54
56
|
def assert_capture2e expected, script, message = nil, rubyopts: nil, **opts
|
55
|
-
|
57
|
+
qscript = XMP2Assert::Quasifile.new script
|
58
|
+
actual, _ = ruby qscript, rubyopts: rubyopts, **opts
|
56
59
|
actual.force_encoding expected.encoding
|
57
60
|
return assert_xmp_raw expected, actual, message
|
58
61
|
end
|
@@ -74,20 +77,20 @@ module XMP2Assert::Assertions
|
|
74
77
|
|
75
78
|
# :TODO: is it private?
|
76
79
|
def assert_xmp_raw xmp, actual, message = nil
|
77
|
-
msg = genmsg xmp, actual, message
|
78
80
|
expected = xmp2rexp xmp
|
79
81
|
|
80
82
|
raise unless expected.match actual
|
81
83
|
rescue
|
82
84
|
# Regexp#match can raise. That should also be a failure.
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
85
|
+
msg = genmsg xmp, actual, message
|
86
|
+
ix = Test::Unit::Assertions::AssertionMessage.convert xmp
|
87
|
+
ia = Test::Unit::Assertions::AssertionMessage.convert actual
|
88
|
+
ex = Test::Unit::AssertionFailedError.new(msg,
|
89
|
+
expected: xmp,
|
90
|
+
actual: actual,
|
91
|
+
inspected_expected: ix,
|
92
|
+
inspected_actual: ia,
|
93
|
+
user_message: message)
|
91
94
|
raise ex
|
92
95
|
else
|
93
96
|
return self # or...?
|
@@ -102,7 +105,10 @@ module XMP2Assert::Assertions
|
|
102
105
|
|
103
106
|
def genmsg x, y, z = nil
|
104
107
|
diff = Test::Unit::Assertions::AssertionMessage.delayed_diff x, y
|
105
|
-
if try(x, :
|
108
|
+
if try(x, :ascii_only?) && try(y, :ascii_only?) then
|
109
|
+
fmt = "<?> expected but was\n<?>.?"
|
110
|
+
argv = [x, y, diff]
|
111
|
+
elsif try(x, :encoding) != try(y, :encoding) then
|
106
112
|
fmt = "<?>(?) expected but was\n<?>(?).?"
|
107
113
|
argv = [x, x.encoding.name, y, y.encoding.name, diff]
|
108
114
|
else
|
@@ -24,6 +24,8 @@
|
|
24
24
|
# SOFTWARE.
|
25
25
|
|
26
26
|
require 'ripper'
|
27
|
+
require_relative 'namespace'
|
28
|
+
require_relative 'parser'
|
27
29
|
|
28
30
|
# Usually, you want to check LOTS of files that may or may not contain xmp
|
29
31
|
# comments at once, maybe from inside of a CI process. That's OK but we want
|
@@ -37,35 +39,21 @@ require 'ripper'
|
|
37
39
|
# XMP2Assert::Classifier.classify(f)
|
38
40
|
# end
|
39
41
|
# ```
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
# @
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
private
|
57
|
-
|
58
|
-
def parse
|
59
|
-
@ret = []
|
60
|
-
super
|
61
|
-
return @ret
|
62
|
-
end
|
63
|
-
|
64
|
-
def on_comment tok
|
65
|
-
case tok
|
66
|
-
when /^# =>/ then @ret |= [:'=>']
|
67
|
-
when /^# >>/ then @ret |= [:'>>']
|
68
|
-
when /^# ~>/ then @ret |= [:'>>']
|
69
|
-
end
|
42
|
+
module XMP2Assert::Classifier
|
43
|
+
# @param (see XMP2Assert::Parser.new)
|
44
|
+
# @return [<Symbol>] either empty, :=>, :>>, or both.
|
45
|
+
# @note syntax error results in empty return value.
|
46
|
+
def self.classify obj, file = nil, line = nil
|
47
|
+
parser = XMP2Assert::Parser.new obj, file, line
|
48
|
+
rescue SyntaxError
|
49
|
+
return []
|
50
|
+
else
|
51
|
+
return parser \
|
52
|
+
.tokens \
|
53
|
+
.map(&:to_sym) \
|
54
|
+
.sort \
|
55
|
+
.uniq \
|
56
|
+
.map {|i| case i when :'=>', :'>>' then i else nil end } \
|
57
|
+
.compact
|
70
58
|
end
|
71
59
|
end
|
data/lib/xmp2assert/converter.rb
CHANGED
@@ -25,89 +25,219 @@
|
|
25
25
|
|
26
26
|
require 'ripper'
|
27
27
|
require 'uuid'
|
28
|
+
require_relative 'namespace'
|
29
|
+
require_relative 'parser'
|
30
|
+
require_relative 'quasifile'
|
28
31
|
|
29
|
-
class
|
32
|
+
# This class converts a Ruby script into assertions
|
33
|
+
class XMP2Assert::Converter
|
30
34
|
private_class_method :new
|
31
35
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
36
|
+
# Detects XMP and make them assertions. For example:
|
37
|
+
#
|
38
|
+
# ```ruby
|
39
|
+
# def foo; 2; end
|
40
|
+
# 1 + foo # => 3
|
41
|
+
# ```
|
42
|
+
#
|
43
|
+
# would become
|
44
|
+
#
|
45
|
+
# ```ruby
|
46
|
+
# def foo; 2; end
|
47
|
+
# (1 + foo).tap {|i| assert_xmp("3", i ) }
|
48
|
+
# ```
|
49
|
+
#
|
50
|
+
# @param (see #initialize)
|
51
|
+
# @return [(Quasifile,String)] tuple of generated file and its expected
|
52
|
+
# output.
|
53
|
+
def self.convert obj, file = nil, line = nil
|
54
|
+
this = new obj, file, line
|
55
|
+
return this.send :convert
|
42
56
|
end
|
43
57
|
|
44
58
|
private
|
45
59
|
|
46
|
-
|
47
|
-
|
60
|
+
# @param (see XMP2Assert::Parser.new)
|
61
|
+
def initialize obj, file = nil, line = nil
|
62
|
+
@program = XMP2Assert::Parser.new obj, file, line
|
48
63
|
end
|
49
64
|
|
50
65
|
def convert
|
51
|
-
@
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
return @ret.join, @outputs
|
66
|
+
@tokens = @program.tokens
|
67
|
+
outputs = aggregate
|
68
|
+
merge_xmp
|
69
|
+
render
|
70
|
+
ret = XMP2Assert::Quasifile.new @tokens.join, *@program.locations
|
71
|
+
return ret, outputs
|
58
72
|
end
|
59
73
|
|
60
|
-
def
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
n = n.to_uri
|
66
|
-
n.gsub! %r/[:-]/, '_'
|
67
|
-
sprintf ".tap {|%s| assert_xmp(%s, %s) }\n", n, tok.chomp.dump, n
|
68
|
-
else
|
69
|
-
tok
|
70
|
-
end
|
71
|
-
end
|
74
|
+
def gensym expr
|
75
|
+
n = UUID.create_sha1 expr, Namespace
|
76
|
+
n = n.to_uri
|
77
|
+
n.gsub! %r/[:-]/, '_'
|
78
|
+
return n
|
72
79
|
end
|
73
80
|
|
74
81
|
Namespace = UUID.create_random
|
75
82
|
private_constant :Namespace
|
76
83
|
|
77
|
-
def
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
84
|
+
def gen_tap xmp
|
85
|
+
n = gensym xmp
|
86
|
+
x = xmp.chomp.dump
|
87
|
+
return sprintf ".tap {|%s| assert_xmp(%s, %s) }", n, x, n
|
88
|
+
end
|
89
|
+
|
90
|
+
def end_of_expr? tok
|
91
|
+
case tok.to_sym
|
92
|
+
when :sp then return false
|
93
|
+
when :comma then return false
|
94
|
+
when :semicolon then return false
|
95
|
+
when :comment then return false
|
96
|
+
when :'=>' then return false
|
97
|
+
when :>> then return false
|
98
|
+
when :nl then return false
|
99
|
+
when :ignored_nl then return false
|
100
|
+
when :heredoc_end then return nil # give up. too complicated
|
101
|
+
when :kw then
|
102
|
+
case tok.to_s
|
103
|
+
when 'then' then return false
|
104
|
+
when 'end' then return nil # give up. too complicated
|
105
|
+
else return true
|
89
106
|
end
|
107
|
+
else return true
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def find_stop xmp
|
112
|
+
pos = @tokens.index xmp
|
113
|
+
(pos - 1).downto 0 do |i|
|
114
|
+
case end_of_expr? @tokens[i]
|
115
|
+
when TrueClass then return true, @tokens[i]
|
116
|
+
when FalseClass then next
|
117
|
+
when NilClass then return nil, @tokens[i + 1]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
# reaching here indicates no stop; fatal.
|
121
|
+
@tokens[pos].raise
|
122
|
+
end
|
123
|
+
|
124
|
+
def valid? line
|
125
|
+
XMP2Assert::Parser.new line.join
|
126
|
+
rescue SyntaxError
|
127
|
+
return false
|
128
|
+
else
|
129
|
+
return true
|
130
|
+
end
|
131
|
+
|
132
|
+
def find_start stop
|
133
|
+
line = @program.same_line_as stop
|
134
|
+
line.sort!
|
135
|
+
line.select! {|i| i.__COLUMN__ <= stop.__COLUMN__ }
|
136
|
+
line = line.drop_while {|i| i.to_sym == :sp }
|
137
|
+
until valid? line do
|
138
|
+
line.shift
|
139
|
+
end
|
140
|
+
return line
|
141
|
+
end
|
142
|
+
|
143
|
+
def need_paren? list
|
144
|
+
start = list.first.to_sym.to_s
|
145
|
+
stop = list.last .to_sym.to_s
|
146
|
+
|
147
|
+
if %r/(.+)_beg$/ =~ start then
|
148
|
+
return %r/#{$1}_end$/ !~ stop
|
149
|
+
elsif %r/^l(paren|brace|bracket)$/ =~ start then
|
150
|
+
return %r/^r#{$1}$/ !~ stop
|
151
|
+
else
|
152
|
+
return true
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# 1. take the line that has xmp.
|
157
|
+
# 2. if that line is syntactically valid, use it.
|
158
|
+
# 3. pop some tokens so that punctuations disappear, e.g. take `1` for
|
159
|
+
#
|
160
|
+
# ```ruby
|
161
|
+
# {
|
162
|
+
# one:
|
163
|
+
# 1, # => 1
|
164
|
+
# }
|
165
|
+
# ```
|
166
|
+
#
|
167
|
+
# 4. shift some tokens so that parens etc. disappear, e.g. take `1` for
|
168
|
+
#
|
169
|
+
# ```ruby
|
170
|
+
# {
|
171
|
+
# one: 1, # => 1
|
172
|
+
# }
|
173
|
+
# ```
|
174
|
+
#
|
175
|
+
# 5. if above 3 and 4 resulted in deleting all tokens, that means the
|
176
|
+
# expression started before that line. Give up and take the whole line to
|
177
|
+
# expect it peacefully terminates something. e.g. take `}` for
|
178
|
+
#
|
179
|
+
# ```ruby
|
180
|
+
# {
|
181
|
+
# one: 1,
|
182
|
+
# } # => {:one=>1}
|
183
|
+
# ```
|
184
|
+
def rev_lookup_expr xmp
|
185
|
+
needs_start, stop = find_stop xmp
|
186
|
+
return nil, stop unless needs_start
|
187
|
+
list = find_start stop
|
188
|
+
|
189
|
+
if list.empty? then
|
190
|
+
return nil, stop
|
191
|
+
elsif need_paren? list then
|
192
|
+
return list.first, stop
|
90
193
|
else
|
91
|
-
|
194
|
+
return nil, stop
|
92
195
|
end
|
93
|
-
@ret << [pos, xmp, tok]
|
94
196
|
end
|
95
197
|
|
96
|
-
def
|
97
|
-
|
98
|
-
|
198
|
+
def aggregate
|
199
|
+
return @tokens.each_with_object String.new do |tok, r|
|
200
|
+
next unless tok.to_sym == :>>
|
201
|
+
str = tok.to_s
|
202
|
+
r << str
|
203
|
+
str.replace "\n"
|
204
|
+
end
|
99
205
|
end
|
100
206
|
|
101
|
-
def
|
102
|
-
|
103
|
-
@
|
207
|
+
def merge_xmp
|
208
|
+
xmp = nil
|
209
|
+
@tokens.each do |tok|
|
210
|
+
case tok.to_sym
|
211
|
+
when :sp then next
|
212
|
+
when :'=>' then
|
213
|
+
str = tok.to_s
|
214
|
+
if xmp then
|
215
|
+
xmp << str
|
216
|
+
str.replace "\n"
|
217
|
+
tok.yylex = :nl
|
218
|
+
else
|
219
|
+
xmp = str
|
220
|
+
end
|
221
|
+
else
|
222
|
+
xmp = nil
|
223
|
+
end
|
224
|
+
end
|
104
225
|
end
|
105
226
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
227
|
+
def render
|
228
|
+
@tokens.each do |tok|
|
229
|
+
next unless tok.to_sym == :'=>'
|
230
|
+
xmp = tok.to_s
|
231
|
+
tap = gen_tap xmp
|
232
|
+
xmp.replace "\n"
|
233
|
+
x, y = rev_lookup_expr tok
|
234
|
+
if x and x != y then
|
235
|
+
x.to_s.sub! %r/^/, '('
|
236
|
+
y.to_s.sub! %r/$/, ')'
|
237
|
+
end
|
238
|
+
y.to_s.sub! %r/$/ do
|
239
|
+
tap # use block to prevent backslash substitution
|
240
|
+
end
|
111
241
|
end
|
112
242
|
end
|
113
243
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
#! /your/favourite/path/to/ruby
|
2
|
+
# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level 2 -*-
|
3
|
+
# -*- frozen_string_literal: true -*-
|
4
|
+
# -*- warn_indent: true -*-
|
5
|
+
|
6
|
+
# Copyright (c) 2017 Urabe, Shyouhei
|
7
|
+
#
|
8
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
9
|
+
# of this software and associated documentation files (the "Software"), to deal
|
10
|
+
# in the Software without restriction, including without limitation the rights
|
11
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
12
|
+
# copies of the Software, and to permit persons to whom the Software is
|
13
|
+
# furnished to do so, subject to the following conditions:
|
14
|
+
#
|
15
|
+
# The above copyright notice and this permission notice shall be
|
16
|
+
# included in all copies or substantial portions of the Software.
|
17
|
+
#
|
18
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
19
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
20
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
21
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
22
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
23
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
24
|
+
# SOFTWARE.
|
25
|
+
;
|
26
|
+
|
27
|
+
# This is a namespace. Look at each classes under this module:
|
28
|
+
#
|
29
|
+
# - {XMP2Assert::Assertions} The assertion framework.
|
30
|
+
# - {XMP2Assert::Classifier} Check if the given file actually has the comment.
|
31
|
+
# - {XMP2Assert::Converter} Source code in-place editor using Ripper.
|
32
|
+
# - {XMP2Assert::Quasifile} IO/String abstraction layer
|
33
|
+
# - {XMP2Assert::PrettierInspect} Helper module to ease inspection.
|
34
|
+
module XMP2Assert; end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
#! /your/favourite/path/to/ruby
|
2
|
+
# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level 2 -*-
|
3
|
+
# -*- frozen_string_literal: true -*-
|
4
|
+
# -*- warn_indent: true -*-
|
5
|
+
|
6
|
+
# Copyright (c) 2017 Urabe, Shyouhei
|
7
|
+
#
|
8
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
9
|
+
# of this software and associated documentation files (the "Software"), to deal
|
10
|
+
# in the Software without restriction, including without limitation the rights
|
11
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
12
|
+
# copies of the Software, and to permit persons to whom the Software is
|
13
|
+
# furnished to do so, subject to the following conditions:
|
14
|
+
#
|
15
|
+
# The above copyright notice and this permission notice shall be
|
16
|
+
# included in all copies or substantial portions of the Software.
|
17
|
+
#
|
18
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
19
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
20
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
21
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
22
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
23
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
24
|
+
# SOFTWARE.
|
25
|
+
|
26
|
+
require 'ripper'
|
27
|
+
require_relative 'namespace'
|
28
|
+
require_relative 'quasifile'
|
29
|
+
require_relative 'token'
|
30
|
+
|
31
|
+
# This is a Ruby parser. Generates ASTs from the given program.
|
32
|
+
class XMP2Assert::Parser < Ripper
|
33
|
+
attr_reader :tokens # @return [Array<Token>] program, split into tokens.
|
34
|
+
attr_reader :sexp # @return [Array] constructed s-expression.
|
35
|
+
|
36
|
+
# @param (see XMP2Assert::Quasifile.new)
|
37
|
+
# @raise [SyntaxError] failed to parse the program.
|
38
|
+
def initialize obj, file = nil, line = nil
|
39
|
+
@qfile = XMP2Assert::Quasifile.new obj, file, line
|
40
|
+
super @qfile.read, *locations
|
41
|
+
@tokens = []
|
42
|
+
@sexp = parse
|
43
|
+
@tokens.sort!
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [String, Integer] the program's file name and line offset.
|
47
|
+
def locations
|
48
|
+
return @qfile.__FILE__, @qfile.__LINE__
|
49
|
+
end
|
50
|
+
|
51
|
+
# Find tokens that are in the same line as the argument.
|
52
|
+
#
|
53
|
+
# ```ruby
|
54
|
+
# [ 1, # => 1
|
55
|
+
# 2, # => 2
|
56
|
+
# ] # => [1, 2]
|
57
|
+
# ```
|
58
|
+
#
|
59
|
+
# It will return `[(:sp ' ') (:int 2) (:'=>' "2")]` for `(:'=>' "2")`.
|
60
|
+
#
|
61
|
+
# @param tok [Token] a token to look at.
|
62
|
+
# @return [Array] tokens of the same line as the argument.
|
63
|
+
def same_line_as tok
|
64
|
+
f, l = tok.__FILE__, tok.__LINE__
|
65
|
+
return @tokens.select {|i| i.__FILE__ == f }.select {|i| i.__LINE__ == l }
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def on_error msg
|
71
|
+
raise SyntaxError, msg
|
72
|
+
end
|
73
|
+
|
74
|
+
def on_comment c
|
75
|
+
case c
|
76
|
+
when /^# => / then
|
77
|
+
yylex = :'=>'
|
78
|
+
yylval = $'
|
79
|
+
when /^# [~>]> / then
|
80
|
+
yylex = :>>
|
81
|
+
yylval = $'
|
82
|
+
else
|
83
|
+
yylex = :comment
|
84
|
+
yylval = c
|
85
|
+
end
|
86
|
+
yylloc = [filename, lineno, column]
|
87
|
+
tok = XMP2Assert::Token.new yylex, yylval, yylloc
|
88
|
+
@tokens << tok
|
89
|
+
return tok
|
90
|
+
end
|
91
|
+
|
92
|
+
def on_scanner_event yylval
|
93
|
+
yylex = __callee__.to_s.sub(/^on_/, '').intern
|
94
|
+
yylloc = [filename, lineno, column]
|
95
|
+
tok = XMP2Assert::Token.new yylex, yylval, yylloc
|
96
|
+
@tokens << tok
|
97
|
+
return tok
|
98
|
+
end
|
99
|
+
|
100
|
+
def on_parser_event *argv
|
101
|
+
nonterminal = __callee__.to_s.sub(/^on_/, '').intern
|
102
|
+
return [nonterminal, *argv]
|
103
|
+
end
|
104
|
+
|
105
|
+
def on_parser_list_new *argv
|
106
|
+
nonterminal = __callee__.to_s.sub(/^on_(.+)_new$/, '\\1').intern
|
107
|
+
raise [__callee__, argv].inspect unless argv.empty?
|
108
|
+
return [nonterminal]
|
109
|
+
end
|
110
|
+
|
111
|
+
def on_parser_list_append list, cdr
|
112
|
+
return list << cdr
|
113
|
+
end
|
114
|
+
|
115
|
+
pim = private_instance_methods false
|
116
|
+
|
117
|
+
SCANNER_EVENTS.each do |e|
|
118
|
+
m = :"on_#{e}"
|
119
|
+
next if pim.include? m
|
120
|
+
alias_method m, :on_scanner_event
|
121
|
+
end
|
122
|
+
|
123
|
+
PARSER_EVENTS.each do |e|
|
124
|
+
m = :"on_#{e}"
|
125
|
+
next if pim.include? m
|
126
|
+
case m
|
127
|
+
when :on_assoc_new then alias_method m, :on_parser_event
|
128
|
+
when /_new$/ then alias_method m, :on_parser_list_new
|
129
|
+
when /_add$/ then alias_method m, :on_parser_list_append
|
130
|
+
else alias_method m, :on_parser_event
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
alias on_parse_error on_error
|
135
|
+
alias compile_error on_error
|
136
|
+
end
|
@@ -24,6 +24,7 @@
|
|
24
24
|
# SOFTWARE.
|
25
25
|
|
26
26
|
require 'pp'
|
27
|
+
require_relative 'namespace'
|
27
28
|
|
28
29
|
# By including this module your class gets a {#inspect} method which uses
|
29
30
|
# {::PP} methods to control outputs. You don't have to worry about redefining
|
@@ -35,7 +36,7 @@ module XMP2Assert::PrettierInspect
|
|
35
36
|
#
|
36
37
|
# Prettier inspection.
|
37
38
|
def inspect
|
38
|
-
str = PP.
|
39
|
+
str = PP.singleline_pp self, ''
|
39
40
|
str.chomp!
|
40
41
|
return str
|
41
42
|
end
|
data/lib/xmp2assert/quasifile.rb
CHANGED
@@ -25,14 +25,14 @@
|
|
25
25
|
|
26
26
|
require 'open-uri'
|
27
27
|
require 'pathname'
|
28
|
+
require_relative 'namespace'
|
29
|
+
require_relative 'prettier_inspect'
|
28
30
|
|
29
31
|
# XMP2Assert converts a ruby script into a test file but we want to hold
|
30
32
|
# original path name / line number for diagnostic purposes. So this class.
|
31
33
|
class XMP2Assert::Quasifile
|
32
34
|
include XMP2Assert::PrettierInspect
|
33
35
|
|
34
|
-
# @return [Quasifile] a new quasifile.
|
35
|
-
#
|
36
36
|
# @overload new(qfile)
|
37
37
|
# Just return the given object (for possible recursive calls).
|
38
38
|
#
|
@@ -84,6 +84,10 @@ class XMP2Assert::Quasifile
|
|
84
84
|
# @param line [Integer] line offset.
|
85
85
|
# @return [Quasifile] generated qiasifile.
|
86
86
|
#
|
87
|
+
# @return [Quasifile] a new quasifile.
|
88
|
+
# @param obj [Quasifile, URI, Pathname, String, File, IO] a file-ish.
|
89
|
+
# @param file [String] path of the file (optional).
|
90
|
+
# @param line [Integer] line offset (optional).
|
87
91
|
def self.new(obj, file = nil, line = nil)
|
88
92
|
case
|
89
93
|
when src = switch { obj.to_str } then # LIKELY
|
@@ -0,0 +1,88 @@
|
|
1
|
+
#! /your/favourite/path/to/ruby
|
2
|
+
# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level 2 -*-
|
3
|
+
# -*- frozen_string_literal: true -*-
|
4
|
+
# -*- warn_indent: true -*-
|
5
|
+
|
6
|
+
# Copyright (c) 2017 Urabe, Shyouhei
|
7
|
+
#
|
8
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
9
|
+
# of this software and associated documentation files (the "Software"), to deal
|
10
|
+
# in the Software without restriction, including without limitation the rights
|
11
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
12
|
+
# copies of the Software, and to permit persons to whom the Software is
|
13
|
+
# furnished to do so, subject to the following conditions:
|
14
|
+
#
|
15
|
+
# The above copyright notice and this permission notice shall be
|
16
|
+
# included in all copies or substantial portions of the Software.
|
17
|
+
#
|
18
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
19
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
20
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
21
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
22
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
23
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
24
|
+
# SOFTWARE.
|
25
|
+
|
26
|
+
require_relative 'namespace'
|
27
|
+
require_relative 'prettier_inspect'
|
28
|
+
|
29
|
+
# Token is a tiny class that represents a token of a Ruby program.
|
30
|
+
#
|
31
|
+
# @!attribute [rw] yylex
|
32
|
+
# @return [Symbol] terminal symbol
|
33
|
+
# @!attribute [rw] yylval
|
34
|
+
# @return [String] terminal value
|
35
|
+
# @!attribute [rw] yylloc
|
36
|
+
# @return [Array] terminal location
|
37
|
+
XMP2Assert::Token = Struct.new :yylex, :yylval, :yylloc do
|
38
|
+
include Comparable
|
39
|
+
|
40
|
+
# Comparison of location in a file, to be used with sort.
|
41
|
+
# @param other [Token] token to compare
|
42
|
+
def <=> other
|
43
|
+
yylloc <=> other.yylloc
|
44
|
+
end
|
45
|
+
|
46
|
+
alias to_sym yylex
|
47
|
+
alias to_s yylval
|
48
|
+
|
49
|
+
# @!group Token locations
|
50
|
+
|
51
|
+
# @return [String] file name
|
52
|
+
def __FILE__
|
53
|
+
return yylloc[0]
|
54
|
+
end
|
55
|
+
|
56
|
+
# @return [String] line number (1 origin)
|
57
|
+
def __LINE__
|
58
|
+
return yylloc[1]
|
59
|
+
end
|
60
|
+
|
61
|
+
# @return [String] column in a line
|
62
|
+
def __COLUMN__
|
63
|
+
return yylloc[2]
|
64
|
+
end
|
65
|
+
# @!endgroup
|
66
|
+
|
67
|
+
# Considet this token being an error.
|
68
|
+
# @param klass [Exception] exception to raise
|
69
|
+
# @param msg [String] diagnostic message
|
70
|
+
def raise klass = SyntaxError, msg = ""
|
71
|
+
l = sprintf "%s:%s", self.__FILE__, self.__LINE__
|
72
|
+
m = sprintf 'syntax error near "%s" at line %d:%d %s',
|
73
|
+
to_s, self.__LINE__, self.__COLUMN__, msg
|
74
|
+
super klass, m, [l, *caller]
|
75
|
+
end
|
76
|
+
|
77
|
+
unless $DEBUG
|
78
|
+
include ::XMP2Assert::PrettierInspect
|
79
|
+
|
80
|
+
def pretty_print pp
|
81
|
+
pp.text "("
|
82
|
+
yylex.pretty_print pp
|
83
|
+
pp.breakable " "
|
84
|
+
yylval.pretty_print pp
|
85
|
+
pp.text ")"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
data/lib/xmp2assert/version.rb
CHANGED
data/lib/xmp2assert/xmp2rexp.rb
CHANGED
data/lib/xmp2assert.rb
CHANGED
@@ -23,19 +23,10 @@
|
|
23
23
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
24
24
|
# SOFTWARE.
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
module XMP2Assert
|
34
|
-
# These files assume the namespace, hence required here inside.
|
35
|
-
require_relative 'xmp2assert/version'
|
36
|
-
require_relative 'xmp2assert/prettier_inspect'
|
37
|
-
require_relative 'xmp2assert/quasifile'
|
38
|
-
require_relative 'xmp2assert/converter'
|
39
|
-
require_relative 'xmp2assert/classifier'
|
40
|
-
require_relative 'xmp2assert/assertions'
|
41
|
-
end
|
26
|
+
require_relative 'xmp2assert/namespace' # needs be first
|
27
|
+
require_relative 'xmp2assert/version'
|
28
|
+
require_relative 'xmp2assert/prettier_inspect'
|
29
|
+
require_relative 'xmp2assert/quasifile'
|
30
|
+
require_relative 'xmp2assert/converter'
|
31
|
+
require_relative 'xmp2assert/classifier'
|
32
|
+
require_relative 'xmp2assert/assertions'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xmp2assert
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '
|
4
|
+
version: '3'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Urabe, Shyouhei
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-05-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -210,15 +210,18 @@ files:
|
|
210
210
|
- LICENSE.txt
|
211
211
|
- README.md
|
212
212
|
- Rakefile
|
213
|
-
-
|
213
|
+
- Styleguide.md
|
214
214
|
- exe/xmpcheck.rb
|
215
215
|
- lib/xmp2assert.rb
|
216
216
|
- lib/xmp2assert/assertions.rb
|
217
217
|
- lib/xmp2assert/classifier.rb
|
218
218
|
- lib/xmp2assert/converter.rb
|
219
|
+
- lib/xmp2assert/namespace.rb
|
220
|
+
- lib/xmp2assert/parser.rb
|
219
221
|
- lib/xmp2assert/prettier_inspect.rb
|
220
222
|
- lib/xmp2assert/quasifile.rb
|
221
223
|
- lib/xmp2assert/template.erb
|
224
|
+
- lib/xmp2assert/token.rb
|
222
225
|
- lib/xmp2assert/version.rb
|
223
226
|
- lib/xmp2assert/xmp2rexp.rb
|
224
227
|
- xmp2assert.gemspec
|
@@ -242,7 +245,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
242
245
|
version: '0'
|
243
246
|
requirements: []
|
244
247
|
rubyforge_project:
|
245
|
-
rubygems_version: 2.6.
|
248
|
+
rubygems_version: 2.6.12
|
246
249
|
signing_key:
|
247
250
|
specification_version: 4
|
248
251
|
summary: auto-generate assertions from `# =>` comments
|
File without changes
|