Skip to content

Commit ff9d363

Browse files
authored
Merge branch 'main' into topic/javawasm_changes
2 parents ed09e78 + fcd88e7 commit ff9d363

101 files changed

Lines changed: 3503 additions & 2780 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/main.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ jobs:
7979
shell: bash
8080

8181
build-ibm:
82-
if: github.repository == 'ruby/prism'
82+
if: github.repository == 'ruby/prism' && false
8383
strategy:
8484
fail-fast: false
8585
matrix:
@@ -105,7 +105,7 @@ jobs:
105105
matrix:
106106
target:
107107
- { ruby: "head", gemfile: "4.1" }
108-
- { ruby: "jruby-10.0.0.0", gemfile: ".." } # https://github.com/jruby/jruby/issues/8923
108+
- { ruby: "jruby", gemfile: ".." }
109109
- { ruby: "truffleruby", gemfile: ".." }
110110
runs-on: ubuntu-latest
111111
env:
@@ -277,7 +277,7 @@ jobs:
277277
- { ruby: "3.4", os: "ubuntu-latest", gemfile: "3.4" }
278278
- { ruby: "4.0", os: "ubuntu-latest", gemfile: "4.0" }
279279
- { ruby: "head", os: "ubuntu-latest", gemfile: "4.1" }
280-
- { ruby: "jruby-10.0.0.0", os: "ubuntu-latest", gemfile: ".." } # https://github.com/jruby/jruby/issues/8923
280+
- { ruby: "jruby", os: "ubuntu-latest", gemfile: ".." }
281281
- { ruby: "truffleruby", os: "ubuntu-latest", gemfile: ".." }
282282

283283
- { ruby: "2.7", os: "macos-latest", gemfile: "2.7" }
@@ -288,7 +288,7 @@ jobs:
288288
- { ruby: "3.4", os: "macos-latest", gemfile: "3.4" }
289289
- { ruby: "4.0", os: "macos-latest", gemfile: "4.0" }
290290
- { ruby: "head", os: "macos-latest", gemfile: "4.1" }
291-
- { ruby: "jruby-10.0.0.0", os: "macos-latest", gemfile: ".." } # https://github.com/jruby/jruby/issues/8923
291+
- { ruby: "jruby", os: "macos-latest", gemfile: ".." }
292292
- { ruby: "truffleruby", os: "macos-latest", gemfile: ".." }
293293

294294
- { ruby: "2.7", os: "windows-latest", gemfile: "2.7" }
@@ -298,8 +298,8 @@ jobs:
298298
- { ruby: "3.3", os: "windows-latest", gemfile: "3.3" }
299299
- { ruby: "3.4", os: "windows-latest", gemfile: "3.4" }
300300
- { ruby: "4.0", os: "windows-latest", gemfile: "4.0" }
301-
# - { ruby: "head", os: "windows-latest", gemfile: "4.1" } TODO: No windows build yet
302-
- { ruby: "jruby-10.0.0.0", os: "windows-latest", gemfile: ".." } # https://github.com/jruby/jruby/issues/8923
301+
- { ruby: "head", os: "windows-latest", gemfile: "4.1" }
302+
- { ruby: "jruby", os: "windows-latest", gemfile: ".." }
303303
env:
304304
BUNDLE_GEMFILE: gemfiles/${{ matrix.target.gemfile }}/Gemfile
305305
runs-on: ${{ matrix.target.os }}

CHANGELOG.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
66

7+
## [Unreleased]
8+
9+
## [1.9.0] - 2026-01-27
10+
11+
### Added
12+
13+
- Lots of work on the Ripper translation layer to make it more compatible and efficient.
14+
- Alias `Prism::Node#breadth_first_search` to `Prism::Node#find`.
15+
- Add `Prism::Node#breadth_first_search_all`/`Prism::Node#find_all` for finding all nodes matching a condition.
16+
17+
### Changed
18+
19+
- Fixed location of opening tokens when invalid syntax is parsed.
20+
- Fix RBI for parsing options.
21+
722
## [1.8.0] - 2026-01-12
823

924
### Added
@@ -19,8 +34,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
1934
- Decouple ripper translator from ripper library.
2035
- Sync Prism::Translation::ParserCurrent with Ruby 4.0.
2136

22-
## [Unreleased]
23-
2437
## [1.7.0] - 2025-12-18
2538

2639
### Added
@@ -731,7 +744,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
731744

732745
- 🎉 Initial release! 🎉
733746

734-
[unreleased]: https://github.com/ruby/prism/compare/v1.8.0...HEAD
747+
[unreleased]: https://github.com/ruby/prism/compare/v1.9.0...HEAD
748+
[1.9.0]: https://github.com/ruby/prism/compare/v1.8.0...v1.9.0
735749
[1.8.0]: https://github.com/ruby/prism/compare/v1.7.0...v1.8.0
736750
[1.7.0]: https://github.com/ruby/prism/compare/v1.6.0...v1.7.0
737751
[1.6.0]: https://github.com/ruby/prism/compare/v1.5.2...v1.6.0

Gemfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ gem "rake-compiler"
1111
gem "ruby_parser"
1212
gem "test-unit"
1313

14-
platforms :mri, :mswin, :mingw, :x64_mingw do
14+
platforms :mri, :windows do
1515
gem "ffi"
1616
gem "irb"
1717
gem "ruby_memcheck"

Gemfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
prism (1.8.0)
4+
prism (1.9.0)
55

66
GEM
77
remote: https://rubygems.org/
@@ -82,4 +82,4 @@ DEPENDENCIES
8282
test-unit
8383

8484
BUNDLED WITH
85-
2.5.16
85+
4.0.4

Steepfile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ target :lib do
1010
# TODO: Type-checking these files is still WIP
1111
ignore "lib/prism/desugar_compiler.rb"
1212
ignore "lib/prism/lex_compat.rb"
13-
ignore "lib/prism/lex_ripper.rb"
1413
ignore "lib/prism/serialize.rb"
1514
ignore "lib/prism/ffi.rb"
1615
ignore "lib/prism/translation"

bin/prism

Lines changed: 9 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ module Prism
3030
bin/prism console
3131
bin/prism dot [source]
3232
bin/prism encoding [encoding]
33-
bin/prism error [name] [source]
33+
bin/prism error [source]
3434
bin/prism lex [source]
3535
bin/prism lex_compat [source]
3636
bin/prism locals [source]
@@ -161,36 +161,17 @@ module Prism
161161
unicode_lists(found) if found == Encoding::UTF_8 || found == Encoding::UTF8_MAC
162162
end
163163

164-
# bin/prism error [name] [source]
164+
# bin/prism error [source]
165165
def error(argv)
166-
name = argv.shift
167-
168-
source = nil
169-
filepath = File.expand_path("../test/prism/errors/#{name}.txt", __dir__)
166+
source, filepath = read_source(argv)
167+
result = Prism.parse(source)
170168

171-
if argv.empty?
172-
raise "Expected #{filepath} to exist" unless File.file?(filepath)
173169

174-
source = File.read(filepath, binmode: true, external_encoding: Encoding::UTF_8)
175-
source = source.lines.grep_v(/^\s*\^/).join.gsub(/\n*\z/, "")
170+
if result.errors.any?
171+
puts result.errors_format
176172
else
177-
if File.file?(filepath)
178-
counter = 1
179-
180-
begin
181-
current = "#{File.dirname(filepath)}/#{File.basename(filepath, ".txt")}_#{counter += 1}.txt"
182-
end while File.file?(current)
183-
184-
filepath = current
185-
end
186-
187-
source, _ = read_source(argv)
173+
puts "No syntax errors for #{filepath}"
188174
end
189-
190-
result = Prism.parse(source)
191-
raise "Expected #{source.inspect} to have errors" if result.success?
192-
193-
File.write(filepath, result.errors_format)
194175
end
195176

196177
# bin/prism lex [source]
@@ -210,18 +191,10 @@ module Prism
210191

211192
# bin/prism lex_compat [source]
212193
def lex_compat(argv)
194+
require "ripper"
213195
source, filepath = read_source(argv)
214196

215-
ripper_value =
216-
begin
217-
Prism.lex_ripper(source)
218-
rescue ArgumentError, SyntaxError
219-
# If Ripper raises a syntax error, we want to continue as if it didn't
220-
# return any tokens at all. prism won't raise a syntax error, so it's
221-
# nicer to still be able to see the tokens that prism generated.
222-
[]
223-
end
224-
197+
ripper_value = Ripper.lex(source)
225198
prism_compat = Prism.lex_compat(source, filepath: filepath)
226199
prism = Prism.lex(source, filepath: filepath)
227200

config.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -653,10 +653,6 @@ tokens:
653653
comment: "a separator between words in a list"
654654
- name: __END__
655655
comment: "marker for the point in the file at which the parser should stop"
656-
- name: MISSING
657-
comment: "a token that was expected but not found"
658-
- name: NOT_PROVIDED
659-
comment: "a token that was not present but it is okay"
660656
flags:
661657
- name: ArgumentsNodeFlags
662658
values:

docs/configuration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ The available values for `type` are:
6161
* `constant` - A field that is an integer that represents an index in the constant pool. This is a `pm_constant_id_t` in C.
6262
* `constant[]` - A field that is an array of constants. This is a `pm_constant_id_list_t` in C.
6363
* `location` - A field that is a location. This is a `pm_location_t` in C.
64-
* `location?` - A field that is a location that is optionally present. This is a `pm_location_t` in C, but if the value is not present then the `start` and `end` fields will be `NULL`.
64+
* `location?` - A field that is a location that is optionally present. This is a `pm_location_t` in C, but if the value is not present then the `length` field will be `0`.
6565
* `uint8` - A field that is an 8-bit unsigned integer. This is a `uint8_t` in C.
6666
* `uint32` - A field that is a 32-bit unsigned integer. This is a `uint32_t` in C.
6767

ext/prism/extension.c

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -201,9 +201,22 @@ build_options_i(VALUE key, VALUE value, VALUE argument) {
201201
const char *version = check_string(value);
202202

203203
if (RSTRING_LEN(value) == 7 && strncmp(version, "current", 7) == 0) {
204-
const char *current_version = RSTRING_PTR(rb_const_get(rb_cObject, rb_intern("RUBY_VERSION")));
205-
if (!pm_options_version_set(options, current_version, 3)) {
206-
rb_exc_raise(rb_exc_new_cstr(rb_cPrismCurrentVersionError, current_version));
204+
if (!pm_options_version_set(options, ruby_version, 3)) {
205+
rb_exc_raise(rb_exc_new_cstr(rb_cPrismCurrentVersionError, ruby_version));
206+
}
207+
} else if (RSTRING_LEN(value) == 7 && strncmp(version, "nearest", 7) == 0) {
208+
const char *nearest_version;
209+
210+
if (ruby_version[0] < '3' || (ruby_version[0] == '3' && ruby_version[2] < '3')) {
211+
nearest_version = "3.3";
212+
} else if (ruby_version[0] > '4' || (ruby_version[0] == '4' && ruby_version[2] > '1')) {
213+
nearest_version = "4.1";
214+
} else {
215+
nearest_version = ruby_version;
216+
}
217+
218+
if (!pm_options_version_set(options, nearest_version, 3)) {
219+
rb_raise(rb_eArgError, "invalid nearest version: %s", nearest_version);
207220
}
208221
} else if (!pm_options_version_set(options, version, RSTRING_LEN(value))) {
209222
rb_raise(rb_eArgError, "invalid version: %" PRIsVALUE, value);
@@ -455,23 +468,23 @@ rb_class_new_instance_freeze(int argc, const VALUE *argv, VALUE klass, bool free
455468
* Create a new Location instance from the given parser and bounds.
456469
*/
457470
static inline VALUE
458-
parser_location(const pm_parser_t *parser, VALUE source, bool freeze, const uint8_t *start, size_t length) {
459-
VALUE argv[] = { source, LONG2FIX(start - parser->start), LONG2FIX(length) };
471+
parser_location(VALUE source, bool freeze, uint32_t start, uint32_t length) {
472+
VALUE argv[] = { source, LONG2FIX(start), LONG2FIX(length) };
460473
return rb_class_new_instance_freeze(3, argv, rb_cPrismLocation, freeze);
461474
}
462475

463476
/**
464477
* Create a new Location instance from the given parser and location.
465478
*/
466-
#define PARSER_LOCATION_LOC(parser, source, freeze, loc) \
467-
parser_location(parser, source, freeze, loc.start, (size_t) (loc.end - loc.start))
479+
#define PARSER_LOCATION(source, freeze, location) \
480+
parser_location(source, freeze, location.start, location.length)
468481

469482
/**
470483
* Build a new Comment instance from the given parser and comment.
471484
*/
472485
static inline VALUE
473-
parser_comment(const pm_parser_t *parser, VALUE source, bool freeze, const pm_comment_t *comment) {
474-
VALUE argv[] = { PARSER_LOCATION_LOC(parser, source, freeze, comment->location) };
486+
parser_comment(VALUE source, bool freeze, const pm_comment_t *comment) {
487+
VALUE argv[] = { PARSER_LOCATION(source, freeze, comment->location) };
475488
VALUE type = (comment->type == PM_COMMENT_EMBDOC) ? rb_cPrismEmbDocComment : rb_cPrismInlineComment;
476489
return rb_class_new_instance_freeze(1, argv, type, freeze);
477490
}
@@ -488,7 +501,7 @@ parser_comments(const pm_parser_t *parser, VALUE source, bool freeze) {
488501
comment != NULL;
489502
comment = (const pm_comment_t *) comment->node.next
490503
) {
491-
VALUE value = parser_comment(parser, source, freeze, comment);
504+
VALUE value = parser_comment(source, freeze, comment);
492505
rb_ary_push(comments, value);
493506
}
494507

@@ -500,9 +513,9 @@ parser_comments(const pm_parser_t *parser, VALUE source, bool freeze) {
500513
* Build a new MagicComment instance from the given parser and magic comment.
501514
*/
502515
static inline VALUE
503-
parser_magic_comment(const pm_parser_t *parser, VALUE source, bool freeze, const pm_magic_comment_t *magic_comment) {
504-
VALUE key_loc = parser_location(parser, source, freeze, magic_comment->key_start, magic_comment->key_length);
505-
VALUE value_loc = parser_location(parser, source, freeze, magic_comment->value_start, magic_comment->value_length);
516+
parser_magic_comment(VALUE source, bool freeze, const pm_magic_comment_t *magic_comment) {
517+
VALUE key_loc = parser_location(source, freeze, magic_comment->key.start, magic_comment->key.length);
518+
VALUE value_loc = parser_location(source, freeze, magic_comment->value.start, magic_comment->value.length);
506519
VALUE argv[] = { key_loc, value_loc };
507520
return rb_class_new_instance_freeze(2, argv, rb_cPrismMagicComment, freeze);
508521
}
@@ -519,7 +532,7 @@ parser_magic_comments(const pm_parser_t *parser, VALUE source, bool freeze) {
519532
magic_comment != NULL;
520533
magic_comment = (const pm_magic_comment_t *) magic_comment->node.next
521534
) {
522-
VALUE value = parser_magic_comment(parser, source, freeze, magic_comment);
535+
VALUE value = parser_magic_comment(source, freeze, magic_comment);
523536
rb_ary_push(magic_comments, value);
524537
}
525538

@@ -533,10 +546,10 @@ parser_magic_comments(const pm_parser_t *parser, VALUE source, bool freeze) {
533546
*/
534547
static VALUE
535548
parser_data_loc(const pm_parser_t *parser, VALUE source, bool freeze) {
536-
if (parser->data_loc.end == NULL) {
549+
if (parser->data_loc.length == 0) {
537550
return Qnil;
538551
} else {
539-
return PARSER_LOCATION_LOC(parser, source, freeze, parser->data_loc);
552+
return parser_location(source, freeze, parser->data_loc.start, parser->data_loc.length);
540553
}
541554
}
542555

@@ -554,7 +567,7 @@ parser_errors(const pm_parser_t *parser, rb_encoding *encoding, VALUE source, bo
554567
) {
555568
VALUE type = ID2SYM(rb_intern(pm_diagnostic_id_human(error->diag_id)));
556569
VALUE message = rb_obj_freeze(rb_enc_str_new_cstr(error->message, encoding));
557-
VALUE location = PARSER_LOCATION_LOC(parser, source, freeze, error->location);
570+
VALUE location = PARSER_LOCATION(source, freeze, error->location);
558571

559572
VALUE level = Qnil;
560573
switch (error->level) {
@@ -594,7 +607,7 @@ parser_warnings(const pm_parser_t *parser, rb_encoding *encoding, VALUE source,
594607
) {
595608
VALUE type = ID2SYM(rb_intern(pm_diagnostic_id_human(warning->diag_id)));
596609
VALUE message = rb_obj_freeze(rb_enc_str_new_cstr(warning->message, encoding));
597-
VALUE location = PARSER_LOCATION_LOC(parser, source, freeze, warning->location);
610+
VALUE location = PARSER_LOCATION(source, freeze, warning->location);
598611

599612
VALUE level = Qnil;
600613
switch (warning->level) {
@@ -894,8 +907,10 @@ parse_input(pm_string_t *input, const pm_options_t *options) {
894907
* version of Ruby syntax (which you can trigger with `nil` or
895908
* `"latest"`). You may also restrict the syntax to a specific version of
896909
* Ruby, e.g., with `"3.3.0"`. To parse with the same syntax version that
897-
* the current Ruby is running use `version: "current"`. Raises
898-
* ArgumentError if the version is not currently supported by Prism.
910+
* the current Ruby is running use `version: "current"`. To parse with the
911+
* nearest version to the current Ruby that is running, use
912+
* `version: "nearest"`. Raises ArgumentError if the version is not
913+
* currently supported by Prism.
899914
*/
900915
static VALUE
901916
parse(int argc, VALUE *argv, VALUE self) {

ext/prism/extension.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
#ifndef PRISM_EXT_NODE_H
22
#define PRISM_EXT_NODE_H
33

4-
#define EXPECTED_PRISM_VERSION "1.8.0"
4+
#define EXPECTED_PRISM_VERSION "1.9.0"
55

66
#include <ruby.h>
77
#include <ruby/encoding.h>
8+
#include <ruby/version.h>
89
#include "prism.h"
910

1011
VALUE pm_source_new(const pm_parser_t *parser, rb_encoding *encoding, bool freeze);

0 commit comments

Comments
 (0)