ruby/ext/ripper/ripper_init.c.tmpl
yui-knk 74c6781153 Change RNode structure from union to struct
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.
2023-09-28 11:58:10 +09:00

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
}