mirror of
https://github.com/ruby/ruby.git
synced 2025-08-15 13:39:04 +02:00

All kind of AST nodes use same struct RNode, which has u1, u2, u3 union members for holding different kind of data. This has two problems. 1. Low flexibility of data structure Some nodes, for example NODE_TRUE, don’t use u1, u2, u3. On the other hand, NODE_OP_ASGN2 needs more than three union members. However they use same structure definition, need to allocate three union members for NODE_TRUE and need to separate NODE_OP_ASGN2 into another node. This change removes the restriction so make it possible to change data structure by each node type. 2. No compile time check for union member access It’s developer’s responsibility for using correct member for each node type when it’s union. This change clarifies which node has which type of fields and enables compile time check. This commit also changes node_buffer_elem_struct buf management to handle different size data with alignment.
652 lines
15 KiB
C
652 lines
15 KiB
C
%# -*- c -*-
|
|
#include "ruby/ruby.h"
|
|
#include "ruby/encoding.h"
|
|
#include "internal.h"
|
|
#include "internal/imemo.h" /* needed by ruby_parser.h */
|
|
#include "internal/parse.h"
|
|
#include "internal/ruby_parser.h"
|
|
#include "node.h"
|
|
#include "rubyparser.h"
|
|
#include "eventids1.h"
|
|
#define YYSTYPE_IS_DECLARED
|
|
#include "parse.h"
|
|
#include "eventids2.h"
|
|
#include "ripper_init.h"
|
|
|
|
#define STR_NEW2(ptr) rb_enc_str_new((ptr),strlen(ptr),rb_ruby_parser_enc(p))
|
|
#define RIPPER_VERSION "0.1.0"
|
|
|
|
ID id_warn, id_warning, id_gets, id_assoc;
|
|
|
|
struct ripper {
|
|
rb_parser_t *p;
|
|
};
|
|
|
|
static void
|
|
ripper_parser_mark2(void *ptr)
|
|
{
|
|
struct ripper *r = (struct ripper*)ptr;
|
|
if (r->p) ripper_parser_mark(r->p);
|
|
}
|
|
|
|
static void
|
|
ripper_parser_free2(void *ptr)
|
|
{
|
|
struct ripper *r = (struct ripper*)ptr;
|
|
if (r->p) ripper_parser_free(r->p);
|
|
xfree(r);
|
|
}
|
|
|
|
static size_t
|
|
ripper_parser_memsize2(const void *ptr)
|
|
{
|
|
struct ripper *r = (struct ripper*)ptr;
|
|
return (r->p) ? ripper_parser_memsize(r->p) : 0;
|
|
}
|
|
|
|
static const rb_data_type_t parser_data_type = {
|
|
"ripper",
|
|
{
|
|
ripper_parser_mark2,
|
|
ripper_parser_free2,
|
|
ripper_parser_memsize2,
|
|
},
|
|
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
|
};
|
|
|
|
ID
|
|
ripper_get_id(VALUE v)
|
|
{
|
|
NODE *nd;
|
|
if (!RB_TYPE_P(v, T_NODE)) return 0;
|
|
nd = (NODE *)v;
|
|
if (!nd_type_p(nd, NODE_RIPPER)) return 0;
|
|
return RNODE_RIPPER(nd)->nd_vid;
|
|
}
|
|
|
|
VALUE
|
|
ripper_get_value(VALUE v)
|
|
{
|
|
NODE *nd;
|
|
if (UNDEF_P(v)) return Qnil;
|
|
if (!RB_TYPE_P(v, T_NODE)) return v;
|
|
nd = (NODE *)v;
|
|
if (!nd_type_p(nd, NODE_RIPPER)) return Qnil;
|
|
return RNODE_RIPPER(nd)->nd_rval;
|
|
}
|
|
|
|
static VALUE
|
|
ripper_lex_get_generic(struct parser_params *p, VALUE src)
|
|
{
|
|
VALUE line = rb_funcallv_public(src, id_gets, 0, 0);
|
|
if (!NIL_P(line) && !RB_TYPE_P(line, T_STRING)) {
|
|
rb_raise(rb_eTypeError,
|
|
"gets returned %"PRIsVALUE" (expected String or nil)",
|
|
rb_obj_class(line));
|
|
}
|
|
return line;
|
|
}
|
|
|
|
void
|
|
ripper_compile_error(struct parser_params *p, const char *fmt, ...)
|
|
{
|
|
VALUE str;
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
str = rb_vsprintf(fmt, args);
|
|
va_end(args);
|
|
rb_funcall(ripper_value(p), rb_intern("compile_error"), 1, str);
|
|
ripper_error(p);
|
|
}
|
|
|
|
static VALUE
|
|
ripper_lex_io_get(struct parser_params *p, VALUE src)
|
|
{
|
|
return rb_io_gets(src);
|
|
}
|
|
|
|
static VALUE
|
|
ripper_s_allocate(VALUE klass)
|
|
{
|
|
struct ripper *r;
|
|
|
|
VALUE self = TypedData_Make_Struct(klass, struct ripper,
|
|
&parser_data_type, r);
|
|
|
|
#ifdef UNIVERSAL_PARSER
|
|
rb_parser_config_t *config;
|
|
config = rb_ruby_parser_config_new(ruby_xmalloc);
|
|
rb_parser_config_initialize(config);
|
|
r->p = rb_ruby_parser_allocate(config);
|
|
#else
|
|
r->p = rb_ruby_ripper_parser_allocate();
|
|
#endif
|
|
rb_ruby_parser_set_value(r->p, self);
|
|
return self;
|
|
}
|
|
|
|
/*
|
|
* call-seq:
|
|
* ripper.error? -> Boolean
|
|
*
|
|
* Return true if parsed source has errors.
|
|
*/
|
|
static VALUE
|
|
ripper_error_p(VALUE vparser)
|
|
{
|
|
struct ripper *r;
|
|
|
|
TypedData_Get_Struct(vparser, struct ripper, &parser_data_type, r);
|
|
return RBOOL(rb_ruby_parser_error_p(r->p));
|
|
}
|
|
|
|
/*
|
|
* call-seq:
|
|
* ripper.end_seen? -> Boolean
|
|
*
|
|
* Return true if parsed source ended by +\_\_END\_\_+.
|
|
*/
|
|
static VALUE
|
|
ripper_parser_end_seen_p(VALUE vparser)
|
|
{
|
|
struct ripper *r;
|
|
|
|
TypedData_Get_Struct(vparser, struct ripper, &parser_data_type, r);
|
|
return RBOOL(rb_ruby_parser_end_seen_p(r->p));
|
|
}
|
|
|
|
/*
|
|
* call-seq:
|
|
* ripper.encoding -> encoding
|
|
*
|
|
* Return encoding of the source.
|
|
*/
|
|
static VALUE
|
|
ripper_parser_encoding(VALUE vparser)
|
|
{
|
|
struct ripper *r;
|
|
|
|
TypedData_Get_Struct(vparser, struct ripper, &parser_data_type, r);
|
|
return rb_ruby_parser_encoding(r->p);
|
|
}
|
|
|
|
/*
|
|
* call-seq:
|
|
* ripper.yydebug -> true or false
|
|
*
|
|
* Get yydebug.
|
|
*/
|
|
static VALUE
|
|
ripper_parser_get_yydebug(VALUE self)
|
|
{
|
|
struct ripper *r;
|
|
|
|
TypedData_Get_Struct(self, struct ripper, &parser_data_type, r);
|
|
return RBOOL(rb_ruby_parser_get_yydebug(r->p));
|
|
}
|
|
|
|
/*
|
|
* call-seq:
|
|
* ripper.yydebug = flag
|
|
*
|
|
* Set yydebug.
|
|
*/
|
|
static VALUE
|
|
ripper_parser_set_yydebug(VALUE self, VALUE flag)
|
|
{
|
|
struct ripper *r;
|
|
|
|
TypedData_Get_Struct(self, struct ripper, &parser_data_type, r);
|
|
rb_ruby_parser_set_yydebug(r->p, RTEST(flag));
|
|
return flag;
|
|
}
|
|
|
|
/*
|
|
* call-seq:
|
|
* ripper.debug_output -> obj
|
|
*
|
|
* Get debug output.
|
|
*/
|
|
static VALUE
|
|
ripper_parser_get_debug_output(VALUE self)
|
|
{
|
|
struct ripper *r;
|
|
|
|
TypedData_Get_Struct(self, struct ripper, &parser_data_type, r);
|
|
return rb_ruby_parser_debug_output(r->p);
|
|
}
|
|
|
|
/*
|
|
* call-seq:
|
|
* ripper.debug_output = obj
|
|
*
|
|
* Set debug output.
|
|
*/
|
|
static VALUE
|
|
ripper_parser_set_debug_output(VALUE self, VALUE output)
|
|
{
|
|
struct ripper *r;
|
|
|
|
TypedData_Get_Struct(self, struct ripper, &parser_data_type, r);
|
|
rb_ruby_parser_set_debug_output(r->p, output);
|
|
return output;
|
|
}
|
|
|
|
#ifdef UNIVERSAL_PARSER
|
|
struct dedent_string_arg {
|
|
struct parser_params *p;
|
|
VALUE input;
|
|
VALUE width;
|
|
};
|
|
|
|
static VALUE
|
|
parser_dedent_string0(VALUE a)
|
|
{
|
|
struct dedent_string_arg *arg = (void *)a;
|
|
int wid, col;
|
|
|
|
StringValue(arg->input);
|
|
wid = NUM2UINT(arg->width);
|
|
col = rb_ruby_ripper_dedent_string(arg->p, arg->input, wid);
|
|
return INT2NUM(col);
|
|
}
|
|
|
|
static VALUE
|
|
parser_config_free(VALUE a)
|
|
{
|
|
rb_parser_config_t *config = (void *)a;
|
|
|
|
rb_ruby_parser_config_free(config);
|
|
return Qnil;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* call-seq:
|
|
* Ripper.dedent_string(input, width) -> Integer
|
|
*
|
|
* USE OF RIPPER LIBRARY ONLY.
|
|
*
|
|
* Strips up to +width+ leading whitespaces from +input+,
|
|
* and returns the stripped column width.
|
|
*/
|
|
#ifdef UNIVERSAL_PARSER
|
|
static VALUE
|
|
parser_dedent_string(VALUE self, VALUE input, VALUE width)
|
|
{
|
|
struct parser_params *p;
|
|
rb_parser_config_t *config;
|
|
struct dedent_string_arg args;
|
|
|
|
config = rb_ruby_parser_config_new(ruby_xmalloc);
|
|
rb_parser_config_initialize(config);
|
|
p = rb_ruby_parser_new(config);
|
|
|
|
args.p = p;
|
|
args.input = input;
|
|
args.width = width;
|
|
return rb_ensure(parser_dedent_string0, (VALUE)&args, parser_config_free, (VALUE)config);
|
|
}
|
|
#else
|
|
static VALUE
|
|
parser_dedent_string(VALUE self, VALUE input, VALUE width)
|
|
{
|
|
int wid, col;
|
|
|
|
StringValue(input);
|
|
wid = NUM2UINT(width);
|
|
col = rb_ruby_ripper_dedent_string(0, input, wid);
|
|
return INT2NUM(col);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* call-seq:
|
|
* Ripper.new(src, filename="(ripper)", lineno=1) -> ripper
|
|
*
|
|
* Create a new Ripper object.
|
|
* _src_ must be a String, an IO, or an Object which has #gets method.
|
|
*
|
|
* This method does not starts parsing.
|
|
* See also Ripper#parse and Ripper.parse.
|
|
*/
|
|
static VALUE
|
|
ripper_initialize(int argc, VALUE *argv, VALUE self)
|
|
{
|
|
struct ripper *r;
|
|
struct parser_params *p;
|
|
VALUE src, fname, lineno;
|
|
VALUE (*gets)(struct parser_params*,VALUE);
|
|
VALUE input, sourcefile_string;
|
|
const char *sourcefile;
|
|
int sourceline;
|
|
|
|
TypedData_Get_Struct(self, struct ripper, &parser_data_type, r);
|
|
p = r->p;
|
|
rb_scan_args(argc, argv, "12", &src, &fname, &lineno);
|
|
if (RB_TYPE_P(src, T_FILE)) {
|
|
gets = ripper_lex_io_get;
|
|
}
|
|
else if (rb_respond_to(src, id_gets)) {
|
|
gets = ripper_lex_get_generic;
|
|
}
|
|
else {
|
|
StringValue(src);
|
|
gets = rb_ruby_ripper_lex_get_str;
|
|
}
|
|
input = src;
|
|
if (NIL_P(fname)) {
|
|
fname = STR_NEW2("(ripper)");
|
|
OBJ_FREEZE(fname);
|
|
}
|
|
else {
|
|
StringValueCStr(fname);
|
|
fname = rb_str_new_frozen(fname);
|
|
}
|
|
rb_ruby_ripper_parser_initialize(p);
|
|
|
|
sourcefile_string = fname;
|
|
sourcefile = RSTRING_PTR(fname);
|
|
sourceline = NIL_P(lineno) ? 0 : NUM2INT(lineno) - 1;
|
|
|
|
rb_ruby_parser_ripper_initialize(p, gets, input, sourcefile_string, sourcefile, sourceline);
|
|
|
|
return Qnil;
|
|
}
|
|
|
|
static VALUE
|
|
ripper_parse0(VALUE vparser)
|
|
{
|
|
struct ripper *r;
|
|
struct parser_params *p;
|
|
|
|
TypedData_Get_Struct(vparser, struct ripper, &parser_data_type, r);
|
|
p = r->p;
|
|
// RB_GC_GUARD(vparser);
|
|
rb_ruby_ripper_parse0(p);
|
|
return rb_ruby_parser_result(p);
|
|
}
|
|
|
|
static VALUE
|
|
ripper_ensure(VALUE vparser)
|
|
{
|
|
struct ripper *r;
|
|
|
|
TypedData_Get_Struct(vparser, struct ripper, &parser_data_type, r);
|
|
rb_ruby_parser_set_parsing_thread(r->p, Qnil);
|
|
return Qnil;
|
|
}
|
|
|
|
/*
|
|
* call-seq:
|
|
* ripper.parse
|
|
*
|
|
* Start parsing and returns the value of the root action.
|
|
*/
|
|
static VALUE
|
|
ripper_parse(VALUE self)
|
|
{
|
|
struct ripper *r;
|
|
struct parser_params *p;
|
|
|
|
TypedData_Get_Struct(self, struct ripper, &parser_data_type, r);
|
|
p = r->p;
|
|
if (!rb_ruby_ripper_initialized_p(p)) {
|
|
rb_raise(rb_eArgError, "method called for uninitialized object");
|
|
}
|
|
if (!NIL_P(rb_ruby_parser_parsing_thread(p))) {
|
|
if (rb_ruby_parser_parsing_thread(p) == rb_thread_current())
|
|
rb_raise(rb_eArgError, "Ripper#parse is not reentrant");
|
|
else
|
|
rb_raise(rb_eArgError, "Ripper#parse is not multithread-safe");
|
|
}
|
|
rb_ruby_parser_set_parsing_thread(p, rb_thread_current());
|
|
rb_ensure(ripper_parse0, self, ripper_ensure, self);
|
|
|
|
return rb_ruby_parser_result(p);
|
|
}
|
|
|
|
/*
|
|
* call-seq:
|
|
* ripper.column -> Integer
|
|
*
|
|
* Return column number of current parsing line.
|
|
* This number starts from 0.
|
|
*/
|
|
static VALUE
|
|
ripper_column(VALUE self)
|
|
{
|
|
struct ripper *r;
|
|
struct parser_params *p;
|
|
long col;
|
|
|
|
TypedData_Get_Struct(self, struct ripper, &parser_data_type, r);
|
|
p = r->p;
|
|
if (!rb_ruby_ripper_initialized_p(p)) {
|
|
rb_raise(rb_eArgError, "method called for uninitialized object");
|
|
}
|
|
if (NIL_P(rb_ruby_parser_parsing_thread(p))) return Qnil;
|
|
col = rb_ruby_ripper_column(p);
|
|
return LONG2NUM(col);
|
|
}
|
|
|
|
/*
|
|
* call-seq:
|
|
* ripper.filename -> String
|
|
*
|
|
* Return current parsing filename.
|
|
*/
|
|
static VALUE
|
|
ripper_filename(VALUE self)
|
|
{
|
|
struct ripper *r;
|
|
struct parser_params *p;
|
|
|
|
TypedData_Get_Struct(self, struct ripper, &parser_data_type, r);
|
|
p = r->p;
|
|
if (!rb_ruby_ripper_initialized_p(p)) {
|
|
rb_raise(rb_eArgError, "method called for uninitialized object");
|
|
}
|
|
return rb_ruby_parser_ruby_sourcefile_string(p);
|
|
}
|
|
|
|
/*
|
|
* call-seq:
|
|
* ripper.lineno -> Integer
|
|
*
|
|
* Return line number of current parsing line.
|
|
* This number starts from 1.
|
|
*/
|
|
static VALUE
|
|
ripper_lineno(VALUE self)
|
|
{
|
|
struct ripper *r;
|
|
struct parser_params *p;
|
|
|
|
TypedData_Get_Struct(self, struct ripper, &parser_data_type, r);
|
|
p = r->p;
|
|
if (!rb_ruby_ripper_initialized_p(p)) {
|
|
rb_raise(rb_eArgError, "method called for uninitialized object");
|
|
}
|
|
if (NIL_P(rb_ruby_parser_parsing_thread(p))) return Qnil;
|
|
return INT2NUM(rb_ruby_parser_ruby_sourceline(p));
|
|
}
|
|
|
|
/*
|
|
* call-seq:
|
|
* ripper.state -> Integer
|
|
*
|
|
* Return scanner state of current token.
|
|
*/
|
|
static VALUE
|
|
ripper_state(VALUE self)
|
|
{
|
|
struct ripper *r;
|
|
struct parser_params *p;
|
|
|
|
TypedData_Get_Struct(self, struct ripper, &parser_data_type, r);
|
|
p = r->p;
|
|
if (!rb_ruby_ripper_initialized_p(p)) {
|
|
rb_raise(rb_eArgError, "method called for uninitialized object");
|
|
}
|
|
if (NIL_P(rb_ruby_parser_parsing_thread(p))) return Qnil;
|
|
return INT2NUM(rb_ruby_parser_lex_state(p));
|
|
}
|
|
|
|
/*
|
|
* call-seq:
|
|
* ripper.token -> String
|
|
*
|
|
* Return the current token string.
|
|
*/
|
|
static VALUE
|
|
ripper_token(VALUE self)
|
|
{
|
|
struct ripper *r;
|
|
struct parser_params *p;
|
|
long pos, len;
|
|
|
|
TypedData_Get_Struct(self, struct ripper, &parser_data_type, r);
|
|
p = r->p;
|
|
if (!rb_ruby_ripper_initialized_p(p)) {
|
|
rb_raise(rb_eArgError, "method called for uninitialized object");
|
|
}
|
|
if (NIL_P(rb_ruby_parser_parsing_thread(p))) return Qnil;
|
|
pos = rb_ruby_ripper_column(p);
|
|
len = rb_ruby_ripper_token_len(p);
|
|
return rb_str_subseq(rb_ruby_ripper_lex_lastline(p), pos, len);
|
|
}
|
|
|
|
#ifdef RIPPER_DEBUG
|
|
/* :nodoc: */
|
|
static VALUE
|
|
ripper_assert_Qundef(VALUE self, VALUE obj, VALUE msg)
|
|
{
|
|
StringValue(msg);
|
|
if (UNDEF_P(obj)) {
|
|
rb_raise(rb_eArgError, "%"PRIsVALUE, msg);
|
|
}
|
|
return Qnil;
|
|
}
|
|
|
|
/* :nodoc: */
|
|
static VALUE
|
|
ripper_raw_value(VALUE self, VALUE obj)
|
|
{
|
|
return ULONG2NUM(obj);
|
|
}
|
|
#endif
|
|
|
|
#ifdef UNIVERSAL_PARSER
|
|
struct lex_state_name_arg {
|
|
struct parser_params *p;
|
|
VALUE state;
|
|
};
|
|
|
|
static VALUE
|
|
lex_state_name0(VALUE a)
|
|
{
|
|
struct lex_state_name_arg *arg = (void *)a;
|
|
|
|
return rb_ruby_ripper_lex_state_name(arg->p, NUM2INT(arg->state));
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* call-seq:
|
|
* Ripper.lex_state_name(integer) -> string
|
|
*
|
|
* Returns a string representation of lex_state.
|
|
*/
|
|
#ifdef UNIVERSAL_PARSER
|
|
static VALUE
|
|
ripper_lex_state_name(VALUE self, VALUE state)
|
|
{
|
|
struct parser_params *p;
|
|
rb_parser_config_t *config;
|
|
struct lex_state_name_arg args;
|
|
|
|
config = rb_ruby_parser_config_new(ruby_xmalloc);
|
|
rb_parser_config_initialize(config);
|
|
p = rb_ruby_parser_new(config);
|
|
|
|
args.p = p;
|
|
args.state = state;
|
|
|
|
return rb_ensure(lex_state_name0, (VALUE)&args, parser_config_free, (VALUE)config);
|
|
}
|
|
#else
|
|
static VALUE
|
|
ripper_lex_state_name(VALUE self, VALUE state)
|
|
{
|
|
return rb_ruby_ripper_lex_state_name(0, NUM2INT(state));
|
|
}
|
|
#endif
|
|
|
|
void
|
|
Init_ripper(void)
|
|
{
|
|
ripper_init_eventids1();
|
|
ripper_init_eventids2();
|
|
id_warn = rb_intern_const("warn");
|
|
id_warning = rb_intern_const("warning");
|
|
id_gets = rb_intern_const("gets");
|
|
id_assoc = rb_intern_const("=>");
|
|
|
|
InitVM(ripper);
|
|
}
|
|
|
|
void
|
|
InitVM_ripper(void)
|
|
{
|
|
VALUE Ripper;
|
|
|
|
Ripper = rb_define_class("Ripper", rb_cObject);
|
|
/* version of Ripper */
|
|
rb_define_const(Ripper, "Version", rb_usascii_str_new2(RIPPER_VERSION));
|
|
rb_define_alloc_func(Ripper, ripper_s_allocate);
|
|
rb_define_method(Ripper, "initialize", ripper_initialize, -1);
|
|
rb_define_method(Ripper, "parse", ripper_parse, 0);
|
|
rb_define_method(Ripper, "column", ripper_column, 0);
|
|
rb_define_method(Ripper, "filename", ripper_filename, 0);
|
|
rb_define_method(Ripper, "lineno", ripper_lineno, 0);
|
|
rb_define_method(Ripper, "state", ripper_state, 0);
|
|
rb_define_method(Ripper, "token", ripper_token, 0);
|
|
rb_define_method(Ripper, "end_seen?", ripper_parser_end_seen_p, 0);
|
|
rb_define_method(Ripper, "encoding", ripper_parser_encoding, 0);
|
|
rb_define_method(Ripper, "yydebug", ripper_parser_get_yydebug, 0);
|
|
rb_define_method(Ripper, "yydebug=", ripper_parser_set_yydebug, 1);
|
|
rb_define_method(Ripper, "debug_output", ripper_parser_get_debug_output, 0);
|
|
rb_define_method(Ripper, "debug_output=", ripper_parser_set_debug_output, 1);
|
|
rb_define_method(Ripper, "error?", ripper_error_p, 0);
|
|
#ifdef RIPPER_DEBUG
|
|
rb_define_method(Ripper, "assert_Qundef", ripper_assert_Qundef, 2);
|
|
rb_define_method(Ripper, "rawVALUE", ripper_raw_value, 1);
|
|
rb_define_method(Ripper, "validate_object", ripper_validate_object, 1);
|
|
#endif
|
|
|
|
rb_define_singleton_method(Ripper, "dedent_string", parser_dedent_string, 2);
|
|
rb_define_private_method(Ripper, "dedent_string", parser_dedent_string, 2);
|
|
|
|
rb_define_singleton_method(Ripper, "lex_state_name", ripper_lex_state_name, 1);
|
|
|
|
<% @exprs.each do |expr, desc| -%>
|
|
/* <%=desc%> */
|
|
rb_define_const(Ripper, "<%=expr%>", INT2NUM(<%=expr%>));
|
|
<% end %>
|
|
ripper_init_eventids1_table(Ripper);
|
|
ripper_init_eventids2_table(Ripper);
|
|
|
|
# if 0
|
|
/* Hack to let RDoc document SCRIPT_LINES__ */
|
|
|
|
/*
|
|
* When a Hash is assigned to +SCRIPT_LINES__+ the contents of files loaded
|
|
* after the assignment will be added as an Array of lines with the file
|
|
* name as the key.
|
|
*/
|
|
rb_define_global_const("SCRIPT_LINES__", Qnil);
|
|
#endif
|
|
|
|
}
|