From dd58f1ade013acdeca8806d1f45dfc7e98f077ca Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 3 Nov 2025 01:39:28 +0000 Subject: [PATCH 1/2] Remove support for ruby-3.2 --- .github/workflows/ci.yml | 10 +++++----- .rubocop.yml | 2 +- README.md | 2 +- lib/unparser/emitter/primitive.rb | 17 +---------------- spec/unit/unparser_spec.rb | 16 ---------------- unparser.gemspec | 2 +- 6 files changed, 9 insertions(+), 40 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c451d287..1d44bd19 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-3.2, ruby-3.3, ruby-3.4] + ruby: [ruby-3.3, ruby-3.4] os: [ubuntu-latest] steps: - uses: actions/checkout@v4 @@ -35,7 +35,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-3.2, ruby-3.3, ruby-3.4] + ruby: [ruby-3.3, ruby-3.4] os: [ubuntu-latest] steps: - uses: actions/checkout@v4 @@ -53,7 +53,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-3.2, ruby-3.3, ruby-3.4] + ruby: [ruby-3.3, ruby-3.4] os: [ubuntu-latest] steps: - uses: actions/checkout@v4 @@ -69,7 +69,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-3.2, ruby-3.3, ruby-3.4] + ruby: [ruby-3.3, ruby-3.4] os: [ubuntu-latest] steps: - uses: actions/checkout@v4 @@ -85,7 +85,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-3.2, ruby-3.3, ruby-3.4] + ruby: [ruby-3.3, ruby-3.4] os: [ubuntu-latest] steps: - uses: actions/checkout@v4 diff --git a/.rubocop.yml b/.rubocop.yml index 62416515..3465fe09 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -5,7 +5,7 @@ AllCops: - '**/*.rake' - 'Gemfile' - 'Gemfile.triage' - TargetRubyVersion: 3.1 + TargetRubyVersion: 3.3 Exclude: - tmp/**/* - vendor/**/* diff --git a/README.md b/README.md index b37568cc..6082a077 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ The following constraints apply: * No support for macruby extensions * Only support for the [modern AST](https://github.com/whitequark/parser/#usage) format -* Only support for Ruby >= 3.2 +* Only support for Ruby >= 3.3 Notable Users: diff --git a/lib/unparser/emitter/primitive.rb b/lib/unparser/emitter/primitive.rb index 0bc0bcbf..51f95af6 100644 --- a/lib/unparser/emitter/primitive.rb +++ b/lib/unparser/emitter/primitive.rb @@ -13,23 +13,8 @@ class Symbol < self private - # mutant:disable def dispatch - if inspect_breaks_parsing? - write(":#{value.name.inspect}") - else - write(value.inspect) - end - end - - # mutant:disable - def inspect_breaks_parsing? - return false unless RUBY_VERSION < '3.2.' - - Unparser.parse(value.inspect) - false - rescue Parser::SyntaxError - true + write(value.inspect) end end # Symbol diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index a2499ee1..bf84f584 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -467,22 +467,6 @@ def foo(bar) ) end - if RUBY_VERSION < '3.2.' - excludes.concat( - %w[ - test/corpus/literal/since/32.rb - ] - ) - end - - if RUBY_VERSION < '3.1.' - excludes.concat( - %w[ - test/corpus/literal/since/31.rb - ] - ) - end - excludes.flat_map { |file| ['--ignore', file] } end diff --git a/unparser.gemspec b/unparser.gemspec index 7c0ac193..35342ea6 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -23,7 +23,7 @@ Gem::Specification.new do |gem| gem.extra_rdoc_files = %w[README.md] gem.executables = %w[unparser] - gem.required_ruby_version = '>= 3.1' + gem.required_ruby_version = '>= 3.3' gem.add_dependency('diff-lcs', '~> 1.6') gem.add_dependency('parser', '>= 3.3.0') From e05b0766a57dd1651262f450eb240a22445a4a9a Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 3 Nov 2025 01:39:29 +0000 Subject: [PATCH 2/2] Change to not use native parser backend anymore --- README.md | 6 ++-- bin/parser-round-trip-test | 7 ++--- lib/unparser.rb | 63 ++++++++++++++++---------------------- spec/spec_helper.rb | 2 -- spec/unit/unparser_spec.rb | 14 ++++----- 5 files changed, 38 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 6082a077..bb8e3bad 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ unparser ![CI](https://github.com/mbj/unparser/workflows/CI/badge.svg) [![Gem Version](https://img.shields.io/gem/v/unparser.svg)](https://rubygems.org/gems/unparser) -Generate equivalent source for ASTs from [parser](https://github.com/whitequark/parser). +Generate equivalent source for ASTs from [prism](https://github.com/ruby/prism) via its parser translation layer. + +**Parser Backend**: Unparser uses [Prism](https://github.com/ruby/prism) (Ruby's official parser) with its [parser translation layer](https://github.com/ruby/prism/blob/main/docs/parser_translation.md) to produce `Parser::AST::Node` compatible ASTs. This requires both the `prism` and `parser` gems as dependencies. The following constraints apply: @@ -30,7 +32,6 @@ Usage ----- ```ruby -require 'parser/current' require 'unparser' ast = Unparser.parse('your(ruby(code))') @@ -41,7 +42,6 @@ Unparser.unparse(ast) # => 'your(ruby(code))' To preserve the comments from the source: ```ruby -require 'parser/current' require 'unparser' ast, comments = Unparser.parser.parse_with_comments(Unparser.buffer('your(ruby(code)) # with comments')) diff --git a/bin/parser-round-trip-test b/bin/parser-round-trip-test index 7b5f0bc6..9d1e711f 100755 --- a/bin/parser-round-trip-test +++ b/bin/parser-round-trip-test @@ -1,8 +1,5 @@ #!/usr/bin/env ruby # frozen_string_literal: true -if Gem::Version.new(RUBY_VERSION) <= '3.4' - load 'bin/parser-whitequark-round-trip-test' -else - load 'bin/parser-prism-round-trip-test' -end +# Unparser now uses Prism translation layer for all Ruby 3.3+ versions +load 'bin/parser-prism-round-trip-test' diff --git a/lib/unparser.rb b/lib/unparser.rb index 5616f888..63fec7ec 100644 --- a/lib/unparser.rb +++ b/lib/unparser.rb @@ -18,52 +18,41 @@ # Library namespace module Unparser # rubocop:disable Metrics/ModuleLength - # Unparser specific AST builder defaulting to modern AST format - if Gem::Version.new(RUBY_VERSION) <= '3.4' - require 'parser/current' - class Builder < Parser::Builders::Default - modernize - - # mutant:disable - def initialize - super + require 'prism' - self.emit_file_line_as_literals = false - end - end - else - require 'prism' - class Builder < Prism::Translation::Parser::Builder - modernize + # Unparser specific AST builder defaulting to modern AST format + class Builder < Prism::Translation::Parser::Builder + modernize - # mutant:disable - def initialize - super + # mutant:disable + def initialize + super - self.emit_file_line_as_literals = false - end + self.emit_file_line_as_literals = false end end + # Select appropriate Prism translation parser based on Ruby version PARSER_CLASS = - if Gem::Version.new(RUBY_VERSION) <= '3.4' - Class.new(Parser::CurrentRuby) do - def declare_local_variable(local_variable) - static_env.declare(local_variable) - end - end + if RUBY_VERSION >= '3.5' + Prism::Translation::Parser35 + elsif RUBY_VERSION >= '3.4' + Prism::Translation::Parser34 else - Class.new(Prism::Translation::Parser34) do - def declare_local_variable(local_variable) - (@local_variables ||= Set.new) << local_variable - end - - def prism_options - super.merge(scopes: [@local_variables.to_a]) - end - end + Prism::Translation::Parser33 + end + + # Create parser instance with local variable declaration support + PARSER_INSTANCE_CLASS = Class.new(PARSER_CLASS) do + def declare_local_variable(local_variable) + (@local_variables ||= Set.new) << local_variable end + def prism_options + super.merge(scopes: [@local_variables.to_a]) + end + end + EMPTY_STRING = ''.freeze EMPTY_ARRAY = [].freeze @@ -238,7 +227,7 @@ def self.parse_ast(source, static_local_variables: Set.new) # @api private # mutant:disable def self.parser - PARSER_CLASS.new(Builder.new).tap do |parser| + PARSER_INSTANCE_CLASS.new(Builder.new).tap do |parser| parser.diagnostics.tap do |diagnostics| diagnostics.all_errors_are_fatal = true end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index fd8f4ef5..38df27e5 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -4,8 +4,6 @@ require 'mutant' require 'yaml' -require 'parser/current' - RSpec.shared_examples_for 'a command method' do it 'returns self' do should equal(object) diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index bf84f584..3bf9576c 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -459,13 +459,13 @@ def foo(bar) let(:version_excludes) do excludes = [] - if RUBY_VERSION >= '3.4.' - excludes.concat( - %w[ - test/corpus/literal/before/34.rb - ] - ) - end + # "before/34.rb" contains syntax valid before Ruby 3.4 but invalid in 3.4+ + # So we need to exclude it on ALL versions (3.3 can't parse it, 3.4+ rejects it) + excludes.concat( + %w[ + test/corpus/literal/before/34.rb + ] + ) excludes.flat_map { |file| ['--ignore', file] } end