M .rubocop.yml => .rubocop.yml +3 -0
@@ 7,6 7,9 @@ Style/WordArray:
Style/PerlBackrefs:
Enabled: false
+Style/IndentArray:
+ Enabled: false
+
Metrics/LineLength:
Max: 120
M Gemfile => Gemfile +1 -1
@@ 4,7 4,7 @@ gem 'parslet'
gem 'pry'
group :development do
- gem 'rspec'
gem 'pry-stack_explorer'
+ gem 'rspec'
gem 'stackprof'
end
M bin/scheme => bin/scheme +1 -1
@@ 6,7 6,7 @@ require_relative '../program'
options = { debug: 0 }
opt_parser = OptionParser.new do |opts|
opts.banner = "Usage: scheme [options] path [arguments]\n" \
- " scheme [options] -e \"(print (+ 2 2))\" [arguments]"
+ ' scheme [options] -e "(print (+ 2 2))" [arguments]'
opts.on('-e SCRIPT', 'Execute script passed as argument') do |script|
options[:script] = script
M compiler.rb => compiler.rb +10 -10
@@ 10,7 10,7 @@ require 'pry'
class Compiler
ROOT_PATH = VM::ROOT_PATH
- LOAD_PATH = [File.join(ROOT_PATH, 'lib'), File.join(ROOT_PATH, 'spec')]
+ LOAD_PATH = [File.join(ROOT_PATH, 'lib'), File.join(ROOT_PATH, 'spec')].freeze
include Compiler::Libraries
include Compiler::Lib::Scheme::Base
@@ 99,7 99,7 @@ class Compiler
elsif options[:locals][name]
call(sexp, options)
else
- fail VM::VariableUndefined, name
+ raise VM::VariableUndefined, name
end
end
@@ 118,7 118,7 @@ class Compiler
(name, *args) = sexp
expr = compile_sexp(args.first, options.merge(quasiquote: false))
if name == 'unquote-splicing'
- fail 'can only use unquote-splicing with a list' if expr.compact.last != VM::PUSH_LIST
+ raise 'can only use unquote-splicing with a list' if expr.compact.last != VM::PUSH_LIST
['splice', expr.first]
else
expr
@@ 219,7 219,7 @@ class Compiler
end
def push_var(name, options)
- fail VM::VariableUndefined, name unless options[:locals][name]
+ raise VM::VariableUndefined, name unless options[:locals][name]
[
VM::PUSH_VAR,
name
@@ 245,12 245,12 @@ class Compiler
end
def parse_file(filename, relative_to: nil)
- if filename =~ /\A\./ && relative_to
- path = File.join(File.dirname(relative_to), filename)
- else
- path = @load_path.map { |p| File.join(p, filename) }.detect { |p| File.exist?(p) }
- end
- fail "File #{filename} not found in load path #{@load_path.join(';')}" unless path
+ path = if filename.start_with?('.') && relative_to
+ File.join(File.dirname(relative_to), filename)
+ else
+ @load_path.map { |p| File.join(p, filename) }.detect { |p| File.exist?(p) }
+ end
+ raise "File #{filename} not found in load path #{@load_path.join(';')}" unless path
code = File.read(path)
@source[filename] = code
Parser.new(code, filename: filename).parse
M compiler/lib/scheme/base.rb => compiler/lib/scheme/base.rb +2 -2
@@ 12,7 12,7 @@ class Compiler
end
def base_apply((lambda, *args), options)
- fail 'apply expects at least 2 arguments' if args.empty?
+ raise 'apply expects at least 2 arguments' if args.empty?
[
args.map { |arg| compile_sexp(arg, options.merge(use: true)) },
VM::PUSH_NUM, args.size,
@@ 56,7 56,7 @@ class Compiler
end
def base_cons(args, options)
- fail 'cons expects exactly 2 arguments' if args.size != 2
+ raise 'cons expects exactly 2 arguments' if args.size != 2
[
args.map { |arg| compile_sexp(arg, options.merge(use: true)) },
VM::PUSH_CONS,
M compiler/libraries.rb => compiler/libraries.rb +2 -2
@@ 12,7 12,7 @@ class Compiler
def do_include(paths, relative_to, options)
paths.map do |path|
- fail "include expects a string, but got #{path.inspect}" unless path =~ /\A"(.+)?"\z/
+ raise "include expects a string, but got #{path.inspect}" unless path =~ /\A"(.+)?"\z/
filename = "#{$1}.scm"
sexps = parse_file(filename, relative_to: relative_to)
compile_sexps(sexps, options: options)
@@ 69,7 69,7 @@ class Compiler
[name, internal_name, renamed[external_name] || external_name, syntax]
end
else
- fail "unknown import directive #{directive}"
+ raise "unknown import directive #{directive}"
end
[include, bindings]
end
M compiler/macro.rb => compiler/macro.rb +1 -1
@@ 23,7 23,7 @@ class Compiler
values = Pattern.new(pattern, literals: @literals).match(sexp)
return [values, template] if values
end
- fail "Could not match any template for #{sexp.inspect}"
+ raise "Could not match any template for #{sexp.inspect}"
end
def expand_template(template)
M parser.rb => parser.rb +1 -1
@@ 71,7 71,7 @@ module LISP
',@' => 'unquote-splicing',
',' => 'unquote',
'`' => 'quasiquote'
- }
+ }.freeze
rule(comment: subtree(:comment))
M spec/lib_spec.rb => spec/lib_spec.rb +1 -1
@@ 27,7 27,7 @@ Dir[File.expand_path('../lib/**/*.scm', __FILE__)].each do |path|
puts result
failed = true
end
- fail 'spec failed' if failed
+ raise 'spec failed' if failed
end
end
end
M spec/support/dumpable_string_io.rb => spec/support/dumpable_string_io.rb +1 -2
@@ 4,6 4,5 @@ class DumpableStringIO < StringIO
[]
end
- def marshal_load(_data)
- end
+ def marshal_load(_data); end
end
M vm.rb => vm.rb +6 -6
@@ 79,7 79,7 @@ class VM
['IMPORT_LIB', 3],
['HALT', 0],
['DEBUG', 0]
- ]
+ ].freeze
INSTRUCTIONS.each_with_index do |(name, _arity), index|
const_set(name.to_sym, index)
@@ 96,7 96,7 @@ class VM
VM::EmptyList,
VM::Int,
VM::Pair
- ]
+ ].freeze
attr_reader :stack, :heap, :ip, :call_stack, :closures, :call_args, :libs, :last_atom
attr_accessor :stdout, :debug
@@ 130,7 130,7 @@ class VM
debug_output.print_ip if debug >= 2
case instruction
when NOOP
- # nothing
+ :noop
when RETURN
do_return(debug)
when DEBUG
@@ 143,7 143,7 @@ class VM
end
def fetch
- fail "heap[#{@ip}] is not executable" unless executable?(@ip)
+ raise "heap[#{@ip}] is not executable" unless executable?(@ip)
instruction = @heap[@ip]
@ip += 1
instruction
@@ 165,7 165,7 @@ class VM
def resolve(address)
return Unspecified.instance if address.nil?
- @heap[address] || fail("invalid address #{address}")
+ @heap[address] || raise("invalid address #{address}")
end
def push(address)
@@ 316,7 316,7 @@ class VM
def return_value
if peek
val = pop_raw
- return val if val.is_a?(Fixnum)
+ return val if val.is_a?(Integer)
return 1 if val == false
end
0
M vm/debug_output.rb => vm/debug_output.rb +3 -4
@@ 60,10 60,9 @@ class VM
def print_stack
print 'stack: '
puts @vm.stack.map { |a| a ? @vm.heap[a].inspect : a.inspect }.join("\n" + (' ' * 28))
- if (atom = @vm.last_atom)
- print 'code: '.rjust(28)
- puts "#{atom.filename}##{atom.line}:#{atom.column}"
- end
+ return unless (atom = @vm.last_atom)
+ print 'code: '.rjust(28)
+ puts "#{atom.filename}##{atom.line}:#{atom.column}"
end
end
end
M vm/gc.rb => vm/gc.rb +1 -1
@@ 9,7 9,7 @@ class VM
next if value.nil?
next if active?(location)
puts "garbage collecting #{value.inspect} at #{location}" if debug >= 3
- fail "heap[#{@ip}] is not writable" unless @vm.writable?(location)
+ raise "heap[#{@ip}] is not writable" unless @vm.writable?(location)
@vm.heap[location] = nil
end
end
M vm/int.rb => vm/int.rb +3 -3
@@ 7,7 7,7 @@ class VM
def initialize(num)
@num = num.to_i
- fail 'integer out of range' if @num < MIN || @num > MAX
+ raise 'integer out of range' if @num < MIN || @num > MAX
end
def raw
@@ 19,8 19,8 @@ class VM
raw == other.raw
end
- alias_method :==, :eq?
- alias_method :eqv?, :eq?
+ alias == eq?
+ alias eqv? eq?
def +(other)
Int.new(raw + other.raw)
M vm/operations.rb => vm/operations.rb +11 -14
@@ 58,7 58,7 @@ class VM
def do_push_local
name = fetch
address = named_args[name] || locals[name]
- fail VariableUndefined, name unless address
+ raise VariableUndefined, name unless address
push(address)
end
@@ 72,13 72,13 @@ class VM
address = c[:locals][name]
push(address)
else
- fail VariableUndefined, name
+ raise VariableUndefined, name
end
end
def do_push_args
last = empty_list
- while args.size > 0
+ until args.empty?
arg = args.pop
address = alloc
@heap[address] = build_pair(arg, last)
@@ 141,7 141,7 @@ class VM
@call_stack << { func: new_ip, return: @ip, args: @call_args, named_args: {} }
end
pp @call_stack if @call_stack.size > MAX_CALL_DEPTH
- fail CallStackTooDeep, 'call stack too deep' if @call_stack.size > MAX_CALL_DEPTH
+ raise CallStackTooDeep, 'call stack too deep' if @call_stack.size > MAX_CALL_DEPTH
@ip = new_ip
end
@@ 153,7 153,7 @@ class VM
end
new_ip = pop
@call_stack << { func: new_ip, return: @ip, args: @call_args, named_args: {} }
- fail CallStackTooDeep, 'call stack too deep' if @call_stack.size > MAX_CALL_DEPTH
+ raise CallStackTooDeep, 'call stack too deep' if @call_stack.size > MAX_CALL_DEPTH
@ip = new_ip
end
@@ 283,7 283,7 @@ class VM
def do_append
count = pop_raw
- if count == 0
+ if count.zero?
push(empty_list)
else
raw = (0...count).map { pop_val }.reverse.map(&:to_a).inject(&:+)
@@ 300,10 300,10 @@ class VM
def do_raw
raw = pop_raw
case raw
- when Fixnum
+ when Integer
push_val(VM::Int.new(raw))
else
- fail "unknown raw value type #{raw.inspect}"
+ raise "unknown raw value type #{raw.inspect}"
end
end
@@ 319,17 319,14 @@ class VM
elsif (c = find_closure_with_symbol(name))
c[:locals][name] = pop
else
- fail VariableUndefined, name
+ raise VariableUndefined, name
end
end
def do_set_args
count = pop_raw
- if @stack.size >= count
- @call_args = (0...count).map { pop }.reverse
- else
- fail NoStackValue, "stack size is #{@stack.size}, but you tried to use #{count}"
- end
+ raise NoStackValue, "stack size is #{@stack.size}, but you tried to use #{count}" if @stack.size < count
+ @call_args = (0...count).map { pop }.reverse
end
def do_set_arg
M vm/pair.rb => vm/pair.rb +1 -1
@@ 17,7 17,7 @@ class VM
def to_s
items = to_a.flat_map do |item|
- if item.is_a?(Fixnum) # address
+ if item.is_a?(Integer) # address
@heap[item].to_s
else
['.', item.to_s]