Skip to content

Commit a4fcd53

Browse files
committed
Accept all 3.3.x and 3.4.x Ruby versions for Prism.parse
1 parent 8197be8 commit a4fcd53

File tree

4 files changed

+84
-34
lines changed

4 files changed

+84
-34
lines changed

ext/prism/extension.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -770,8 +770,10 @@ parse_input(pm_string_t *input, const pm_options_t *options) {
770770
* * `version` - the version of Ruby syntax that prism should used to parse Ruby
771771
* code. By default prism assumes you want to parse with the latest version
772772
* of Ruby syntax (which you can trigger with `nil` or `"latest"`). You
773-
* may also restrict the syntax to a specific version of Ruby. The
774-
* supported values are `"3.3.0"` and `"3.4.0"`.
773+
* may also restrict the syntax to a specific version of Ruby, e.g., with `"3.3.0"`.
774+
* To parse with the same syntax version that the current Ruby is running
775+
* use `version: RUBY_VERSION`. Raises ArgumentError if the version is not
776+
* currently supported by Prism.
775777
*/
776778
static VALUE
777779
parse(int argc, VALUE *argv, VALUE self) {

lib/prism/ffi.rb

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,20 @@ def dump_options_command_line(options)
411411
end
412412
end
413413

414+
# Return the value that should be dumped for the version option.
415+
def dump_options_version(version)
416+
case version
417+
when nil, "latest"
418+
0
419+
when /\A3\.3\.\d+\z/
420+
1
421+
when /\A3\.4\.\d+\z/
422+
0
423+
else
424+
raise ArgumentError, "invalid version: #{version}"
425+
end
426+
end
427+
414428
# Convert the given options into a serialized options string.
415429
def dump_options(options)
416430
template = +""
@@ -443,7 +457,7 @@ def dump_options(options)
443457
values << dump_options_command_line(options)
444458

445459
template << "C"
446-
values << { nil => 0, "3.3.0" => 1, "3.3.1" => 1, "3.4.0" => 0, "latest" => 0 }.fetch(options[:version])
460+
values << dump_options_version(options[:version])
447461

448462
template << "C"
449463
values << (options[:encoding] == false ? 1 : 0)

src/options.c

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "prism/options.h"
2+
#include "prism/util/pm_char.h"
23

34
/**
45
* Set the shebang callback option on the given options struct.
@@ -57,47 +58,42 @@ pm_options_command_line_set(pm_options_t *options, uint8_t command_line) {
5758
options->command_line = command_line;
5859
}
5960

61+
static bool is_number(const char *string, size_t length) {
62+
return pm_strspn_decimal_digit((const uint8_t *) string, (ptrdiff_t) length) == length;
63+
}
64+
6065
/**
6166
* Set the version option on the given options struct by parsing the given
6267
* string. If the string contains an invalid option, this returns false.
6368
* Otherwise, it returns true.
6469
*/
6570
PRISM_EXPORTED_FUNCTION bool
6671
pm_options_version_set(pm_options_t *options, const char *version, size_t length) {
67-
switch (length) {
68-
case 0:
69-
if (version == NULL) {
70-
options->version = PM_OPTIONS_VERSION_LATEST;
71-
return true;
72-
}
73-
74-
return false;
75-
case 5:
76-
assert(version != NULL);
77-
78-
if ((strncmp(version, "3.3.0", length) == 0) || (strncmp(version, "3.3.1", length) == 0)) {
79-
options->version = PM_OPTIONS_VERSION_CRUBY_3_3;
80-
return true;
81-
}
82-
83-
if (strncmp(version, "3.4.0", length) == 0) {
84-
options->version = PM_OPTIONS_VERSION_LATEST;
85-
return true;
86-
}
72+
if (version == NULL) {
73+
options->version = PM_OPTIONS_VERSION_LATEST;
74+
return true;
75+
}
8776

88-
return false;
89-
case 6:
90-
assert(version != NULL);
77+
if (length >= 4) {
78+
if (strncmp(version, "3.3.", 4) == 0 && is_number(version + 4, length - 4)) {
79+
options->version = PM_OPTIONS_VERSION_CRUBY_3_3;
80+
return true;
81+
}
9182

92-
if (strncmp(version, "latest", length) == 0) {
93-
options->version = PM_OPTIONS_VERSION_LATEST;
94-
return true;
95-
}
83+
if (strncmp(version, "3.4.", 4) == 0 && is_number(version + 4, length - 4)) {
84+
options->version = PM_OPTIONS_VERSION_LATEST;
85+
return true;
86+
}
87+
}
9688

97-
return false;
98-
default:
99-
return false;
89+
if (length >= 6) {
90+
if (strncmp(version, "latest", 7) == 0) { // 7 to compare the \0 as well
91+
options->version = PM_OPTIONS_VERSION_LATEST;
92+
return true;
93+
}
10094
}
95+
96+
return false;
10197
}
10298

10399
/**

test/prism/version_test.rb

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,46 @@
44

55
module Prism
66
class VersionTest < TestCase
7-
def test_version_is_set
7+
def test_prism_version_is_set
88
refute_nil VERSION
99
end
10+
11+
def test_syntax_versions
12+
assert Prism.parse("1 + 1", version: "3.3.0").success?
13+
assert Prism.parse("1 + 1", version: "3.3.1").success?
14+
assert Prism.parse("1 + 1", version: "3.3.9").success?
15+
assert Prism.parse("1 + 1", version: "3.3.10").success?
16+
17+
assert Prism.parse("1 + 1", version: "3.4.0").success?
18+
assert Prism.parse("1 + 1", version: "3.4.9").success?
19+
assert Prism.parse("1 + 1", version: "3.4.10").success?
20+
21+
assert Prism.parse("1 + 1", version: "latest").success?
22+
23+
# Test edge case
24+
error = assert_raise ArgumentError do
25+
Prism.parse("1 + 1", version: "latest2")
26+
end
27+
assert_equal "invalid version: latest2", error.message
28+
29+
assert_raise ArgumentError do
30+
Prism.parse("1 + 1", version: "3.3.a")
31+
end
32+
33+
# Not supported version syntax
34+
assert_raise ArgumentError do
35+
Prism.parse("1 + 1", version: "3.3")
36+
end
37+
38+
# Not supported version (too old)
39+
assert_raise ArgumentError do
40+
Prism.parse("1 + 1", version: "3.2.0")
41+
end
42+
43+
# Not supported version (too new)
44+
assert_raise ArgumentError do
45+
Prism.parse("1 + 1", version: "3.5.0")
46+
end
47+
end
1048
end
1149
end

0 commit comments

Comments
 (0)