Module: RubyNext::Language
- Defined in:
- lib/ruby-next/language.rb,
lib/ruby-next/language/eval.rb,
lib/ruby-next/language/setup.rb,
lib/ruby-next/language/parser.rb,
lib/ruby-next/language/runtime.rb,
lib/ruby-next/language/rewriters/base.rb,
lib/ruby-next/language/rewriters/text.rb,
lib/ruby-next/language/paco_parsers/base.rb,
lib/ruby-next/language/rewriters/2.4/dir.rb,
lib/ruby-next/language/rewriters/abstract.rb,
lib/ruby-next/language/paco_parsers/comments.rb,
lib/ruby-next/language/rewriters/3.4/it_param.rb,
lib/ruby-next/language/rewriters/3.0/in_pattern.rb,
lib/ruby-next/language/rewriters/2.7/args_forward.rb,
lib/ruby-next/language/rewriters/3.0/find_pattern.rb,
lib/ruby-next/language/rewriters/2.6/endless_range.rb,
lib/ruby-next/language/paco_parsers/string_literals.rb,
lib/ruby-next/language/rewriters/3.0/endless_method.rb,
lib/ruby-next/language/rewriters/3.1/shorthand_hash.rb,
lib/ruby-next/language/rewriters/2.1/required_kwargs.rb,
lib/ruby-next/language/rewriters/2.3/safe_navigation.rb,
lib/ruby-next/language/rewriters/2.7/numbered_params.rb,
lib/ruby-next/language/rewriters/3.1/anonymous_block.rb,
lib/ruby-next/language/rewriters/2.1/numeric_literals.rb,
lib/ruby-next/language/rewriters/2.3/squiggly_heredoc.rb,
lib/ruby-next/language/rewriters/2.7/pattern_matching.rb,
lib/ruby-next/language/rewriters/3.1/pin_vars_pattern.rb,
lib/ruby-next/language/rewriters/3.2/anonymous_restargs.rb,
lib/ruby-next/language/rewriters/2.5/rescue_within_block.rb,
lib/ruby-next/language/rewriters/3.0/args_forward_leading.rb,
lib/ruby-next/language/rewriters/proposed/method_reference.rb,
lib/ruby-next/language/rewriters/3.1/endless_method_command.rb,
lib/ruby-next/language/rewriters/proposed/bind_vars_pattern.rb,
lib/ruby-next/language/rewriters/3.1/refinement_import_methods.rb,
lib/ruby-next/language/rewriters/3.1/oneline_pattern_parensless.rb
Overview
Language module contains tools to transpile newer Ruby syntax into an older one.
It works the following way:
- Takes a Ruby source code as input
- Generates the AST using the edge parser (via the `parser` gem)
- Pass this AST through the list of processors (one feature = one processor)
- Each processor may modify the AST
- Generates a transpiled source code from the transformed AST (via the `unparser` gem)
Defined Under Namespace
Modules: BuilderExt, ClassEval, Eval, GemTranspiler, InstanceEval, KernelEval, PacoParsers, Rewriters, Runtime Classes: Builder, PrismBuilder, TransformContext
Constant Summary collapse
- RewriterNotFoundError =
Class.new(StandardError)
- MODES =
%i[rewrite ast].freeze
Class Attribute Summary collapse
-
.builder_class ⇒ Object
Returns the value of attribute builder_class.
-
.exclude_patterns ⇒ Object
readonly
Returns the value of attribute exclude_patterns.
-
.include_patterns ⇒ Object
readonly
Returns the value of attribute include_patterns.
-
.mode ⇒ Object
Returns the value of attribute mode.
-
.parser_class ⇒ Object
Returns the value of attribute parser_class.
-
.parser_syntax_errors ⇒ Object
Returns the value of attribute parser_syntax_errors.
-
.rewriters ⇒ Object
Returns the value of attribute rewriters.
-
.strategy ⇒ Object
Returns the value of attribute strategy.
- .watch_dirs ⇒ Object readonly
Class Method Summary collapse
- .ast? ⇒ Boolean
-
.current_rewriters ⇒ Object
Rewriters required for the current version.
- .parse(source, file = "(string)") ⇒ Object
- .parse_with_comments(source, file = "(string)") ⇒ Object
- .parser ⇒ Object
- .rewrite? ⇒ Boolean
- .runtime! ⇒ Object
- .runtime? ⇒ Boolean
-
.select_rewriters(*names) ⇒ Object
This method guarantees that rewriters will be returned in order they defined in Language module.
- .setup_gem_load_path(lib_dir = "lib", rbnext_dir: RUBY_NEXT_DIR, transpile: false) ⇒ Object
- .target_dir?(dirname) ⇒ Boolean
- .transform(source, rewriters: self.rewriters, using: RubyNext::Core.refine?, path: nil, context: TransformContext.new(path: path)) ⇒ Object
- .transformable?(path) ⇒ Boolean
Instance Method Summary collapse
Class Attribute Details
.builder_class ⇒ Object
Returns the value of attribute builder_class.
53 54 55 |
# File 'lib/ruby-next/language/parser.rb', line 53 def builder_class @builder_class end |
.exclude_patterns ⇒ Object
Returns the value of attribute exclude_patterns.
70 71 72 |
# File 'lib/ruby-next/language.rb', line 70 def exclude_patterns @exclude_patterns end |
.include_patterns ⇒ Object
Returns the value of attribute include_patterns.
69 70 71 |
# File 'lib/ruby-next/language.rb', line 69 def include_patterns @include_patterns end |
.mode ⇒ Object
Returns the value of attribute mode.
83 84 85 |
# File 'lib/ruby-next/language.rb', line 83 def mode @mode end |
.parser_class ⇒ Object
Returns the value of attribute parser_class.
53 54 55 |
# File 'lib/ruby-next/language/parser.rb', line 53 def parser_class @parser_class end |
.parser_syntax_errors ⇒ Object
Returns the value of attribute parser_syntax_errors.
53 54 55 |
# File 'lib/ruby-next/language/parser.rb', line 53 def parser_syntax_errors @parser_syntax_errors end |
.rewriters ⇒ Object
Returns the value of attribute rewriters.
77 78 79 |
# File 'lib/ruby-next/language.rb', line 77 def rewriters @rewriters end |
.strategy ⇒ Object
Returns the value of attribute strategy.
79 80 81 |
# File 'lib/ruby-next/language.rb', line 79 def strategy @strategy end |
.watch_dirs ⇒ Object
72 73 74 75 |
# File 'lib/ruby-next/language.rb', line 72 def watch_dirs warn "[DEPRECATED] Use `RubyNext::Language.include_patterns` instead of `RubyNext::Language.watch_dirs`" @watch_dirs end |
Class Method Details
.ast? ⇒ Boolean
94 95 96 |
# File 'lib/ruby-next/language.rb', line 94 def ast? mode == :ast end |
.current_rewriters ⇒ Object
Rewriters required for the current version
150 151 152 |
# File 'lib/ruby-next/language.rb', line 150 def current_rewriters @current_rewriters ||= rewriters.select(&:unsupported_syntax?) end |
.parse(source, file = "(string)") ⇒ Object
63 64 65 66 67 68 69 70 71 |
# File 'lib/ruby-next/language/parser.rb', line 63 def parse(source, file = "(string)") buffer = ::Parser::Source::Buffer.new(file).tap do |buffer| buffer.source = source end parser.parse(buffer) rescue *parser_syntax_errors => e raise ::SyntaxError, e., e.backtrace end |
.parse_with_comments(source, file = "(string)") ⇒ Object
73 74 75 76 77 78 79 80 81 |
# File 'lib/ruby-next/language/parser.rb', line 73 def parse_with_comments(source, file = "(string)") buffer = ::Parser::Source::Buffer.new(file).tap do |buffer| buffer.source = source end parser.parse_with_comments(buffer) rescue *parser_syntax_errors => e raise ::SyntaxError, e., e.backtrace end |
.parser ⇒ Object
55 56 57 58 59 60 61 |
# File 'lib/ruby-next/language/parser.rb', line 55 def parser parser_class.new(builder_class.new).tap do |prs| prs.diagnostics.tap do |diagnostics| diagnostics.all_errors_are_fatal = true end end end |
.rewrite? ⇒ Boolean
90 91 92 |
# File 'lib/ruby-next/language.rb', line 90 def rewrite? mode == :rewrite? end |
.runtime! ⇒ Object
98 99 100 101 102 |
# File 'lib/ruby-next/language.rb', line 98 def runtime! require "ruby-next/language/rewriters/runtime" @runtime = true end |
.runtime? ⇒ Boolean
104 105 106 |
# File 'lib/ruby-next/language.rb', line 104 def runtime? @runtime end |
.select_rewriters(*names) ⇒ Object
This method guarantees that rewriters will be returned in order they defined in Language module
155 156 157 158 159 160 161 162 |
# File 'lib/ruby-next/language.rb', line 155 def select_rewriters(*names) rewriters_delta = names - rewriters.map { |rewriter| rewriter::NAME } if rewriters_delta.any? raise RewriterNotFoundError, "Rewriters not found: #{rewriters_delta.join(",")}" end rewriters.select { |rewriter| names.include?(rewriter::NAME) } end |
.setup_gem_load_path(lib_dir = "lib", rbnext_dir: RUBY_NEXT_DIR, transpile: false) ⇒ Object
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/ruby-next/language/setup.rb', line 34 def setup_gem_load_path(lib_dir = "lib", rbnext_dir: RUBY_NEXT_DIR, transpile: false) called_from = caller_locations(1, 1).first.path dirname = File.realpath(File.dirname(called_from)) loop do basename = File.basename(dirname) raise "Couldn't find gem's load dir: #{lib_dir}" if basename == dirname break if basename == lib_dir dirname = File.dirname(basename) end dirname = File.realpath(dirname) return if Language.runtime? && Language.target_dir?(dirname) next_dirname = File.join(dirname, rbnext_dir) GemTranspiler.maybe_transpile(File.dirname(dirname), lib_dir, next_dirname) if transpile current_index = $LOAD_PATH.find_index do |load_path| pn = Pathname.new(load_path) pn.exist? && pn.realpath.to_s == dirname end raise "Gem's lib is not in the $LOAD_PATH: #{dirname}" if current_index.nil? version = RubyNext.next_ruby_version loop do break unless version version_dir = File.join(next_dirname, version.segments[0..1].join(".")) if File.exist?(version_dir) $LOAD_PATH.insert current_index, version_dir current_index += 1 end version = RubyNext.next_ruby_version(version) end end |
.target_dir?(dirname) ⇒ Boolean
136 137 138 139 140 141 142 |
# File 'lib/ruby-next/language.rb', line 136 def target_dir?(dirname) # fnmatch? requires a file name, not a folder fname = File.join(dirname, "x.rb") include_patterns.any? { |pattern| File.fnmatch?(pattern, fname) } && exclude_patterns.none? { |pattern| File.fnmatch?(pattern, fname) } end |
.transform(source, rewriters: self.rewriters, using: RubyNext::Core.refine?, path: nil, context: TransformContext.new(path: path)) ⇒ Object
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/ruby-next/language.rb', line 108 def transform(source, rewriters: self.rewriters, using: RubyNext::Core.refine?, path: nil, context: TransformContext.new(path: path)) text_rewriters, ast_rewriters = rewriters.partition(&:text?) retried = 0 new_source = text_rewrite(source, rewriters: text_rewriters, using: using, context: context) begin new_source = if mode == :rewrite rewrite(new_source, rewriters: ast_rewriters, using: using, context: context) else regenerate(new_source, rewriters: ast_rewriters, using: using, context: context) end rescue Unparser::UnknownNodeError => err RubyNext.warn "Ruby Next fallbacks to \"rewrite\" transpiling mode since the version of Unparser you use doesn't support some syntax yet: #{err.}.\n" \ "Try upgrading the Unparser or set transpiling mode to \"rewrite\" in case you use some edge or experimental syntax." self.mode = :rewrite retried += 1 retry unless retried > 1 raise end return new_source unless RubyNext::Core.refine? return new_source unless using && context.use_ruby_next? Core.inject! new_source.dup end |
.transformable?(path) ⇒ Boolean
144 145 146 147 |
# File 'lib/ruby-next/language.rb', line 144 def transformable?(path) include_patterns.any? { |pattern| File.fnmatch?(pattern, path) } && exclude_patterns.none? { |pattern| File.fnmatch?(pattern, path) } end |
Instance Method Details
#runtime? ⇒ Boolean
29 30 31 |
# File 'lib/ruby-next/language/setup.rb', line 29 def runtime? false end |