Skip to content

Instantly share code, notes, and snippets.

@jenya239
Created November 8, 2024 14:16
Show Gist options
  • Save jenya239/03e565e6af3fbd93934647dd4c0251e7 to your computer and use it in GitHub Desktop.
Save jenya239/03e565e6af3fbd93934647dd4c0251e7 to your computer and use it in GitHub Desktop.
ruby esm
require 'awesome_print'
require 'active_support/all'
class Modules
include Singleton
attr_reader :src_dir
def initialize
@src_dir = find_src_dir
@by_path = {}
end
def find_src_dir(start_path = Dir.pwd)
path = Pathname.new(start_path)
until path.root?
src_path = path.join('src')
return src_path.to_s if src_path.directory?
path = path.parent
end
nil
end
def register(esmod)
@by_path[esmod.path] = esmod
end
def resolve(path)
esmod = @by_path.key?( path ) ? @by_path[path] : ESModule.new(path)
end
end
class Import
attr_reader :esmod, :index, :import, :from, :line, :path, :imported, :names
def initialize(esmod, index, import, from, line)
@esmod, @index, @import, @from, @line = esmod, index, import, from, line
@names = @import.split(',').map(&:strip).map(&:to_sym)
ap names: @names
@modules = Modules.instance
resolve_path
@imported = @modules.resolve(@path)
end
def resolve_path
dir = @from.start_with?('/') ? @modules.src_dir : @esmod.dir
@path = File.expand_path( File.join( dir, @from + '.rb' ) )
# ap import: @from, path: @path
end
end
class ESModule
attr_reader :path, :dir, :exports
def initialize(path)
# \s*#\s+import ... from ...
@modules = Modules.instance
ap @modules.src_dir
@path = File.expand_path( path )
@modules.register(self)
@dir = File.dirname(@path)
@contents = File.read(@path)
@imports = []
@exports = {}
process_imports
# process conent
# local_variable_set для всех импортов
module_eval
# вычесть из локалов то что было импортировано
ap path: @path, size: @contents.size, locals: @binding.local_variables, lines: @lines.size, imports: @imports.size, imported_names: imported_names, exported_names: exported_names, exports: @exports
# v: @binding.local_variable_get(:v),
end
def process_imports
@original_contents = @contents.dup
@lines = @contents.split("\n")
@lines.each_with_index do |line, index|
if line =~ /^\s*$/
# ap index:, msg: :empty
elsif line =~ /^\s*\#\s*\#/
# ap index:, msg: :import_comment
elsif line =~ /^\s*\#\s*import\s+(.+)\s*from\s*(.+)\s*$/
@imports << Import.new( self, index, $1.strip, $2.strip, line )
# ap index:, import: $1.strip, from: $2.strip
else
# ap index:, msg: :end_of_imports
@after_imports_line_index = index
break
end
end
end
def imported_names
@imports.map(&:names).flatten.uniq
end
def exported_names
@binding.local_variables.reject { |sym| sym.to_s.start_with?('_') } - imported_names
end
def module_eval
@binding = binding
@imports.each do |i|
i.names.each do |name|
@binding.local_variable_set(name, i.imported.exports[name])
end
end
@binding.eval(@contents)
exported_names.each do |name|
@exports[name] = @binding.local_variable_get(name)
end
end
end
main = ESModule.new('mod.rb')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment