Module: Prism
Overview
The Prism Ruby parser.
“Parsing Ruby is suddenly manageable!”
- You, hopefully
Constant Summary
-
BACKEND =
# File 'lib/prism.rb', line 137
The FFI backend is used on other Ruby implementations.
:FFI
-
VERSION =
# File 'lib/prism/ffi.rb', line 256
The version constant is set by reading the result of calling pm_version.
LibRubyParser.pm_version.read_string.freeze
Class Method Summary
-
.dump(source, **options) ⇒ String
Dump the AST corresponding to the given string to a string. For supported options, see .parse.
-
.dump_file(filepath, **options) ⇒ String
Dump the AST corresponding to the given file to a string. For supported options, see .parse.
-
.find(callable, rubyvm: !!defined?(RubyVM)))
Given a Method, UnboundMethod, Proc, or
Thread::Backtrace::Location, returns thePrismnode representing it. - .lex(source, **options) ⇒ LexResult
-
.lex_compat(source, **options) ⇒ LexCompat::Result
Returns a parse result whose value is an array of tokens that closely resembles the return value of
Ripper.lex. - .lex_file(filepath, **options) ⇒ LexResult
-
.load(source, serialized, freeze) ⇒ ParseResult
Load the serialized AST using the source as a reference into a tree.
-
.parse(source, **options) ⇒ ParseResult
Parse the given string and return a
ParseResultinstance. The options that are supported are: - .parse_comments(source, **options) ⇒ Array
-
.parse_failure?(source, **options) ⇒ Boolean
Parse the given string and return true if it parses with errors. For supported options, see .parse.
-
.parse_file(filepath, **options)
Mirror the
parse_fileAPI by using the serialization API. -
.parse_file_comments(filepath, **options)
Mirror the
parse_file_commentsAPI by using the serialization API. -
.parse_file_failure?(filepath, **options) ⇒ Boolean
Parse the given file and return true if it parses with errors. For supported options, see .parse.
-
.parse_file_success?(filepath, **options) ⇒ Boolean
Parse the given file and return true if it parses without errors. For supported options, see .parse.
-
.parse_lex(source, **options) ⇒ ParseLexResult
Parse the given string and return a
ParseLexResultinstance that contains a 2-element array, where the first element is the AST and the second element is an array ofTokeninstances. -
.parse_lex_file(filepath, **options) ⇒ ParseLexResult
Parse the given file and return a
ParseLexResultinstance that contains a 2-element array, where the first element is the AST and the second element is an array ofTokeninstances. -
.parse_stream(stream, **options) ⇒ ParseResult
Parse the given object that responds to
getsand return aParseResultinstance. The options that are supported are the same as .parse. -
.parse_success?(source, **options) ⇒ Boolean
Parse the given string and return true if it parses without errors. For supported options, see .parse.
-
.profile(source, **options) ⇒ nil
Parse the given string and return nothing. This method is meant to allow profilers to avoid the overhead of reifying the AST to Ruby. For supported options, see .parse.
-
.profile_file(filepath, **options) ⇒ nil
Parse the given file and return nothing. This method is meant to allow profilers to avoid the overhead of reifying the AST to Ruby. For supported options, see .parse.
-
.scope(locals: [], forwarding: [])
Create a new scope with the given locals and forwarding options that is suitable for passing into one of the
Prism.*methods that accepts thescopesoption. -
.dump_options(options)
private
Convert the given options into a serialized options string.
-
.dump_options_command_line(options)
private
Return the value that should be dumped for the command_line option.
-
.dump_options_version(version)
private
Return the value that should be dumped for the version option.
-
.version_string_to_number(version)
private
Converts a version string like “4.0.0” or “4.0” into a number.
- .dump_common(string, options) private Internal use only
- .lex_common(string, code, options) private Internal use only
- .parse_comments_common(string, code, options) private Internal use only
- .parse_common(string, code, options) private Internal use only
- .parse_file_success_common(string, options) private Internal use only
- .parse_lex_common(string, code, options) private Internal use only
Class Method Details
.dump(source, **options) ⇒ String
Dump the AST corresponding to the given string to a string. For supported options, see .parse.
# File 'prism/extension.c', line 399
static VALUE
dump(int argc, VALUE *argv, VALUE self) {
pm_options_t *options = pm_options_new();
VALUE string = string_options(argc, argv, options);
const uint8_t *source = (const uint8_t *) RSTRING_PTR(string);
size_t length = RSTRING_LEN(string);
#ifdef PRISM_BUILD_DEBUG
char* dup = xmalloc(length);
memcpy(dup, source, length);
source = (const uint8_t *) dup;
#endif
VALUE value = dump_input(source, length, options);
if (pm_options_freeze(options)) rb_obj_freeze(value);
#ifdef PRISM_BUILD_DEBUG
#ifdef xfree_sized
xfree_sized(dup, length);
#else
xfree(dup);
#endif
#endif
pm_options_free(options);
return value;
}
.dump_common(string, options) (private)
# File 'lib/prism/ffi.rb', line 387
def dump_common(string, ) # :nodoc: LibRubyParser::PrismBuffer.with do |buffer| LibRubyParser.pm_serialize_parse(buffer.pointer, string.pointer, string.length, ()) dumped = buffer.read dumped.freeze if .fetch(:freeze, false) dumped end end
.dump_file(filepath, **options) ⇒ String
Dump the AST corresponding to the given file to a string. For supported options, see .parse.
# File 'prism/extension.c', line 437
static VALUE
dump_file(int argc, VALUE *argv, VALUE self) {
pm_options_t *options = pm_options_new();
VALUE encoded_filepath;
pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
VALUE value = dump_input(pm_source_source(src), pm_source_length(src), options);
pm_source_free(src);
pm_options_free(options);
return value;
}
.dump_options(options) (private)
Convert the given options into a serialized options string.
# File 'lib/prism/ffi.rb', line 482
def () template = +"" values = [] template << "L" if (filepath = [:filepath]) values.push(filepath.bytesize, filepath.b) template << "A*" else values << 0 end template << "l" values << .fetch(:line, 1) template << "L" if (encoding = [:encoding]) name = encoding.is_a?(Encoding) ? encoding.name : encoding values.push(name.bytesize, name.b) template << "A*" else values << 0 end template << "C" values << (.fetch(:frozen_string_literal, false) ? 1 : 0) template << "C" values << () template << "C" values << ([:version]) template << "C" values << ([:encoding] == false ? 1 : 0) template << "C" values << (.fetch(:main_script, false) ? 1 : 0) template << "C" values << (.fetch(:partial_script, false) ? 1 : 0) template << "C" values << (.fetch(:freeze, false) ? 1 : 0) template << "L" if (scopes = [:scopes]) values << scopes.length scopes.each do |scope| locals = nil forwarding = 0 case scope when Array locals = scope when Scope locals = scope.locals scope.forwarding.each do |forward| case forward when :* then forwarding |= 0x1 when :** then forwarding |= 0x2 when :& then forwarding |= 0x4 when :"..." then forwarding |= 0x8 else raise ArgumentError, "invalid forwarding value: #{forward}" end end else raise TypeError, "wrong argument type #{scope.class.inspect} (expected Array or Prism::Scope)" end template << "L" values << locals.length template << "C" values << forwarding locals.each do |local| name = local.name template << "L" values << name.bytesize template << "A*" values << name.b end end else values << 0 end values.pack(template) end
.dump_options_command_line(options) (private)
Return the value that should be dumped for the command_line option.
# File 'lib/prism/ffi.rb', line 429
def () command_line = .fetch(:command_line, "") raise ArgumentError, "command_line must be a string" unless command_line.is_a?(String) command_line.each_char.inject(0) do |value, char| case char when "a" then value | 0b000001 when "e" then value | 0b000010 when "l" then value | 0b000100 when "n" then value | 0b001000 when "p" then value | 0b010000 when "x" then value | 0b100000 else raise ArgumentError, "invalid command_line option: #{char}" end end end
.dump_options_version(version) (private)
Return the value that should be dumped for the version option.
# File 'lib/prism/ffi.rb', line 447
def (version) case version when "current" version_string_to_number(RUBY_VERSION) || raise(CurrentVersionError, RUBY_VERSION) when "latest", nil 0 # Handled in pm_parser_init when "nearest" dump = version_string_to_number(RUBY_VERSION) return dump if dump if RUBY_VERSION < "3.3" version_string_to_number("3.3") else 0 # Handled in pm_parser_init end else version_string_to_number(version) || raise(ArgumentError, "invalid version: #{version}") end end
.find(callable, rubyvm: !!defined?(RubyVM)))
Given a Method, UnboundMethod, Proc, or Thread::Backtrace::Location, returns the Prism node representing it. On CRuby, this uses node_id for an exact match. On other implementations, it falls back to best-effort matching by source location line number.
.lex(source, **options) ⇒ LexResult
Return a ::Prism::LexResult instance that contains an array of ::Prism::Token instances
corresponding to the given string. For supported options, see .parse.
# File 'prism/extension.c', line 847
static VALUE
lex(int argc, VALUE *argv, VALUE self) {
pm_options_t *options = pm_options_new();
VALUE string = string_options(argc, argv, options);
VALUE result = parse_lex_input((const uint8_t *) RSTRING_PTR(string), RSTRING_LEN(string), options, false);
pm_options_free(options);
return result;
}
.lex_common(string, code, options) (private)
# File 'lib/prism/ffi.rb', line 398
def lex_common(string, code, ) # :nodoc: LibRubyParser::PrismBuffer.with do |buffer| LibRubyParser.pm_serialize_lex(buffer.pointer, string.pointer, string.length, ()) Serialize.load_lex(code, buffer.read, .fetch(:freeze, false)) end end
.lex_compat(source, **options) ⇒ LexCompat::Result
Returns a parse result whose value is an array of tokens that closely resembles the return value of Ripper.lex.
For supported options, see .parse.
.lex_file(filepath, **options) ⇒ LexResult
Return a ::Prism::LexResult instance that contains an array of ::Prism::Token instances
corresponding to the given file. For supported options, see .parse.
# File 'prism/extension.c', line 866
static VALUE
lex_file(int argc, VALUE *argv, VALUE self) {
pm_options_t *options = pm_options_new();
VALUE encoded_filepath;
pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
VALUE value = parse_lex_input(pm_source_source(src), pm_source_length(src), options, false);
pm_source_free(src);
pm_options_free(options);
return value;
}
.load(source, serialized, freeze) ⇒ ParseResult
Load the serialized AST using the source as a reference into a tree.
# File 'lib/prism.rb', line 84
def self.load(source, serialized, freeze = false) Serialize.load_parse(source, serialized, freeze) end
.parse(source, **options) ⇒ ParseResult
Parse the given string and return a ::Prism::ParseResult instance. The options that
are supported are:
command_line- either nil or a string of the various options that were set on the command line. Valid values are combinations of "a", "l", "n", "p", and "x".encoding- the encoding of the source being parsed. This should be an encoding or nil.filepath- the filepath of the source being parsed. This should be a string or nil.freeze- whether or not to deeply freeze the AST. This should be a boolean or nil.frozen_string_literal- whether or not the frozen string literal pragma has been set. This should be a boolean or nil.line- the line number that the parse starts on. This should be an integer or nil. Note that this is 1-indexed.main_script- a boolean indicating whether or not the source being parsed is the main script being run by the interpreter. This controls whether or not shebangs are parsed for additional flags and whether or not the parser will attempt to find a matching shebang if the first one does not contain the word "ruby".partial_script- when the file being parsed is considered a "partial" script, jumps will not be marked as errors if they are not contained within loops/blocks. This is used in the case that you're parsing a script that you know will be embedded inside another script later, but you do not have that context yet. For example, when parsing an ERB template that will be evaluated inside another script.scopes- the locals that are in scope surrounding the code that is being parsed. This should be an array of arrays of symbols or nil. Scopes are ordered from the outermost scope to the innermost one.version- the version of Ruby syntax that prism should used to parse Ruby code. By default prism assumes you want to parse with the latest version of Ruby syntax (which you can trigger withnilor"latest"). You may also restrict the syntax to a specific version of Ruby, e.g., with"3.3.0". To parse with the same syntax version that the current Ruby is running useversion: "current". To parse with the nearest version to the current Ruby that is running, useversion: "nearest". Raises ArgumentError if the version is not currently supported by Prism.
# File 'prism/extension.c', line 955
static VALUE
parse(int argc, VALUE *argv, VALUE self) {
pm_options_t *options = pm_options_new();
VALUE string = string_options(argc, argv, options);
const uint8_t *source = (const uint8_t *) RSTRING_PTR(string);
size_t length = RSTRING_LEN(string);
#ifdef PRISM_BUILD_DEBUG
char* dup = xmalloc(length);
memcpy(dup, source, length);
source = (const uint8_t *) dup;
#endif
VALUE value = parse_input(source, length, options);
#ifdef PRISM_BUILD_DEBUG
#ifdef xfree_sized
xfree_sized(dup, length);
#else
xfree(dup);
#endif
#endif
pm_options_free(options);
return value;
}
.parse_comments(source, **options) ⇒ Array
Parse the given string and return an array of ::Prism::Comment objects. For supported
options, see .parse.
# File 'prism/extension.c', line 1154
static VALUE
parse_comments(int argc, VALUE *argv, VALUE self) {
pm_options_t *options = pm_options_new();
VALUE string = string_options(argc, argv, options);
VALUE result = parse_input_comments((const uint8_t *) RSTRING_PTR(string), RSTRING_LEN(string), options);
pm_options_free(options);
return result;
}
.parse_comments_common(string, code, options) (private)
# File 'lib/prism/ffi.rb', line 410
def parse_comments_common(string, code, ) # :nodoc: LibRubyParser::PrismBuffer.with do |buffer| LibRubyParser.pm_serialize_parse_comments(buffer.pointer, string.pointer, string.length, ()) Serialize.load_parse_comments(code, buffer.read, .fetch(:freeze, false)) end end
.parse_common(string, code, options) (private)
# File 'lib/prism/ffi.rb', line 405
def parse_common(string, code, ) # :nodoc: serialized = dump_common(string, ) Serialize.load_parse(code, serialized, .fetch(:freeze, false)) end
.parse_failure?(source, **options) ⇒ Boolean
Parse the given string and return true if it parses with errors. For supported options, see .parse.
# File 'prism/extension.c', line 1286
static VALUE
parse_failure_p(int argc, VALUE *argv, VALUE self) {
return RTEST(parse_success_p(argc, argv, self)) ? Qfalse : Qtrue;
}
.parse_file(filepath, **options)
Mirror the parse_file API by using the serialization API. This uses native strings instead of Ruby strings because it allows us to use mmap when it is available.
# File 'prism/extension.c', line 991
static VALUE
parse_file(int argc, VALUE *argv, VALUE self) {
pm_options_t *options = pm_options_new();
VALUE encoded_filepath;
pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
VALUE value = parse_input(pm_source_source(src), pm_source_length(src), options);
pm_source_free(src);
pm_options_free(options);
return value;
}
.parse_file_comments(filepath, **options)
Mirror the parse_file_comments API by using the serialization API. This uses native strings instead of Ruby strings because it allows us to use mmap when it is available.
# File 'prism/extension.c', line 1173
static VALUE
parse_file_comments(int argc, VALUE *argv, VALUE self) {
pm_options_t *options = pm_options_new();
VALUE encoded_filepath;
pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
VALUE value = parse_input_comments(pm_source_source(src), pm_source_length(src), options);
pm_source_free(src);
pm_options_free(options);
return value;
}
.parse_file_failure?(filepath, **options) ⇒ Boolean
Parse the given file and return true if it parses with errors. For supported options, see .parse.
# File 'prism/extension.c', line 1321
static VALUE
parse_file_failure_p(int argc, VALUE *argv, VALUE self) {
return RTEST(parse_file_success_p(argc, argv, self)) ? Qfalse : Qtrue;
}
.parse_file_success?(filepath, **options) ⇒ Boolean
Parse the given file and return true if it parses without errors. For supported options, see .parse.
# File 'prism/extension.c', line 1299
static VALUE
parse_file_success_p(int argc, VALUE *argv, VALUE self) {
pm_options_t *options = pm_options_new();
VALUE encoded_filepath;
pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
VALUE result = parse_input_success_p(pm_source_source(src), pm_source_length(src), options);
pm_source_free(src);
pm_options_free(options);
return result;
}
.parse_file_success_common(string, options) (private)
# File 'lib/prism/ffi.rb', line 424
def parse_file_success_common(string, ) # :nodoc: LibRubyParser.pm_serialize_parse_success_p(string.pointer, string.length, ()) end
.parse_lex(source, **options) ⇒ ParseLexResult
Parse the given string and return a ::Prism::ParseLexResult instance that contains a
2-element array, where the first element is the AST and the second element is
an array of ::Prism::Token instances.
This API is only meant to be used in the case where you need both the AST and the tokens. If you only need one or the other, use either .parse or .lex.
For supported options, see .parse.
# File 'prism/extension.c', line 1202
static VALUE
parse_lex(int argc, VALUE *argv, VALUE self) {
pm_options_t *options = pm_options_new();
VALUE string = string_options(argc, argv, options);
VALUE value = parse_lex_input((const uint8_t *) RSTRING_PTR(string), RSTRING_LEN(string), options, true);
pm_options_free(options);
return value;
}
.parse_lex_common(string, code, options) (private)
# File 'lib/prism/ffi.rb', line 417
def parse_lex_common(string, code, ) # :nodoc: LibRubyParser::PrismBuffer.with do |buffer| LibRubyParser.pm_serialize_parse_lex(buffer.pointer, string.pointer, string.length, ()) Serialize.load_parse_lex(code, buffer.read, .fetch(:freeze, false)) end end
.parse_lex_file(filepath, **options) ⇒ ParseLexResult
Parse the given file and return a ::Prism::ParseLexResult instance that contains a
2-element array, where the first element is the AST and the second element is
an array of ::Prism::Token instances.
This API is only meant to be used in the case where you need both the AST and the tokens. If you only need one or the other, use either .parse_file or .lex_file.
For supported options, see .parse.
# File 'prism/extension.c', line 1228
static VALUE
parse_lex_file(int argc, VALUE *argv, VALUE self) {
pm_options_t *options = pm_options_new();
VALUE encoded_filepath;
pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
VALUE value = parse_lex_input(pm_source_source(src), pm_source_length(src), options, true);
pm_source_free(src);
pm_options_free(options);
return value;
}
.parse_stream(stream, **options) ⇒ ParseResult
Parse the given object that responds to gets and return a ::Prism::ParseResult
instance. The options that are supported are the same as .parse.
# File 'prism/extension.c', line 1098
static VALUE
parse_stream(int argc, VALUE *argv, VALUE self) {
VALUE stream;
VALUE keywords;
rb_scan_args(argc, argv, "1:", &stream, &keywords);
pm_options_t *options = pm_options_new();
extract_options(options, Qnil, keywords);
pm_source_t *src = pm_source_stream_new((void *) stream, parse_stream_fgets, parse_stream_eof);
pm_arena_t *arena = pm_arena_new();
pm_parser_t *parser;
pm_node_t *node = pm_parse_stream(&parser, arena, src, options);
rb_encoding *encoding = rb_enc_find(pm_parser_encoding_name(parser));
VALUE source = pm_source_new(parser, encoding, pm_options_freeze(options));
VALUE value = pm_ast_new(parser, node, encoding, source, pm_options_freeze(options));
VALUE result = parse_result_create(rb_cPrismParseResult, parser, value, encoding, source, pm_options_freeze(options));
pm_source_free(src);
pm_parser_free(parser);
pm_arena_free(arena);
pm_options_free(options);
return result;
}
.parse_success?(source, **options) ⇒ Boolean
Parse the given string and return true if it parses without errors. For supported options, see .parse.
# File 'prism/extension.c', line 1267
static VALUE
parse_success_p(int argc, VALUE *argv, VALUE self) {
pm_options_t *options = pm_options_new();
VALUE string = string_options(argc, argv, options);
VALUE result = parse_input_success_p((const uint8_t *) RSTRING_PTR(string), RSTRING_LEN(string), options);
pm_options_free(options);
return result;
}
.profile(source, **options) ⇒ nil
Parse the given string and return nothing. This method is meant to allow profilers to avoid the overhead of reifying the AST to Ruby. For supported options, see .parse.
# File 'prism/extension.c', line 1027
static VALUE
profile(int argc, VALUE *argv, VALUE self) {
pm_options_t *options = pm_options_new();
VALUE string = string_options(argc, argv, options);
profile_input((const uint8_t *) RSTRING_PTR(string), RSTRING_LEN(string), options);
pm_options_free(options);
return Qnil;
}
.profile_file(filepath, **options) ⇒ nil
Parse the given file and return nothing. This method is meant to allow profilers to avoid the overhead of reifying the AST to Ruby. For supported options, see .parse.
# File 'prism/extension.c', line 1047
static VALUE
profile_file(int argc, VALUE *argv, VALUE self) {
pm_options_t *options = pm_options_new();
VALUE encoded_filepath;
pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
profile_input(pm_source_source(src), pm_source_length(src), options);
pm_source_free(src);
pm_options_free(options);
return Qnil;
}
.scope(locals: [], forwarding: [])
Create a new scope with the given locals and forwarding options that is suitable for passing into one of the Prism.* methods that accepts the scopes option.
.version_string_to_number(version) (private)
Converts a version string like “4.0.0” or “4.0” into a number. Returns nil if the version is unknown.
# File 'lib/prism/ffi.rb', line 468
def version_string_to_number(version) case version when /\A3\.3(\.\d+)?\z/ 1 when /\A3\.4(\.\d+)?\z/ 2 when /\A3\.5(\.\d+)?\z/, /\A4\.0(\.\d+)?\z/ 3 when /\A4\.1(\.\d+)?\z/ 4 end end