Skip to content

Commit c92678b

Browse files
authored
Merge pull request #81 from flavorjones/flavorjones-master-frozen-string-literal
support Ruby 2.4's frozen string literals (on master)
2 parents 8e40d4c + 3f8e7f3 commit c92678b

File tree

8 files changed

+42
-31
lines changed

8 files changed

+42
-31
lines changed

bin/racc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,10 @@ def main
165165
profiler.report
166166
rescue Racc::CompileError, Errno::ENOENT, Errno::EPERM => err
167167
raise if $DEBUG
168-
lineno = err.message.slice!(/\A\d+:/).to_s
168+
message = err.message.dup # string may be frozen literal
169+
lineno = message.slice!(/\A\d+:/).to_s
169170
location = lineno.empty? ? bright("#{input}:") : bright("#{input}:#{lineno}")
170-
$stderr.puts "#{red('Error: ')}#{location} #{err.message.strip}"
171+
$stderr.puts "#{red('Error: ')}#{location} #{message.strip}"
171172
exit 1
172173
end
173174
end

lib/racc/color.rb

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,43 +23,43 @@ def self.without_color
2323
def bright(text)
2424
return text unless Color.enabled?
2525
text = text.gsub(/\e\[.*?m[^\e]*\e\[0m/, "\e[0m\\0\e[1m")
26-
"\e[1m#{text}\e[0m"
26+
String.new "\e[1m#{text}\e[0m"
2727
end
2828

2929
def red(text)
3030
return text unless Color.enabled?
31-
"\e[31m#{text}\e[0m"
31+
String.new "\e[31m#{text}\e[0m"
3232
end
3333

3434
def green(text)
3535
return text unless Color.enabled?
36-
"\e[32m#{text}\e[0m"
36+
String.new "\e[32m#{text}\e[0m"
3737
end
3838

3939
def violet(text)
4040
return text unless Color.enabled?
41-
"\e[1;35m#{text}\e[0m"
41+
String.new "\e[1;35m#{text}\e[0m"
4242
end
4343

4444
# Syntax highlighting for various types of symbols...
4545
def nonterminal(text)
4646
return text unless Color.enabled?
47-
"\e[1;34m#{text}\e[0m" # blue
47+
String.new "\e[1;34m#{text}\e[0m" # blue
4848
end
4949

5050
def terminal(text)
5151
return text unless Color.enabled?
52-
"\e[1;36m\e[4m#{text}\e[0m" # cyan, with underline
52+
String.new "\e[1;36m\e[4m#{text}\e[0m" # cyan, with underline
5353
end
5454

5555
def string(text)
5656
return text unless Color.enabled?
57-
"\e[1;33m#{text}\e[0m" # bright yellow
57+
String.new "\e[1;33m#{text}\e[0m" # bright yellow
5858
end
5959

6060
def explicit_prec(text)
6161
return text unless Color.enabled?
62-
"\e[1;31m#{text}\e[0m" # bright reddish orange
62+
String.new "\e[1;31m#{text}\e[0m" # bright reddish orange
6363
end
6464
end
65-
end
65+
end

lib/racc/grammar.rb

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ def check_terminals
298298
locations = undeclared.flat_map(&:locate).map(&:rule).uniq
299299
raise CompileError, "terminal#{'s' unless undeclared.one?} " \
300300
"#{Racc.to_sentence(undeclared)} #{undeclared.one? ? 'was' : 'were'} " \
301-
"not declared in a 'token' block:\n" <<
301+
"not declared in a 'token' block:\n" +
302302
Source::SparseLines.render(locations.map(&:source))
303303
end
304304

@@ -308,15 +308,15 @@ def check_terminals
308308
raise CompileError, "token#{'s' unless wrongly_declared.one?} " \
309309
"#{Racc.to_sentence(wrongly_declared)} were declared in a 'token'" \
310310
" block, but #{wrongly_declared.one? ? 'it also has' : 'they also have'}" \
311-
" derivation rules:\n" << Source::SparseLines.render(bad_rules.map(&:source))
311+
" derivation rules:\n" + Source::SparseLines.render(bad_rules.map(&:source))
312312
end
313313
end
314314

315315
bad_strings = @symbols.select { |s| s.string_symbol? && s.nonterminal? }
316316
unless bad_strings.empty?
317317
bad_rules = bad_strings.flat_map(&:heads).map(&:rule)
318318
raise CompileError, 'you may not create derivation rules for a ' \
319-
"string literal:\n" << Source::SparseLines.render(bad_rules.map(&:source))
319+
"string literal:\n" + Source::SparseLines.render(bad_rules.map(&:source))
320320
end
321321

322322
bad_prec = @symbols.select { |s| s.assoc && s.nonterminal? }
@@ -325,7 +325,7 @@ def check_terminals
325325
raise CompileError, "token#{'s' unless bad_prec.one?} " \
326326
"#{Racc.to_sentence(bad_prec)} appeared in a prechigh/preclow " \
327327
"block, but #{bad_prec.one? ? 'it is not a' : 'they are not'} " \
328-
"terminal#{'s' unless bad_prec.one?}:\n" <<
328+
"terminal#{'s' unless bad_prec.one?}:\n" +
329329
Source::SparseLines.render(bad_rules.map(&:source))
330330
end
331331

@@ -335,7 +335,7 @@ def check_terminals
335335
unless bad_prec.empty?
336336
raise CompileError, "The following rule#{'s' unless bad_prec.one?} " \
337337
"use#{'s' if bad_prec.one?} nonterminals for explicit precedence, " \
338-
"which is not allowed:\n" <<
338+
"which is not allowed:\n" +
339339
Source::SparseLines.render(bad_prec.map(&:source))
340340
end
341341
end
@@ -344,13 +344,13 @@ def check_rules
344344
@rules.group_by(&:target).each_value do |same_lhs|
345345
same_lhs.group_by { |r| r.symbols.reject(&:hidden?) }.each_value do |same_rhs|
346346
next unless same_rhs.size > 1
347-
raise CompileError, "The following rules are duplicates:\n" <<
347+
raise CompileError, "The following rules are duplicates:\n" +
348348
Source::SparseLines.render(same_rhs.map(&:source))
349349
end
350350
end
351351

352352
unless @error.heads.empty?
353-
raise CompileError, "You cannot create rules for the error symbol:\n" <<
353+
raise CompileError, "You cannot create rules for the error symbol:\n" +
354354
Source::SparseLines.render(@error.heads.map { |ptr| ptr.rule.source} )
355355
end
356356
end
@@ -568,7 +568,7 @@ def following
568568
end
569569

570570
def to_s
571-
result = "#{@rule.target} : "
571+
result = String.new "#{@rule.target} : "
572572
if @index > 0
573573
result << "#{preceding.reject(&:hidden?).map(&:to_s).join(' ')} ."
574574
else

lib/racc/source.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def spiffy
3131
highlights.sort_by!(&:from)
3232

3333
raw = text
34-
cooked = ''
34+
cooked = String.new
3535
offset = 0
3636

3737
highlights.each do |hilite|

lib/racc/state_transition_table.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ def add_entry(all, array, chkval, ptr_array)
202202
end
203203

204204
def mkmapexp(arr)
205-
map = ''
205+
map = String.new
206206
maxdup = RE_DUP_MAX
207207

208208
arr.chunk(&:nil?).each do |is_nil, items|

lib/racc/warning.rb

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def initialize(type, title, details = nil)
5353
end
5454

5555
def to_s
56-
msg = violet('Warning: ') << bright(title)
56+
msg = violet('Warning: ') + bright(title)
5757
msg << "\n" << details if details
5858
msg
5959
end
@@ -104,7 +104,7 @@ def title
104104
def details
105105
"Its derivation rule#{'s all' unless @sym.heads.one?} contain" \
106106
"#{'s' if @sym.heads.one?} #{'an ' if @sym.heads.one?}infinite loop" \
107-
"#{'s' unless @sym.heads.one?}:\n" <<
107+
"#{'s' unless @sym.heads.one?}:\n" +
108108
@sym.heads.map { |ptr| ptr.rule.to_s }.join("\n")
109109
end
110110

@@ -175,7 +175,7 @@ def details
175175

176176
"When the next token is #{connective}#{Racc.to_sentence(tokens, 'or')}" \
177177
", it is overridden by #{rules.one? ? 'this' : 'these'} " \
178-
"higher-precedence rule#{'s' unless rules.one?}:\n" <<
178+
"higher-precedence rule#{'s' unless rules.one?}:\n" +
179179
Source::SparseLines.render(rules.map(&:source))
180180
end.join("\n\n")
181181
end
@@ -196,15 +196,15 @@ def initialize(conflict, grammar, verbose)
196196
end
197197

198198
def title
199-
"Shift/reduce conflict on #{@sym}," <<
199+
"Shift/reduce conflict on #{@sym}," +
200200
(@path.reject(&:hidden?).empty? ?
201201
' at the beginning of the parse.' :
202202
' after the following input:')
203203
end
204204

205205
def details
206206
if @path.reject(&:hidden?).empty?
207-
result = ''
207+
result = String.new
208208
else
209209
result = @path.reject(&:hidden?).map(&:to_s).join(' ') << "\n"
210210
end
@@ -253,15 +253,15 @@ def initialize(conflict, grammar, verbose)
253253
end
254254

255255
def title
256-
"Reduce/reduce conflict on #{@sym}," <<
256+
"Reduce/reduce conflict on #{@sym}," +
257257
(@path.reject(&:hidden?).empty? ?
258258
' at the beginning of the parse.' :
259259
' after the following input:')
260260
end
261261

262262
def details
263263
if @path.reject(&:hidden?).empty?
264-
result = ''
264+
result = String.new
265265
else
266266
result = @path.reject(&:hidden?).map(&:to_s).join(' ') << "\n"
267267
end
@@ -300,4 +300,4 @@ def type
300300
end
301301
end
302302
end
303-
end
303+
end

test/run_tests.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
11
#!/usr/bin/env bash
22

3+
set -eux
4+
5+
test_frozen_strings=$(ruby -e 'puts (RUBY_ENGINE == "ruby" && RUBY_VERSION > "2.4")')
6+
7+
if [[ $test_frozen_strings == "true" ]] ; then
8+
echo "NOTE: enabling frozen string literals"
9+
rvm install rubygems 2.6.12 --force # because of an issue in rubygems 2.7 with ruby 2.5 and frozen string literals
10+
export RUBYOPT="--enable-frozen-string-literal --debug=frozen-string-literal"
11+
fi
12+
313
bundle exec rake test
414
bundle exec rake test_pure

test/test_scanner.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ class TestScanner < TestCase
99
define_method("test_scan_#{File.basename(testfile)}".to_sym) do
1010
original = File.read(testfile)
1111
# wrap the Ruby source code in an action block
12-
wrapped = "class Test\nrule\na : '*' {" << original << "\n}"
12+
wrapped = "class Test\nrule\na : '*' {" + original + "\n}"
1313
file = Source::Buffer.new(testfile, wrapped)
1414
scanner = Racc::GrammarFileScanner.new(file)
1515

16-
rebuilt = ''
16+
rebuilt = String.new
1717
scanner.yylex do |token|
1818
break if token.nil?
1919
rebuilt << token[1][0].text if token[0] == :ACTION

0 commit comments

Comments
 (0)