Skip to content

Instantly share code, notes, and snippets.

@wanabe
Last active February 12, 2023 22:12
Show Gist options
  • Save wanabe/a3fe8a4569eb4c92bca60fbdc9750109 to your computer and use it in GitHub Desktop.
Save wanabe/a3fe8a4569eb4c92bca60fbdc9750109 to your computer and use it in GitHub Desktop.
# original file: https://git.ruby-lang.org/ruby.git/tree/parse.y
%prefix "tiny_ruby_parser"
%capture on
%value c -> "tiny_ruby_value" rb -> "TinyRubyValue"
%auxil c -> "tiny_ruby_context_ext *"
dump <- program:program
c -> {
tiny_ruby_parser_dump_node(program.node, rb_str_new2("# "));
}
rb -> {
program.debug_dump("# ")
}
program <- $$:top_compstmt
'\n'*
EOF
top_compstmt <- < top_stmts:top_stmts >
term*
c -> {
VALUE tbl;
if (NIL_P(top_stmts.node->var_table)) {
tbl = rb_ary_new();
} else {
tbl = top_stmts.node->var_table;
}
$$.node = NEW_NODE(NODE_SCOPE, tbl, top_stmts.node, 0, top_stmts.node->nd_loc.beg_pos, top_stmts.node->nd_loc.end_pos);
}
rb -> {
$$ = ScopeNode.new(top_stmts.var_table || [], top_stmts, nil, top_stmts.nd_loc.beg_pos, top_stmts.nd_loc.end_pos)
}
top_stmts <- top_stmts:top_stmts spaces?
term+ spaces?
top_stmt:top_stmt
c -> {
top_stmt.node->newline = 1;
$$.node = tiny_ruby_parser_block_append(top_stmts.node, top_stmt.node);
tiny_ruby_parser_var_table_pass($$.node, top_stmt.node);
}
rb -> {
top_stmt.newline = true
$$ = TinyRubyParser.block_append(top_stmts, top_stmt)
$$.var_table_pass(top_stmt)
}
/ top_stmt:top_stmt
c -> {
top_stmt.node->newline = 1;
$$ = top_stmt;
}
rb -> {
top_stmt.newline = true;
$$ = top_stmt
}
/ spaces?
c -> {
$$.node = NEW_NODE(NODE_BEGIN, 0, 0, 0, $0sl, $0sl);
}
rb -> {
$$ = BeginNode.new(nil, nil, nil, $0sl, $0sl)
}
top_stmt <- $$:stmt
stmt <- $$:expr
compstmt <- $$:stmts term*
bodystmt <- $$:compstmt
stmts <- stmts:stmts spaces?
term+ spaces?
stmt:stmt
c -> {
stmt.node->newline = 1;
$$.node = tiny_ruby_parser_block_append(stmts.node, stmt.node);
tiny_ruby_parser_var_table_pass($$.node, stmt.node);
}
rb -> {
stmt.newline = true
$$ = TinyRubyParser.block_append(stmts, stmt)
$$.var_table_pass(stmt)
}
/ stmt:stmt
c -> {
stmt.node->newline = 1;
$$.node = stmt.node;
}
rb -> {
stmt.newline = true
$$ = stmt
}
expr <- $$:command_call
/ $$:arg
command_call <- $$:command
command <- fcall:fcall
spaces
command_args:command_args
c -> {
fcall.node->nd_args = command_args.node;
fcall.node->nd_loc.beg_pos = $0c.range.start_loc;
fcall.node->nd_loc.end_pos = $0c.range.end_loc;
$$ = fcall;
}
rb -> {
fcall.nd_args = command_args
fcall.nd_loc.beg_pos = $0c.start_loc
fcall.nd_loc.end_pos = $0c.end_loc
$$ = fcall
}
method_call <- fcall:fcall spaces?
paren_args:paren_args
c -> {
$$.node = fcall.node;
$$.node->nd_args = paren_args.node;
$$.node->nd_loc.end_pos = $0c.range.end_loc;
}
rb -> {
$$ = fcall
$$.nd_args = paren_args
$$.nd_loc.end_pos = $0c.end_loc
}
/ primary:primary
call_op:call_op
<operation2:operation2>
(
paren_args:paren_args
c -> { $$.node = paren_args.node; }
rb -> { $$ = paren_args }
/ ''
c -> { $$.node = NULL; }
rb -> { $$ = nil }
)
c -> {
$$.node = NEW_NODE(call_op.value, primary.node, operation2.value, $$.node, $0sl, $0el);
$$.node->nd_loc.lineno = $1el.lineno;
}
rb -> {
$$ = call_op.new(primary, operation2, $$, $0sl, $0el)
$$.nd_loc.lineno = $1el.lineno
}
fcall <- operation:operation
c -> {
$$.node = NEW_NODE(NODE_FCALL, NULL, operation.node, NULL, $0sl, $0el);
}
rb -> {
$$ = FCallNode.new(operation, nil, $0sl, $0el)
}
fname <- $$:tIDENTIFIER
/ $$:tCONSTANT
/ $$:op
operation <- $$:tIDENTIFIER
operation2 <- $$:operation
/ $$:op
op <- [-+/%*]
c -> {
$$.value = ID2SYM(rb_intern($0));
}
rb -> {
$$ = $0.to_sym
}
call_op <- '.'
c -> {
$$.value = NODE_CALL;
}
rb -> {
$$ = CallNode
}
/ tANDDOT
c -> {
$$.value = NODE_QCALL;
}
rb -> {
$$ = QCallNode
}
tANDDOT <- '&.'
command_args <- $$:call_args
paren_args <- '('
spaces?
$$:call_args?
spaces?
')'
call_args <- $$:args
args <- args:args
spaces?
','
spaces?
arg_value:arg_value
c -> {
$$.node = tiny_ruby_parser_list_append(args.node, arg_value.node);
}
rb -> {
$$ = TinyRubyParser.list_append(args, arg_value)
}
/ arg_value:arg_value
c -> {
$$.node = NEW_NODE(NODE_LIST, arg_value.node, 1, 0, $0sl, $0el);
}
rb -> {
$$ = ListNode.new(arg_value, $0sl, $0el)
}
arg_value <- $$:arg
arg_rhs <- $$:arg
arg <- $$:arg_addsub
/ lhs:tIDENTIFIER spaces?
'=' spaces?
arg:arg_rhs
c -> {
$$.node = NEW_NODE(NODE_LASGN, lhs.value, arg.node, 0, $0sl, $0el);
tiny_ruby_parser_var_table_add($$.node, lhs.value);
}
rb -> {
$$ = LasgnNode.new(lhs, arg, $0sl, $0el)
$$.var_table_add(lhs)
}
arg_addsub <- recv:arg_addsub
spaces?
< [-+] >
spaces?
arg:arg_muldiv
c -> {
NODE *args = NEW_NODE(NODE_LIST, arg.node, 1, 0, arg.node->nd_loc.beg_pos, arg.node->nd_loc.end_pos);
$$.node = NEW_NODE(NODE_OPCALL, recv.node, ID2SYM(rb_intern($1)), args, $0sl, $0el);
}
rb -> {
args = ListNode.new(arg, arg.nd_loc.beg_pos, arg.nd_loc.end_pos)
$$ = OpCallNode.new(recv, $1.to_sym, args, $0sl, $0el)
}
/ $$:arg_muldiv
arg_muldiv <- recv:arg_muldiv
spaces?
< [*/%] >
spaces?
arg:arg_primary
c -> {
NODE *args = NEW_NODE(NODE_LIST, arg.node, 1, 0, arg.node->nd_loc.beg_pos, arg.node->nd_loc.end_pos);
$$.node = NEW_NODE(NODE_OPCALL, recv.node, ID2SYM(rb_intern($1)), args, $0sl, $0el);
}
rb -> {
args = ListNode.new(arg, arg.nd_loc.beg_pos, arg.nd_loc.end_pos)
$$ = OpCallNode.new(recv, $1.to_sym, args, $0sl, $0el)
}
/ $$:arg_primary
arg_primary <- $$:primary
primary <- defn_head:defn_head
f_arglist:f_arglist spaces?
bodystmt:bodystmt? spaces?
'end'
c -> {
NODE *body = bodystmt.node;
if (f_arglist.node->nd_head) {
body = tiny_ruby_parser_block_append(f_arglist.node->nd_head, bodystmt.node);
f_arglist.node->nd_head = NULL;
tiny_ruby_parser_var_table_pass(body, bodystmt.node);
}
$$.node = tiny_ruby_parser_set_defun_body(defn_head.node, f_arglist.node, body, $0sl, $0el);
tiny_ruby_parser_var_table_pass($$.node, body);
}
rb -> {
if f_arglist.nd_head
body = TinyRubyParser.block_append(f_arglist.nd_head, bodystmt)
f_arglist.nd_head = nil
body.var_table_pass(bodystmt)
else
body = bodystmt
end
$$ = TinyRubyParser.set_defun_body(defn_head, f_arglist, body, $0sl, $0el);
$$.var_table_pass(body)
}
/ 'class' spaces
cpath:cpath <spaces?> <term?> (spaces? term?)*
bodystmt:bodystmt? (spaces? term?)*
'end'
c -> {
NODE *body = bodystmt.node;
if($2[0] != '\0') {
body = tiny_ruby_parser_block_append(NEW_NODE(NODE_BEGIN, 0, 0, 0, $1sl, $1sl), bodystmt.node);
tiny_ruby_parser_var_table_pass(body, bodystmt.node);
}
$$.node = NEW_NODE(NODE_CLASS, cpath.node, NEW_NODE(NODE_SCOPE, rb_ary_new(), body, 0, $0sl, $0el), NULL, $0sl, $0el);
}
rb -> {
if $2 != ""
body = TinyRubyParser.block_append(BeginNode.new(nil, nil, nil, $1sl, $1sl), bodystmt)
body.var_table_pass(bodystmt)
else
body = bodystmt
end
$$ = ClassNode.new(cpath, ScopeNode.new([], body, nil, $0sl, $0el), nil, $0sl, $0el)
}
/ $$:method_call
/ $$:literal
/ '(' $$:compstmt ')'
defn_head <- 'def' spaces def_name:def_name
c -> {
$$.node = NEW_NODE(NODE_DEFN, 0, def_name.node->nd_mid, def_name.node, $0sl, $0el);
}
rb -> {
$$ = DefnNode.new(def_name.nd_mid, def_name, $0sl, $0el)
}
def_name <- fname:fname
c -> {
$$.node = NEW_NODE(NODE_SELF, 0, fname.node, 0, $0sl, $0el);
}
rb -> {
$$ = SelfNode.new(nil, fname, nil, $0sl, $0el)
}
f_arglist <- spaces? f_paren_args:f_paren_args <spaces?> <term?> (spaces* term)*
c -> {
$$ = f_paren_args;
if($2[0] != '\0') {
$$.node->nd_head = NEW_NODE(NODE_BEGIN, 0, 0, 0, $1sl, $1sl);
}
}
rb -> {
$$ = f_paren_args
if $2 != ""
$$.nd_head = BeginNode.new(nil, nil, nil, $1sl, $1sl)
end
}
/ f_args:f_args spaces? term <spaces?> <term?> (spaces* term)*
c -> {
$$ = f_args;
if($4[0] != '\0') {
$$.node->nd_head = NEW_NODE(NODE_BEGIN, 0, 0, 0, $3sl, $3sl);
}
}
rb -> {
$$ = f_args
if $4 != ""
$$.nd_head = BeginNode.new(nil, nil, nil, $3sl, $3sl)
end
}
f_paren_args <- '(' spaces? $$:f_args spaces? ')'
f_args <- f_arg:f_arg
c -> {
struct tiny_ruby_parser_args_info *args = calloc(1, sizeof(struct tiny_ruby_parser_args_info));
args->pre_args_num = RARRAY_LEN(f_arg.node->var_table);
$$.node = NEW_NODE(NODE_ARGS, 0, 0, args, $0sl, $0el);
tiny_ruby_parser_var_table_pass($$.node, f_arg.node);
}
rb -> {
args = ArgsInfo.new
args.pre_args_num = f_arg.var_table.size
$$ = ArgsNode.new(nil, nil, args, $0sl, $0el)
$$.var_table_pass(f_arg)
}
/ # none
c -> {
struct tiny_ruby_parser_args_info *args = calloc(1, sizeof(struct tiny_ruby_parser_args_info));
$$.node = NEW_NODE(NODE_ARGS, 0, 0, args, $0sl, $0el);
}
rb -> {
args = ArgsInfo.new
$$ = ArgsNode.new(nil, nil, args, $0sl, $0el)
}
f_arg <- f_arg:f_arg spaces? ',' spaces? f_arg_item:f_arg_item
c -> {
$$.node = f_arg.node;
$$.node->nd_plen++;
$$.node->nd_next = tiny_ruby_parser_block_append($$.node->nd_next, f_arg_item.node->nd_next);
tiny_ruby_parser_var_table_pass($$.node, f_arg_item.node);
free(f_arg_item.node);
}
rb -> {
$$ = f_arg
$$.nd_plen += 1
$$.nd_next = TinyRubyParser.block_append($$.nd_next, f_arg_item.nd_next)
$$.var_table_pass(f_arg_item)
}
/ $$:f_arg_item
f_arg_item <- f_arg_asgn:f_arg_asgn
c -> {
$$.node = NEW_NODE(NODE_ARGS_AUX, f_arg_asgn.value, 1, 0, NULL_LOC.beg_pos, NULL_LOC.end_pos);
tiny_ruby_parser_var_table_add($$.node, f_arg_asgn.value);
}
rb -> {
$$ = ArgsAuxNode.new(f_arg_asgn, 1, NULL_LOC.beg_pos, NULL_LOC.end_pos)
$$.var_table_add(f_arg_asgn)
}
f_arg_asgn <- $$:f_norm_arg
f_norm_arg <- $$:tIDENTIFIER
cpath <- cname:tCONSTANT
c -> {
$$.node = NEW_NODE(NODE_COLON2, 0, cname.value, 0, $0sl, $0el);
}
rb -> {
$$ = Colon2Node.new(nil, cname, $0sl, $0el)
}
literal <- $$:numeric
numeric <- $$:simple_numeric
simple_numeric <- $$:tINTEGER
tINTEGER <- '0' [Xx] < [0-9a-fA-F]+ ('_'+ [0-9a-fA-F]+)* >
c -> {
$$.node = NEW_NODE(NODE_LIT, tiny_ruby_parser_cstr2num($1, 16), 0, 0, $0sl, $0el);
}
rb -> {
$$ = LitNode.new($1.gsub("_", "").to_i(16), $0sl, $0el)
}
/ '0' [Oo] < [0-7]+ ('_'+ [_0-7]+)* >
c -> {
$$.node = NEW_NODE(NODE_LIT, tiny_ruby_parser_cstr2num($2, 8), 0, 0, $0sl, $0el);
}
rb -> {
$$ = LitNode.new($2.gsub("_", "").to_i(8), $0sl, $0el)
}
/ '0' [Bb] < [01]+ ('_'+ [_01]+)* >
c -> {
$$.node = NEW_NODE(NODE_LIT, tiny_ruby_parser_cstr2num($3, 2), 0, 0, $0sl, $0el);
}
rb -> {
$$ = LitNode.new($3.gsub("_", "").to_i(2), $0sl, $0el)
}
/ ('0' [Dd])? < [0-9]+ ('_'+ [0-9]+)* >
c -> {
$$.node = NEW_NODE(NODE_LIT, tiny_ruby_parser_cstr2num($4, 10), 0, 0, $0sl, $0el);
}
rb -> {
$$ = LitNode.new($4.gsub("_", "").to_i, $0sl, $0el)
}
tIDENTIFIER <- [a-z_] [a-zA-Z_0-9]*
c -> {
$$.value = ID2SYM(rb_intern($0));
}
rb -> {
$$ = $0.to_sym
}
tCONSTANT <- [A-Z] [a-zA-Z_0-9]*
c -> {
$$.value = ID2SYM(rb_intern($0));
}
rb -> {
$$ = $0.to_sym
}
term <- ';'
c -> {
$$.value = LONG2NUM(';');
}
rb -> {
$$ = ';'
}
/ '\n'
c -> {
$$.value = LONG2NUM('\n');
}
rb -> {
$$ = '\n'
}
spaces <- [ \t\v\f\r]+
%location
c -> ${
static inline void packcr_location_init(packcr_location_t *lp) {
lp->lineno = 0;
lp->column = 0;
}
static inline void packcr_location_forward(packcr_location_t *lp, char *buf, size_t n) {
size_t i = 0;
for (; i < n; i++) {
if (buf[i] == '\n') {
lp->column = 0;
lp->lineno++;
} else {
lp->column++;
}
}
}
static inline packcr_location_t packcr_location_add(packcr_location_t l1, packcr_location_t l2) {
packcr_location_t l = { l1.lineno + l2.lineno, l2.column };
if (l2.lineno == 0) {
l.column += l1.column;
}
return l;
}
static inline packcr_location_t packcr_location_sub(packcr_location_t l1, packcr_location_t l2) {
packcr_location_t l = { l1.lineno - l2.lineno, l1.column - l2.column };
return l;
}
}
rb -> ${
class Location
attr_reader :lineno, :column
def initialize(lineno = 0, column = 0)
@lineno = lineno
@column = column
end
def +(other)
if other.lineno == 0
Location.new(@lineno + other.lineno, @column + other.column)
else
Location.new(@lineno + other.lineno, other.column)
end
end
def -(other)
if other.lineno == self.lineno
Location.new(@lineno - other.lineno, @column - other.column)
elsif other.column == 0
Location.new(@lineno - other.lineno, @column - other.column)
else
raise "unexpected location #{self.inspect} - #{other.inspect}"
end
end
def forward(buffer, cur, n)
Location.new(@lineno, @column).forward!(buffer, cur, n)
end
def forward!(buffer, cur, n)
buffer[cur, n].scan(/(.*)(\n)?/) do
if Regexp.last_match[2]
@lineno += 1
@column = 0
else
@column += Regexp.last_match[1].length
end
end
self
end
end
}
%header
c -> ${
#include "ruby.h"
#undef __
#define NEW_NODE(t,a0,a1,a2,beg,end) tiny_ruby_parser_newnode((t),(VALUE)(a0),(VALUE)(a1),(VALUE)(a2),beg,end)
typedef struct packcr_location {
size_t lineno;
size_t column;
} packcr_location_t;
typedef packcr_location_t rb_code_position_t;
typedef struct rb_code_location_struct {
rb_code_position_t beg_pos;
rb_code_position_t end_pos;
size_t lineno;
} rb_code_location_t;
enum node_type {
NODE_SCOPE,
NODE_LIT,
NODE_LIST,
NODE_FCALL,
NODE_BLOCK,
NODE_OPCALL,
NODE_CALL,
NODE_QCALL,
NODE_DEFN,
NODE_SELF,
NODE_ARGS_AUX,
NODE_ARGS,
NODE_LASGN,
NODE_BEGIN,
NODE_CLASS,
NODE_COLON2,
NODE_LAST
};
struct tiny_ruby_parser_args_info {
int pre_args_num;
};
typedef struct RNode {
VALUE type;
union {
VALUE value;
struct RNode *node;
} u1;
union {
VALUE value;
struct RNode *node;
long argc;
} u2;
union {
VALUE value;
struct RNode *node;
struct tiny_ruby_parser_args_info *args;
} u3;
rb_code_location_t nd_loc;
int node_id;
int newline;
VALUE var_table;
} NODE;
typedef union {
VALUE value;
NODE *node;
} tiny_ruby_value;
typedef struct {
struct tiny_ruby_parser_context_tag *ctx;
} tiny_ruby_context_ext;
#define nd_head u1.node
#define nd_cpath u1.node
#define nd_vid u1.value
#define nd_mid u2.value
#define nd_end u2.node
#define nd_value u2.node
#define nd_alen u2.argc
#define nd_plen u2.argc
#define nd_args u3.node
#define nd_next u3.node
#define nd_defn u3.node
#define nd_super u3.node
#define nd_ainfo u3.args
}
%source
c -> ${
static const rb_code_location_t NULL_LOC = { {0, -1}, {0, -1} };
VALUE tiny_ruby_parser_cstr2num(const char *str, int base) {
char *ptr;
long v = 0;
for (ptr = (char*)str; *ptr; ptr++) {
const char c = *ptr;
if (c >= '0' && c <= '9') {
v = v * base + c - '0';
} else if (c >= 'a' && c <= 'z') {
v = v * base + c - 'a' + 10;
} else if (c >= 'A' && c <= 'Z') {
v = v * base + c - 'A' + 10;
}
}
return LONG2NUM(v);
}
static int global_node_id = 0;
NODE *tiny_ruby_parser_newnode(enum node_type type, VALUE a0, VALUE a1, VALUE a2, const rb_code_position_t beg_pos, const rb_code_position_t end_pos) {
NODE *node = (NODE*)malloc(sizeof(NODE));
node->type = type;
node->u1.value = a0;
node->u2.value = a1;
node->u3.value = a2;
node->nd_loc.beg_pos = beg_pos;
node->nd_loc.end_pos = end_pos;
node->nd_loc.lineno = beg_pos.lineno;
node->node_id = global_node_id++;
node->newline = 0;
node->var_table = Qnil;
return node;
}
NODE *tiny_ruby_parser_block_append(NODE *head, NODE *tail) {
NODE *h = head, *end;
if (tail == 0) return head;
if (head == 0) return tail;
switch (head->type) {
case NODE_LIT:
return tail;
case NODE_BLOCK:
end = head->nd_end;
break;
default:
h = end = NEW_NODE(NODE_BLOCK, head, 0, 0, head->nd_loc.beg_pos, head->nd_loc.end_pos);
end->var_table = head->var_table;
end->nd_end = end;
head = end;
}
if (tail->type != NODE_BLOCK) {
tail = NEW_NODE(NODE_BLOCK, tail, 0, 0, tail->nd_loc.beg_pos, tail->nd_loc.end_pos);
tail->nd_end = tail;
}
end->nd_next = tail;
h->nd_end = tail->nd_end;
head->nd_loc.end_pos = tail->nd_loc.end_pos;
return head;
}
NODE *tiny_ruby_parser_list_append(NODE *list, NODE *item) {
NODE *last;
if (list == 0) return NEW_NODE(NODE_LIST, item, 1, 0, item->nd_loc.beg_pos, item->nd_loc.end_pos);
if (list->nd_next) {
last = list->nd_next->nd_end;
} else {
last = list;
}
list->nd_alen += 1;
last->nd_next = NEW_NODE(NODE_LIST, item, 1, 0, item->nd_loc.beg_pos, item->nd_loc.end_pos);
list->nd_next->nd_end = last->nd_next;
list->nd_loc.end_pos = item->nd_loc.end_pos;
return list;
}
void tiny_ruby_parser_var_table_add(NODE *node, VALUE sym) {
if (NIL_P(node->var_table)) {
node->var_table = rb_ary_new();
}
rb_ary_push(node->var_table, sym);
}
void tiny_ruby_parser_var_table_pass(NODE *dst, NODE *src) {
if (!src) {
return;
}
if (!NIL_P(src->var_table)) {
if (NIL_P(dst->var_table)) {
dst->var_table = src->var_table;
} else {
rb_ary_concat(dst->var_table, src->var_table);
}
}
src->var_table = Qnil;
}
NODE *tiny_ruby_parser_set_defun_body(NODE *n, NODE *args, NODE *body, const rb_code_position_t beg_pos, const rb_code_position_t end_pos) {
VALUE tbl;
tiny_ruby_parser_var_table_pass(args, body);
tbl = args->var_table;
if (NIL_P(tbl)) {
tbl = rb_ary_new();
}
n->nd_defn = NEW_NODE(NODE_SCOPE, tbl, body, args, beg_pos, end_pos);
args->var_table = Qnil;
n->nd_loc.beg_pos = beg_pos;
n->nd_loc.end_pos = end_pos;
return n;
}
VALUE tiny_ruby_parser_i_inspect(RB_BLOCK_CALL_FUNC_ARGLIST(item, dummy)) {
return rb_inspect(item);
}
void tiny_ruby_parser_dump_node(NODE *node, VALUE indent) {
printf("%s", RSTRING_PTR(indent));
if (node == 0) {
printf("(null node)\n");
return;
}
printf("@ ");
switch(node->type) {
#define nt(t) \
case t: \
printf(#t);\
break
nt(NODE_SCOPE);
nt(NODE_LIT);
nt(NODE_LIST);
nt(NODE_FCALL);
nt(NODE_OPCALL);
nt(NODE_QCALL);
nt(NODE_CALL);
nt(NODE_BLOCK);
nt(NODE_DEFN);
nt(NODE_ARGS);
nt(NODE_LASGN);
nt(NODE_BEGIN);
nt(NODE_CLASS);
nt(NODE_COLON2);
#undef nt
default:
printf("(INVALID TYPE: %ld)", node->type);
}
printf(" (id: %d, line: %ld, location: (%ld,%ld)-(%ld,%ld))",
node->node_id, node->nd_loc.lineno + 1,
node->nd_loc.beg_pos.lineno + 1, node->nd_loc.beg_pos.column,
node->nd_loc.end_pos.lineno + 1, node->nd_loc.end_pos.column
);
if(node->newline) {
printf("*");
}
printf("\n");
switch(node->type) {
case NODE_SCOPE:
printf("%s+- nd_tbl: ", RSTRING_PTR(indent));
fflush(stdout);
if (!node->u1.value || NIL_P(node->u1.value) || RARRAY_LEN(node->u1.value) == 0) {
printf("(empty)\n");
} else {
VALUE strs = rb_block_call(node->u1.value, rb_intern("map"), 0, NULL,tiny_ruby_parser_i_inspect, Qnil);
VALUE str = rb_funcall(strs, rb_intern("join"), 1, rb_str_new_cstr(","));
rb_funcall(rb_mKernel, rb_intern("puts"), 1, str);
rb_funcall(rb_const_get(rb_mKernel, rb_intern("STDOUT")), rb_intern("flush"), 0);
}
printf("%s+- nd_args:\n", RSTRING_PTR(indent));
rb_str_cat(indent, "| ", 4);
tiny_ruby_parser_dump_node(node->nd_args, indent);
rb_str_resize(indent, RSTRING_LEN(indent) - 4);
printf("%s+- nd_body:\n", RSTRING_PTR(indent));
rb_str_cat(indent, " ", 4);
tiny_ruby_parser_dump_node(node->u2.node, indent);
rb_str_resize(indent, RSTRING_LEN(indent) - 4);
break;
case NODE_LIT:
printf("%s+- nd_lit: ", RSTRING_PTR(indent));
fflush(stdout);
rb_p(node->u1.value);
rb_funcall(rb_const_get(rb_mKernel, rb_intern("STDOUT")), rb_intern("flush"), 0);
break;
case NODE_FCALL:
printf("%s+- nd_mid: ", RSTRING_PTR(indent));
fflush(stdout);
rb_p(node->u2.value);
rb_funcall(rb_const_get(rb_mKernel, rb_intern("STDOUT")), rb_intern("flush"), 0);
printf("%s+- nd_args:\n", RSTRING_PTR(indent));
rb_str_cat(indent, " ", 4);
tiny_ruby_parser_dump_node(node->u3.node, indent);
rb_str_resize(indent, RSTRING_LEN(indent) - 4);
break;
case NODE_OPCALL:
case NODE_CALL:
case NODE_QCALL:
printf("%s+- nd_mid: ", RSTRING_PTR(indent));
fflush(stdout);
rb_p(node->u2.value);
rb_funcall(rb_const_get(rb_mKernel, rb_intern("STDOUT")), rb_intern("flush"), 0);
printf("%s+- nd_recv:\n", RSTRING_PTR(indent));
rb_str_cat(indent, "| ", 4);
tiny_ruby_parser_dump_node(node->u1.node, indent);
rb_str_resize(indent, RSTRING_LEN(indent) - 4);
printf("%s+- nd_args:\n", RSTRING_PTR(indent));
rb_str_cat(indent, " ", 4);
tiny_ruby_parser_dump_node(node->u3.node, indent);
rb_str_resize(indent, RSTRING_LEN(indent) - 4);
break;
case NODE_LIST:
printf("%s+- nd_alen: %ld\n", RSTRING_PTR(indent), node->u2.argc);
{
NODE *n;
for (n = node; n; n = n->nd_next) {
printf("%s+- nd_head:\n", RSTRING_PTR(indent));
rb_str_cat(indent, "| ", 4);
tiny_ruby_parser_dump_node(n->u1.node, indent);
rb_str_resize(indent, RSTRING_LEN(indent) - 4);
}
}
printf("%s+- nd_next:\n", RSTRING_PTR(indent));
printf("%s (null node)\n", RSTRING_PTR(indent));
break;
case NODE_BLOCK:
{
NODE *n;
int i;
VALUE child_indent = rb_str_cat(rb_str_dup(indent), "| ", 4);
for (n = node, i = 1; n; i++, n = n->nd_next) {
printf("%s+- nd_head (%d):\n", RSTRING_PTR(indent), i);
if (n == node->nd_end) {
child_indent = rb_str_cat(rb_str_dup(indent), " ", 4);
}
tiny_ruby_parser_dump_node(n->nd_head, child_indent);
}
}
break;
case NODE_DEFN:
printf("%s+- nd_mid: ", RSTRING_PTR(indent));
fflush(stdout);
rb_p(node->u2.value);
rb_funcall(rb_const_get(rb_mKernel, rb_intern("STDOUT")), rb_intern("flush"), 0);
printf("%s+- nd_defn:\n", RSTRING_PTR(indent));
rb_str_cat(indent, " ", 4);
tiny_ruby_parser_dump_node(node->nd_defn, indent);
rb_str_resize(indent, RSTRING_LEN(indent) - 4);
break;
case NODE_ARGS:
printf("%s+- nd_ainfo->pre_args_num: %d\n", RSTRING_PTR(indent), node->nd_ainfo->pre_args_num);
printf("%s+- nd_ainfo->pre_init:\n", RSTRING_PTR(indent));
printf("%s| (null node)\n", RSTRING_PTR(indent));
printf("%s+- nd_ainfo->post_args_num: 0\n", RSTRING_PTR(indent));
printf("%s+- nd_ainfo->post_init:\n", RSTRING_PTR(indent));
printf("%s| (null node)\n", RSTRING_PTR(indent));
printf("%s+- nd_ainfo->first_post_arg: (null)\n", RSTRING_PTR(indent));
printf("%s+- nd_ainfo->rest_arg: (null)\n", RSTRING_PTR(indent));
printf("%s+- nd_ainfo->block_arg: (null)\n", RSTRING_PTR(indent));
printf("%s+- nd_ainfo->opt_args:\n", RSTRING_PTR(indent));
printf("%s| (null node)\n", RSTRING_PTR(indent));
printf("%s+- nd_ainfo->kw_args:\n", RSTRING_PTR(indent));
printf("%s| (null node)\n", RSTRING_PTR(indent));
printf("%s+- nd_ainfo->kw_rest_arg:\n", RSTRING_PTR(indent));
printf("%s (null node)\n", RSTRING_PTR(indent));
break;
case NODE_LASGN:
printf("%s+- nd_vid: ", RSTRING_PTR(indent));
fflush(stdout);
rb_p(node->nd_vid);
rb_funcall(rb_const_get(rb_mKernel, rb_intern("STDOUT")), rb_intern("flush"), 0);
printf("%s+- nd_value:\n", RSTRING_PTR(indent));
rb_str_cat(indent, " ", 4);
tiny_ruby_parser_dump_node(node->nd_value, indent);
rb_str_resize(indent, RSTRING_LEN(indent) - 4);
break;
case NODE_BEGIN:
printf("%s+- nd_body:\n", RSTRING_PTR(indent));
rb_str_cat(indent, " ", 4);
tiny_ruby_parser_dump_node(node->u2.node, indent);
rb_str_resize(indent, RSTRING_LEN(indent) - 4);
break;
case NODE_CLASS:
printf("%s+- nd_cpath:\n", RSTRING_PTR(indent));
rb_str_cat(indent, "| ", 4);
tiny_ruby_parser_dump_node(node->nd_cpath, indent);
rb_str_resize(indent, RSTRING_LEN(indent) - 4);
printf("%s+- nd_super:\n", RSTRING_PTR(indent));
rb_str_cat(indent, "| ", 4);
tiny_ruby_parser_dump_node(node->nd_super, indent);
rb_str_resize(indent, RSTRING_LEN(indent) - 4);
printf("%s+- nd_body:\n", RSTRING_PTR(indent));
rb_str_cat(indent, " ", 4);
tiny_ruby_parser_dump_node(node->u2.node, indent);
rb_str_resize(indent, RSTRING_LEN(indent) - 4);
break;
case NODE_COLON2:
printf("%s+- nd_mid: ", RSTRING_PTR(indent));
fflush(stdout);
rb_p(node->u2.value);
rb_funcall(rb_const_get(rb_mKernel, rb_intern("STDOUT")), rb_intern("flush"), 0);
printf("%s+- nd_head:\n", RSTRING_PTR(indent));
rb_str_cat(indent, " ", 4);
tiny_ruby_parser_dump_node(node->u1.node, indent);
rb_str_resize(indent, RSTRING_LEN(indent) - 4);
}
}
}
rb -> ${
class << TinyRubyParser
def block_append(head, tail)
if !head
return tail
end
head.block_append(tail)
end
def list_append(list, item)
if !list
return ListNode.new(item, item.nd_loc.beg_pos, item.nd_loc.end_pos)
end
if list.nd_next
last = list.nd_next.nd_end
else
last = list
end
list.nd_alen += 1
last.nd_next = ListNode.new(item, item.nd_loc.beg_pos, item.nd_loc.end_pos)
list.nd_next.nd_end = last.nd_next
list.nd_loc.end_pos = item.nd_loc.end_pos
list
end
def set_defun_body(n, args, body, beg_pos, end_pos)
args.var_table_pass(body)
n.nd_defn = ScopeNode.new(args.var_table || [], body, args, beg_pos, end_pos)
args.var_table = nil
n.nd_loc.beg_pos = beg_pos
n.nd_loc.end_pos = end_pos
n
end
end
class CodeLocation
attr_accessor :beg_pos, :end_pos, :lineno
def initialize(beg_pos, end_pos)
@beg_pos = beg_pos
@end_pos = end_pos
@lineno = beg_pos.lineno
end
end
NULL_LOC = CodeLocation.new(Location.new(0, -1), Location.new(0, -1))
class Node
attr_accessor :u1, :u2, :u3, :nd_loc, :node_id, :var_table
attr_accessor :newline
@current_id = 0
class << self
def next_id
id = @current_id
@current_id += 1
id
end
def debug_dump(node, indent)
if !node
puts "#{indent}(null node)"
else
node.debug_dump(indent)
end
end
def node_accessor(name, uname)
alias_method name, uname
alias_method :"#{name}=", :"#{uname}="
end
end
def initialize(u1, u2, u3, beg_pos, end_pos)
@u1 = u1
@u2 = u2
@u3 = u3
@nd_loc = CodeLocation.new(beg_pos, end_pos)
@node_id = Node.next_id
end
def debug_dump(indent)
puts "#{indent}@ #{label} (id: #{@node_id}, line: #{@nd_loc.lineno + 1}, location: (#{@nd_loc.beg_pos.lineno + 1},#{@nd_loc.beg_pos.column})-(#{@nd_loc.end_pos.lineno + 1},#{@nd_loc.end_pos.column}))#{newline ? "*" : ""}"
end
def to_block
block_node = BlockNode.new(self, nd_loc.beg_pos, nd_loc.end_pos)
block_node.var_table = self.var_table
block_node
end
def block_append(tail)
h = e = to_block
e.nd_end = e
head = e
return head if !tail
z = tail
tail = tail.to_block
raise z.inspect if !tail
e.nd_next = tail
h.nd_end = tail.nd_end
head.nd_loc.end_pos = tail.nd_loc.end_pos
head
end
def var_table_add(sym)
@var_table ||= []
@var_table << sym
end
def var_table_pass(src)
return if !src
if src.var_table
if @var_table
@var_table.concat(src.var_table)
else
@var_table = src.var_table
end
end
src.var_table = nil
end
node_accessor :nd_head, :u1
node_accessor :nd_cpath, :u1
node_accessor :nd_tbl, :u1
node_accessor :nd_vid, :u1
node_accessor :nd_alen, :u2
node_accessor :nd_end, :u2
node_accessor :nd_value, :u2
node_accessor :nd_mid, :u2
node_accessor :nd_plen, :u2
node_accessor :nd_next, :u3
node_accessor :nd_args, :u3
node_accessor :nd_defn, :u3
node_accessor :nd_ainfo, :u3
node_accessor :nd_super, :u3
end
class LitNode < Node
def initialize(u1, beg_pos, end_pos)
super(u1, nil, nil, beg_pos, end_pos)
end
def label
"NODE_LIT"
end
def debug_dump(indent)
super
puts "#{indent}+- nd_lit: #{@u1}"
end
def block_append(tail)
tail
end
end
class ScopeNode < Node
def label
"NODE_SCOPE"
end
def debug_dump(indent)
super
print "#{indent}+- nd_tbl: "
if @u1.empty?
puts "(empty)"
else
puts @u1.map(&:inspect).join(",")
end
puts "#{indent}+- nd_args:"
Node.debug_dump(nd_args, indent + "| ")
puts "#{indent}+- nd_body:"
Node.debug_dump(@u2, indent + " ")
end
end
class ListNode < Node
def initialize(u1, beg_pos, end_pos)
super(u1, 1, nil, beg_pos, end_pos)
end
def label
"NODE_LIST"
end
def debug_dump(indent)
super
puts "#{indent}+- nd_alen: #{@u2}"
n = self
begin
puts "#{indent}+- nd_head:"
Node.debug_dump(n.u1, indent + "| ")
n = n.nd_next
end while n
puts "#{indent}+- nd_next:",
"#{indent} (null node)"
end
end
class CallNode < Node
def label
"NODE_CALL"
end
def debug_dump_recv(indent)
puts "#{indent}+- nd_recv:"
Node.debug_dump(@u1, indent + "| ")
end
def debug_dump(indent)
super
puts "#{indent}+- nd_mid: #{u2.inspect}"
debug_dump_recv(indent)
puts "#{indent}+- nd_args:"
Node.debug_dump(@u3, indent + " ")
end
end
class QCallNode < CallNode
def label
"NODE_QCALL"
end
end
class FCallNode < CallNode
def initialize(u2, u3, beg_pos, end_pos)
super(nil, u2, u3, beg_pos, end_pos)
end
def label
"NODE_FCALL"
end
def debug_dump_recv(...)
end
end
class OpCallNode < CallNode
def label
"NODE_OPCALL"
end
end
class BlockNode < Node
def initialize(u1, beg_pos, end_pos)
super(u1, self, nil, beg_pos, end_pos)
end
def label
"NODE_BLOCK"
end
def to_block
self
end
def block_append(tail)
e = nd_end
return self if !tail
tail = tail.to_block
e.nd_next = tail
self.nd_end = tail.nd_end
self.nd_loc.end_pos = tail.nd_loc.end_pos
self
end
def debug_dump(indent)
super
child_indent = indent + "| "
n = self
i = 1
while n
puts "#{indent}+- nd_head (#{i}):"
if n == nd_end
child_indent = indent + " "
end
Node.debug_dump(n.nd_head, child_indent)
i += 1
n = n.nd_next
end
end
end
class SelfNode < Node
end
class DefnNode < Node
def initialize(u2, u3, beg_pos, end_pos)
super(nil, u2, u3, beg_pos, end_pos)
end
def label
"NODE_DEFN"
end
def debug_dump(indent)
super
puts "#{indent}+- nd_mid: #{u2.inspect}"
puts "#{indent}+- nd_defn:"
Node.debug_dump(@u3, indent + " ")
end
end
class ArgsInfo
attr_accessor :pre_args_num
def initialize
@pre_args_num = 0
end
end
class ArgsAuxNode < Node
def initialize(u1, u2, beg_pos, end_pos)
super(u1, u2, nil, beg_pos, end_pos)
end
end
class ArgsNode < Node
def label
"NODE_ARGS"
end
def debug_dump(indent)
super
puts "#{indent}+- nd_ainfo->pre_args_num: #{@u3.pre_args_num}"
puts "#{indent}+- nd_ainfo->pre_init:"
puts "#{indent}| (null node)"
puts "#{indent}+- nd_ainfo->post_args_num: 0"
puts "#{indent}+- nd_ainfo->post_init:"
puts "#{indent}| (null node)"
puts "#{indent}+- nd_ainfo->first_post_arg: (null)"
puts "#{indent}+- nd_ainfo->rest_arg: (null)"
puts "#{indent}+- nd_ainfo->block_arg: (null)"
puts "#{indent}+- nd_ainfo->opt_args:"
puts "#{indent}| (null node)"
puts "#{indent}+- nd_ainfo->kw_args:"
puts "#{indent}| (null node)"
puts "#{indent}+- nd_ainfo->kw_rest_arg:"
puts "#{indent} (null node)"
end
end
class LasgnNode < Node
def initialize(u1, u2, beg_pos, end_pos)
super(u1, u2, nil, beg_pos, end_pos)
end
def label
"NODE_LASGN"
end
def debug_dump(indent)
super
puts "#{indent}+- nd_vid: #{nd_vid.inspect}"
puts "#{indent}+- nd_value:"
Node.debug_dump(nd_value, indent + " ")
end
end
class BeginNode < Node
def label
"NODE_BEGIN"
end
def debug_dump(indent)
super
puts "#{indent}+- nd_body:"
Node.debug_dump(@u2, indent + " ")
end
end
class ClassNode < Node
def label
"NODE_CLASS"
end
def debug_dump(indent)
super
puts "#{indent}+- nd_cpath:"
Node.debug_dump(nd_cpath, indent + "| ")
puts "#{indent}+- nd_super:"
Node.debug_dump(nd_super, indent + "| ")
puts "#{indent}+- nd_body:"
Node.debug_dump(@u2, indent + " ")
end
end
class Colon2Node < Node
def initialize(u1, u2, beg_pos, end_pos)
super(u1, u2, nil, beg_pos, end_pos)
end
def label
"NODE_COLON2"
end
def debug_dump(indent)
super
puts "#{indent}+- nd_mid: #{u2.inspect}"
puts "#{indent}+- nd_head:"
Node.debug_dump(@u1, indent + " ")
end
end
}
%latesource
c -> ${
VALUE parser_run(VALUE self) {
tiny_ruby_context_ext auxil;
tiny_ruby_parser_context_t *ctx = tiny_ruby_parser_create(&auxil);
auxil.ctx = ctx;
while (tiny_ruby_parser_parse(ctx, NULL));
tiny_ruby_parser_destroy(ctx);
return Qnil;
}
void Init_tiny_ruby_parser(void) {
VALUE cTinyRubyParser = rb_define_class("TinyRubyParser", rb_cObject);
rb_define_method(cTinyRubyParser, "run", parser_run, 0);
}
}
rb -> ${
if ENV["EXT"] == "1"
require "mkmf"
create_makefile("tiny_ruby_parser")
system("make", out: :err) || raise
require "tiny_ruby_parser.so"
end
if ARGV[0]
require "stringio"
str = ARGV[0]
$stdin = StringIO.new("#{str}\n", "r")
end
TinyRubyParser.new().run
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment