~tim/scheme-vm

b4dade13d0a03dcaff651fe7b6e8d2db434158a4 — Tim Morgan 2 years ago 4d798bd
Extract file loading from Compiler
4 files changed, 40 insertions(+), 18 deletions(-)

M compiler.rb
M compiler/libraries.rb
A loader.rb
M spec/program_spec.rb
M compiler.rb => compiler.rb +1 -16
@@ 9,20 9,16 @@ require 'pp'
require 'pry'

class Compiler
  ROOT_PATH = VM::ROOT_PATH
  LOAD_PATH = [File.join(ROOT_PATH, 'lib')].freeze

  include Compiler::Libraries
  include Compiler::Lib::Scheme::Base
  include Compiler::Lib::Scheme::ProcessContext
  include Compiler::Lib::Scheme::Write

  def initialize(ast = nil, filename:, arguments: {}, load_path: LOAD_PATH, program:)
  def initialize(ast = nil, filename:, arguments: {}, program:)
    @program = program
    @variables = {}
    @filename = filename
    @arguments = arguments
    @load_path = load_path
    @syntax = {}              # macro transformers
    @locals = {}              # top-level locals (globals)
    @libs = {}                # loaded libraries


@@ 284,15 280,4 @@ class Compiler
  def source
    @program.source
  end

  def parse_file(filename, relative_to: nil)
    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)
    @program.parse(code, filename: filename)
  end
end

M compiler/libraries.rb => compiler/libraries.rb +8 -1
@@ 1,4 1,5 @@
require_relative '../vm'
require_relative '../loader'

class Compiler
  module Libraries


@@ 16,11 17,17 @@ class Compiler
      paths.map do |path|
        raise "include expects a string, but got #{path.inspect}" unless path =~ /\A"(.+)?"\z/
        filename = $1
        sexps = parse_file(filename, relative_to: relative_to)
        sexps = load_and_parse_file(filename, relative_to: relative_to)
        compile_sexps(sexps, options: options)
      end
    end

    def load_and_parse_file(filename, relative_to:)
      loader = Loader.new(filename, relative_to: relative_to)
      loader.load
      @program.parse(loader.code, filename: loader.path)
    end

    def do_import((*sets), relative_to, options)
      sets.map do |set|
        import_set(set, relative_to, options)

A loader.rb => loader.rb +30 -0
@@ 0,0 1,30 @@
require 'pathname'
require_relative './vm'

class Loader
  ROOT_PATH = VM::ROOT_PATH
  LOAD_PATH = [File.join(ROOT_PATH, 'lib')].freeze

  def initialize(filename, relative_to: nil)
    @filename = filename
    @relative_to = relative_to
  end

  attr_reader :filename, :relative_to, :path, :code

  def load
    @path = find_path
    raise "File #{filename} not found in load path #{LOAD_PATH.join(';')}" unless @path
    @code = File.read(@path)
  end

  private

  def find_path
    if filename.start_with?('.') && relative_to
      File.expand_path(File.join(File.dirname(relative_to), filename))
    else
      LOAD_PATH.map { |p| File.expand_path(File.join(p, filename)) }.detect { |p| File.exist?(p) }
    end
  end
end

M spec/program_spec.rb => spec/program_spec.rb +1 -1
@@ 265,7 265,7 @@ describe Program do
        stdout.rewind
        expect(stdout.read).to eq(
          "Error: foo is not defined\n\n" \
            "./fixtures/bad-macro.scm#5\n\n" \
            "#{File.expand_path('../fixtures/bad-macro.scm', __FILE__)}#5\n\n" \
            "    (syntax-rules ()\n" \
            "      ((bad-macro) (foo))))\n" \
            "                    ^ foo is not defined\n"