#include "yarp/extension.h" typedef enum { YP_ISEQ_TYPE_TOP, YP_ISEQ_TYPE_BLOCK } yp_iseq_type_t; typedef enum { YP_RUBY_EVENT_B_CALL, YP_RUBY_EVENT_B_RETURN } yp_ruby_event_t; typedef struct yp_iseq_compiler { // This is the parent compiler. It is used to communicate between ISEQs that // need to be able to jump back to the parent ISEQ. struct yp_iseq_compiler *parent; // This is the list of local variables that are defined on this scope. yp_constant_id_list_t *locals; // This is the instruction sequence that we are compiling. It's actually just // a Ruby array that maps to the output of RubyVM::InstructionSequence#to_a. VALUE insns; // This is a list of IDs coming from the instructions that are being compiled. // In theory they should be deterministic, but we don't have that // functionality yet. Fortunately you can pass -1 for all of them and // everything for the most part continues to work. VALUE node_ids; // This is the current size of the instruction sequence's stack. int stack_size; // This is the maximum size of the instruction sequence's stack. int stack_max; // This is the name of the instruction sequence. const char *name; // This is the type of the instruction sequence. yp_iseq_type_t type; // This is the optional argument information. VALUE optionals; // This is the number of arguments. int arg_size; // This is the current size of the instruction sequence's instructions and // operands. size_t size; // This is the index of the current inline storage. size_t inline_storage_index; } yp_iseq_compiler_t; static void yp_iseq_compiler_init(yp_iseq_compiler_t *compiler, yp_iseq_compiler_t *parent, yp_constant_id_list_t *locals, const char *name, yp_iseq_type_t type) { *compiler = (yp_iseq_compiler_t) { .parent = parent, .locals = locals, .insns = rb_ary_new(), .node_ids = rb_ary_new(), .stack_size = 0, .stack_max = 0, .name = name, .type = type, .optionals = rb_hash_new(), .arg_size = 0, .size = 0, .inline_storage_index = 0 }; } /******************************************************************************/ /* Utilities */ /******************************************************************************/ static inline int sizet2int(size_t value) { if (value > INT_MAX) rb_raise(rb_eRuntimeError, "value too large"); return (int) value; } static int local_index(yp_iseq_compiler_t *compiler, yp_constant_id_t constant_id, int depth) { int compiler_index; yp_iseq_compiler_t *local_compiler = compiler; for (compiler_index = 0; compiler_index < depth; compiler_index++) { local_compiler = local_compiler->parent; assert(local_compiler != NULL); } size_t index; for (index = 0; index < local_compiler->locals->size; index++) { if (local_compiler->locals->ids[index] == constant_id) { return sizet2int(local_compiler->locals->size - index + 2); } } return -1; } /******************************************************************************/ /* Parse specific VALUEs from strings */ /******************************************************************************/ static VALUE parse_number(const char *start, const char *end) { size_t length = end - start; char *buffer = alloca(length + 1); memcpy(buffer, start, length); buffer[length] = '\0'; return rb_cstr_to_inum(buffer, -10, Qfalse); } static inline VALUE parse_string(yp_string_t *string) { return rb_str_new(yp_string_source(string), yp_string_length(string)); } static inline ID parse_symbol(const char *start, const char *end) { return rb_intern2(start, end - start); } static inline ID parse_location_symbol(yp_location_t *location) { return parse_symbol(location->start, location->end); } static inline ID parse_node_symbol(yp_node_t *node) { return parse_symbol(node->location.start, node->location.end); } static inline ID parse_string_symbol(yp_string_t *string) { const char *start = yp_string_source(string); return parse_symbol(start, start + yp_string_length(string)); } /******************************************************************************/ /* Create Ruby objects for compilation */ /******************************************************************************/ static VALUE yp_iseq_new(yp_iseq_compiler_t *compiler) { VALUE code_location = rb_ary_new_capa(4); rb_ary_push(code_location, INT2FIX(1)); rb_ary_push(code_location, INT2FIX(0)); rb_ary_push(code_location, INT2FIX(1)); rb_ary_push(code_location, INT2FIX(0)); VALUE data = rb_hash_new(); rb_hash_aset(data, ID2SYM(rb_intern("arg_size")), INT2FIX(compiler->arg_size)); rb_hash_aset(data, ID2SYM(rb_intern("local_size")), INT2FIX(0)); rb_hash_aset(data, ID2SYM(rb_intern("stack_max")), INT2FIX(compiler->stack_max)); rb_hash_aset(data, ID2SYM(rb_intern("node_id")), INT2FIX(-1)); rb_hash_aset(data, ID2SYM(rb_intern("code_location")), code_location); rb_hash_aset(data, ID2SYM(rb_intern("node_ids")), compiler->node_ids); VALUE type = Qnil; switch (compiler->type) { case YP_ISEQ_TYPE_TOP: type = ID2SYM(rb_intern("top")); break; case YP_ISEQ_TYPE_BLOCK: type = ID2SYM(rb_intern("block")); break; } VALUE iseq = rb_ary_new_capa(13); rb_ary_push(iseq, rb_str_new_cstr("YARVInstructionSequence/SimpleDataFormat")); rb_ary_push(iseq, INT2FIX(3)); rb_ary_push(iseq, INT2FIX(3)); rb_ary_push(iseq, INT2FIX(1)); rb_ary_push(iseq, data); rb_ary_push(iseq, rb_str_new_cstr(compiler->name)); rb_ary_push(iseq, rb_str_new_cstr("")); rb_ary_push(iseq, rb_str_new_cstr("")); rb_ary_push(iseq, INT2FIX(1)); rb_ary_push(iseq, type); rb_ary_push(iseq, rb_ary_new()); rb_ary_push(iseq, compiler->optionals); rb_ary_push(iseq, rb_ary_new()); rb_ary_push(iseq, compiler->insns); return iseq; } // static const int YP_CALLDATA_ARGS_SPLAT = 1 << 0; // static const int YP_CALLDATA_ARGS_BLOCKARG = 1 << 1; static const int YP_CALLDATA_FCALL = 1 << 2; static const int YP_CALLDATA_VCALL = 1 << 3; static const int YP_CALLDATA_ARGS_SIMPLE = 1 << 4; // static const int YP_CALLDATA_BLOCKISEQ = 1 << 5; // static const int YP_CALLDATA_KWARG = 1 << 6; // static const int YP_CALLDATA_KW_SPLAT = 1 << 7; // static const int YP_CALLDATA_TAILCALL = 1 << 8; // static const int YP_CALLDATA_SUPER = 1 << 9; // static const int YP_CALLDATA_ZSUPER = 1 << 10; // static const int YP_CALLDATA_OPT_SEND = 1 << 11; // static const int YP_CALLDATA_KW_SPLAT_MUT = 1 << 12; static VALUE yp_calldata_new(ID mid, int flag, size_t orig_argc) { VALUE calldata = rb_hash_new(); rb_hash_aset(calldata, ID2SYM(rb_intern("mid")), ID2SYM(mid)); rb_hash_aset(calldata, ID2SYM(rb_intern("flag")), INT2FIX(flag)); rb_hash_aset(calldata, ID2SYM(rb_intern("orig_argc")), INT2FIX(orig_argc)); return calldata; } static inline VALUE yp_inline_storage_new(yp_iseq_compiler_t *compiler) { return INT2FIX(compiler->inline_storage_index++); } /******************************************************************************/ /* Push instructions onto a compiler */ /******************************************************************************/ static VALUE push_insn(yp_iseq_compiler_t *compiler, int stack_change, size_t size, ...) { va_list opnds; va_start(opnds, size); VALUE insn = rb_ary_new_capa(size); for (size_t index = 0; index < size; index++) { rb_ary_push(insn, va_arg(opnds, VALUE)); } va_end(opnds); compiler->stack_size += stack_change; if (compiler->stack_size > compiler->stack_max) { compiler->stack_max = compiler->stack_size; } compiler->size += size; rb_ary_push(compiler->insns, insn); rb_ary_push(compiler->node_ids, INT2FIX(-1)); return insn; } static VALUE push_label(yp_iseq_compiler_t *compiler) { VALUE label = ID2SYM(rb_intern_str(rb_sprintf("label_%zu", compiler->size))); rb_ary_push(compiler->insns, label); return label; } static void push_ruby_event(yp_iseq_compiler_t *compiler, yp_ruby_event_t event) { switch (event) { case YP_RUBY_EVENT_B_CALL: rb_ary_push(compiler->insns, ID2SYM(rb_intern("RUBY_EVENT_B_CALL"))); break; case YP_RUBY_EVENT_B_RETURN: rb_ary_push(compiler->insns, ID2SYM(rb_intern("RUBY_EVENT_B_RETURN"))); break; } } static inline VALUE push_anytostring(yp_iseq_compiler_t *compiler) { return push_insn(compiler, -2 + 1, 1, ID2SYM(rb_intern("anytostring"))); } static inline VALUE push_branchif(yp_iseq_compiler_t *compiler, VALUE label) { return push_insn(compiler, -1 + 0, 2, ID2SYM(rb_intern("branchif")), label); } static inline VALUE push_branchunless(yp_iseq_compiler_t *compiler, VALUE label) { return push_insn(compiler, -1 + 0, 2, ID2SYM(rb_intern("branchunless")), label); } static inline VALUE push_concatstrings(yp_iseq_compiler_t *compiler, int count) { return push_insn(compiler, -count + 1, 2, ID2SYM(rb_intern("concatstrings")), INT2FIX(count)); } static inline VALUE push_dup(yp_iseq_compiler_t *compiler) { return push_insn(compiler, -1 + 2, 1, ID2SYM(rb_intern("dup"))); } static inline VALUE push_getclassvariable(yp_iseq_compiler_t *compiler, VALUE name, VALUE inline_storage) { return push_insn(compiler, -0 + 1, 3, ID2SYM(rb_intern("getclassvariable")), name, inline_storage); } static inline VALUE push_getconstant(yp_iseq_compiler_t *compiler, VALUE name) { return push_insn(compiler, -2 + 1, 2, ID2SYM(rb_intern("getconstant")), name); } static inline VALUE push_getglobal(yp_iseq_compiler_t *compiler, VALUE name) { return push_insn(compiler, -0 + 1, 2, ID2SYM(rb_intern("getglobal")), name); } static inline VALUE push_getinstancevariable(yp_iseq_compiler_t *compiler, VALUE name, VALUE inline_storage) { return push_insn(compiler, -0 + 1, 3, ID2SYM(rb_intern("getinstancevariable")), name, inline_storage); } static inline VALUE push_getlocal(yp_iseq_compiler_t *compiler, VALUE index, VALUE depth) { return push_insn(compiler, -0 + 1, 3, ID2SYM(rb_intern("getlocal")), index, depth); } static inline VALUE push_leave(yp_iseq_compiler_t *compiler) { return push_insn(compiler, -1 + 0, 1, ID2SYM(rb_intern("leave"))); } static inline VALUE push_newarray(yp_iseq_compiler_t *compiler, int count) { return push_insn(compiler, -count + 1, 2, ID2SYM(rb_intern("newarray")), INT2FIX(count)); } static inline VALUE push_newhash(yp_iseq_compiler_t *compiler, int count) { return push_insn(compiler, -count + 1, 2, ID2SYM(rb_intern("newhash")), INT2FIX(count)); } static inline VALUE push_newrange(yp_iseq_compiler_t *compiler, VALUE flag) { return push_insn(compiler, -2 + 1, 2, ID2SYM(rb_intern("newrange")), flag); } static inline VALUE push_nop(yp_iseq_compiler_t *compiler) { return push_insn(compiler, -2 + 1, 1, ID2SYM(rb_intern("nop"))); } static inline VALUE push_objtostring(yp_iseq_compiler_t *compiler, VALUE calldata) { return push_insn(compiler, -1 + 1, 2, ID2SYM(rb_intern("objtostring")), calldata); } static inline VALUE push_pop(yp_iseq_compiler_t *compiler) { return push_insn(compiler, -1 + 0, 1, ID2SYM(rb_intern("pop"))); } static inline VALUE push_putnil(yp_iseq_compiler_t *compiler) { return push_insn(compiler, -0 + 1, 1, ID2SYM(rb_intern("putnil"))); } static inline VALUE push_putobject(yp_iseq_compiler_t *compiler, VALUE value) { return push_insn(compiler, -0 + 1, 2, ID2SYM(rb_intern("putobject")), value); } static inline VALUE push_putself(yp_iseq_compiler_t *compiler) { return push_insn(compiler, -0 + 1, 1, ID2SYM(rb_intern("putself"))); } static inline VALUE push_setlocal(yp_iseq_compiler_t *compiler, VALUE index, VALUE depth) { return push_insn(compiler, -1 + 0, 3, ID2SYM(rb_intern("setlocal")), index, depth); } static const VALUE YP_SPECIALOBJECT_VMCORE = INT2FIX(1); static const VALUE YP_SPECIALOBJECT_CBASE = INT2FIX(2); // static const VALUE YP_SPECIALOBJECT_CONST_BASE = INT2FIX(3); static inline VALUE push_putspecialobject(yp_iseq_compiler_t *compiler, VALUE object) { return push_insn(compiler, -0 + 1, 2, ID2SYM(rb_intern("putspecialobject")), object); } static inline VALUE push_putstring(yp_iseq_compiler_t *compiler, VALUE string) { return push_insn(compiler, -0 + 1, 2, ID2SYM(rb_intern("putstring")), string); } static inline VALUE push_send(yp_iseq_compiler_t *compiler, int stack_change, VALUE calldata, VALUE block_iseq) { return push_insn(compiler, stack_change, 3, ID2SYM(rb_intern("send")), calldata, block_iseq); } static inline VALUE push_setclassvariable(yp_iseq_compiler_t *compiler, VALUE name, VALUE inline_storage) { return push_insn(compiler, -1 + 0, 3, ID2SYM(rb_intern("setclassvariable")), name, inline_storage); } static inline VALUE push_setglobal(yp_iseq_compiler_t *compiler, VALUE name) { return push_insn(compiler, -1 + 0, 2, ID2SYM(rb_intern("setglobal")), name); } static inline VALUE push_setinstancevariable(yp_iseq_compiler_t *compiler, VALUE name, VALUE inline_storage) { return push_insn(compiler, -1 + 0, 3, ID2SYM(rb_intern("setinstancevariable")), name, inline_storage); } /******************************************************************************/ /* Compile an AST node using the given compiler */ /******************************************************************************/ static void yp_compile_node(yp_iseq_compiler_t *compiler, yp_node_t *base_node) { switch (base_node->type) { case YP_NODE_ALIAS_NODE: { yp_alias_node_t *node = (yp_alias_node_t *) base_node; push_putspecialobject(compiler, YP_SPECIALOBJECT_VMCORE); push_putspecialobject(compiler, YP_SPECIALOBJECT_CBASE); yp_compile_node(compiler, node->new_name); yp_compile_node(compiler, node->old_name); push_send(compiler, -3, yp_calldata_new(rb_intern("core#set_method_alias"), YP_CALLDATA_ARGS_SIMPLE, 3), Qnil); return; } case YP_NODE_AND_NODE: { yp_and_node_t *node = (yp_and_node_t *) base_node; yp_compile_node(compiler, node->left); push_dup(compiler); VALUE branchunless = push_branchunless(compiler, Qnil); push_pop(compiler); yp_compile_node(compiler, node->right); VALUE label = push_label(compiler); rb_ary_store(branchunless, 1, label); return; } case YP_NODE_ARGUMENTS_NODE: { yp_arguments_node_t *node = (yp_arguments_node_t *) base_node; yp_node_list_t node_list = node->arguments; for (size_t index = 0; index < node_list.size; index++) { yp_compile_node(compiler, node_list.nodes[index]); } return; } case YP_NODE_ARRAY_NODE: { yp_array_node_t *node = (yp_array_node_t *) base_node; yp_node_list_t elements = node->elements; for (size_t index = 0; index < elements.size; index++) { yp_compile_node(compiler, elements.nodes[index]); } push_newarray(compiler, sizet2int(elements.size)); return; } case YP_NODE_ASSOC_NODE: { yp_assoc_node_t *node = (yp_assoc_node_t *) base_node; yp_compile_node(compiler, node->key); yp_compile_node(compiler, node->value); return; } case YP_NODE_BLOCK_NODE: { yp_block_node_t *node = (yp_block_node_t *) base_node; VALUE optional_labels = rb_ary_new(); if (node->parameters && node->parameters->parameters && node->parameters->parameters->optionals.size > 0) { compiler->arg_size += node->parameters->parameters->optionals.size; yp_node_list_t *optionals = &node->parameters->parameters->optionals; for (size_t i = 0; i < optionals->size; i++) { VALUE label = push_label(compiler); rb_ary_push(optional_labels, label); yp_compile_node(compiler, optionals->nodes[i]); } VALUE label = push_label(compiler); rb_ary_push(optional_labels, label); rb_hash_aset(compiler->optionals, ID2SYM(rb_intern("opt")), optional_labels); push_ruby_event(compiler, YP_RUBY_EVENT_B_CALL); push_nop(compiler); } else { push_ruby_event(compiler, YP_RUBY_EVENT_B_CALL); } if (node->statements) { yp_compile_node(compiler, node->statements); } else { push_putnil(compiler); } push_ruby_event(compiler, YP_RUBY_EVENT_B_RETURN); push_leave(compiler); return; } case YP_NODE_CALL_NODE: { yp_call_node_t *node = (yp_call_node_t *) base_node; ID mid = parse_location_symbol(&node->message_loc); int flags = 0; size_t orig_argc; if (node->receiver == NULL) { push_putself(compiler); } else { yp_compile_node(compiler, node->receiver); } if (node->arguments == NULL) { if (flags & YP_CALLDATA_FCALL) flags |= YP_CALLDATA_VCALL; orig_argc = 0; } else { yp_arguments_node_t *arguments = node->arguments; yp_compile_node(compiler, (yp_node_t *) arguments); orig_argc = arguments->arguments.size; } VALUE block_iseq = Qnil; if (node->block != NULL) { yp_iseq_compiler_t block_compiler; yp_iseq_compiler_init( &block_compiler, compiler, &node->block->locals, "block in ", YP_ISEQ_TYPE_BLOCK ); yp_compile_node(&block_compiler, (yp_node_t *) node->block); block_iseq = yp_iseq_new(&block_compiler); } if (block_iseq == Qnil && flags == 0) { flags |= YP_CALLDATA_ARGS_SIMPLE; } if (node->receiver == NULL) { flags |= YP_CALLDATA_FCALL; if (block_iseq == Qnil && node->arguments == NULL) { flags |= YP_CALLDATA_VCALL; } } push_send(compiler, -sizet2int(orig_argc), yp_calldata_new(mid, flags, orig_argc), block_iseq); return; } case YP_NODE_CLASS_VARIABLE_READ_NODE: { yp_class_variable_read_node_t *node = (yp_class_variable_read_node_t *) base_node; push_getclassvariable(compiler, ID2SYM(parse_node_symbol((yp_node_t *) node)), yp_inline_storage_new(compiler)); return; } case YP_NODE_CLASS_VARIABLE_WRITE_NODE: { yp_class_variable_write_node_t *node = (yp_class_variable_write_node_t *) base_node; if (node->value == NULL) { rb_raise(rb_eNotImpError, "class variable write without value not implemented"); } yp_compile_node(compiler, node->value); push_dup(compiler); push_setclassvariable(compiler, ID2SYM(parse_location_symbol(&node->name_loc)), yp_inline_storage_new(compiler)); return; } case YP_NODE_CONSTANT_PATH_NODE: { yp_constant_path_node_t *node = (yp_constant_path_node_t *) base_node; yp_compile_node(compiler, node->parent); push_putobject(compiler, Qfalse); push_getconstant(compiler, ID2SYM(parse_node_symbol((yp_node_t *) node->child))); return; } case YP_NODE_CONSTANT_READ_NODE: push_putnil(compiler); push_putobject(compiler, Qtrue); push_getconstant(compiler, ID2SYM(parse_node_symbol((yp_node_t *) base_node))); return; case YP_NODE_EMBEDDED_STATEMENTS_NODE: { yp_embedded_statements_node_t *node = (yp_embedded_statements_node_t *) base_node; yp_compile_node(compiler, (yp_node_t *) node->statements); return; } case YP_NODE_FALSE_NODE: push_putobject(compiler, Qfalse); return; case YP_NODE_GLOBAL_VARIABLE_READ_NODE: push_getglobal(compiler, ID2SYM(parse_location_symbol(&base_node->location))); return; case YP_NODE_GLOBAL_VARIABLE_WRITE_NODE: { yp_global_variable_write_node_t *node = (yp_global_variable_write_node_t *) base_node; if (node->value == NULL) { rb_raise(rb_eNotImpError, "global variable write without value not implemented"); } yp_compile_node(compiler, node->value); push_dup(compiler); push_setglobal(compiler, ID2SYM(parse_location_symbol(&node->name_loc))); return; } case YP_NODE_HASH_NODE: { yp_hash_node_t *node = (yp_hash_node_t *) base_node; yp_node_list_t elements = node->elements; for (size_t index = 0; index < elements.size; index++) { yp_compile_node(compiler, elements.nodes[index]); } push_newhash(compiler, sizet2int(elements.size * 2)); return; } case YP_NODE_INSTANCE_VARIABLE_READ_NODE: push_getinstancevariable(compiler, ID2SYM(parse_node_symbol((yp_node_t *) base_node)), yp_inline_storage_new(compiler)); return; case YP_NODE_INSTANCE_VARIABLE_WRITE_NODE: { yp_instance_variable_write_node_t *node = (yp_instance_variable_write_node_t *) base_node; if (node->value == NULL) { rb_raise(rb_eNotImpError, "instance variable write without value not implemented"); } yp_compile_node(compiler, node->value); push_dup(compiler); push_setinstancevariable(compiler, ID2SYM(parse_location_symbol(&node->name_loc)), yp_inline_storage_new(compiler)); return; } case YP_NODE_INTEGER_NODE: push_putobject(compiler, parse_number(base_node->location.start, base_node->location.end)); return; case YP_NODE_INTERPOLATED_STRING_NODE: { yp_interpolated_string_node_t *node = (yp_interpolated_string_node_t *) base_node; for (size_t index = 0; index < node->parts.size; index++) { yp_node_t *part = node->parts.nodes[index]; switch (part->type) { case YP_NODE_STRING_NODE: { yp_string_node_t *string_node = (yp_string_node_t *) part; push_putobject(compiler, parse_string(&string_node->unescaped)); break; } default: yp_compile_node(compiler, part); push_dup(compiler); push_objtostring(compiler, yp_calldata_new(rb_intern("to_s"), YP_CALLDATA_FCALL | YP_CALLDATA_ARGS_SIMPLE, 0)); push_anytostring(compiler); break; } } push_concatstrings(compiler, sizet2int(node->parts.size)); return; } case YP_NODE_KEYWORD_HASH_NODE: { yp_keyword_hash_node_t *node = (yp_keyword_hash_node_t *) base_node; yp_node_list_t elements = node->elements; for (size_t index = 0; index < elements.size; index++) { yp_compile_node(compiler, elements.nodes[index]); } push_newhash(compiler, sizet2int(elements.size * 2)); return; } case YP_NODE_LOCAL_VARIABLE_READ_NODE: { yp_local_variable_read_node_t *node = (yp_local_variable_read_node_t *) base_node; int index = local_index(compiler, node->constant_id, node->depth); push_getlocal(compiler, INT2FIX(index), INT2FIX(node->depth)); return; } case YP_NODE_LOCAL_VARIABLE_WRITE_NODE: { yp_local_variable_write_node_t *node = (yp_local_variable_write_node_t *) base_node; if (node->value == NULL) { rb_raise(rb_eNotImpError, "local variable write without value not implemented"); } int index = local_index(compiler, node->constant_id, node->depth); yp_compile_node(compiler, node->value); push_dup(compiler); push_setlocal(compiler, INT2FIX(index), INT2FIX(node->depth)); return; } case YP_NODE_NIL_NODE: push_putnil(compiler); return; case YP_NODE_OR_NODE: { yp_or_node_t *node = (yp_or_node_t *) base_node; yp_compile_node(compiler, node->left); push_dup(compiler); VALUE branchif = push_branchif(compiler, Qnil); push_pop(compiler); yp_compile_node(compiler, node->right); VALUE label = push_label(compiler); rb_ary_store(branchif, 1, label); return; } case YP_NODE_PARENTHESES_NODE: { yp_parentheses_node_t *node = (yp_parentheses_node_t *) base_node; if (node->statements == NULL) { push_putnil(compiler); } else { yp_compile_node(compiler, node->statements); } return; } case YP_NODE_PROGRAM_NODE: { yp_program_node_t *node = (yp_program_node_t *) base_node; if (node->statements->body.size == 0) { push_putnil(compiler); } else { yp_compile_node(compiler, (yp_node_t *) node->statements); } push_leave(compiler); return; } case YP_NODE_RANGE_NODE: { yp_range_node_t *node = (yp_range_node_t *) base_node; if (node->left == NULL) { push_putnil(compiler); } else { yp_compile_node(compiler, node->left); } if (node->right == NULL) { push_putnil(compiler); } else { yp_compile_node(compiler, node->right); } push_newrange(compiler, INT2FIX((node->operator_loc.end - node->operator_loc.start) == 3)); return; } case YP_NODE_SELF_NODE: push_putself(compiler); return; case YP_NODE_STATEMENTS_NODE: { yp_statements_node_t *node = (yp_statements_node_t *) base_node; yp_node_list_t node_list = node->body; for (size_t index = 0; index < node_list.size; index++) { yp_compile_node(compiler, node_list.nodes[index]); if (index < node_list.size - 1) push_pop(compiler); } return; } case YP_NODE_STRING_NODE: { yp_string_node_t *node = (yp_string_node_t *) base_node; push_putstring(compiler, parse_string(&node->unescaped)); return; } case YP_NODE_SYMBOL_NODE: { yp_symbol_node_t *node = (yp_symbol_node_t *) base_node; push_putobject(compiler, ID2SYM(parse_string_symbol(&node->unescaped))); return; } case YP_NODE_TRUE_NODE: push_putobject(compiler, Qtrue); return; case YP_NODE_UNDEF_NODE: { yp_undef_node_t *node = (yp_undef_node_t *) base_node; for (size_t index = 0; index < node->names.size; index++) { push_putspecialobject(compiler, YP_SPECIALOBJECT_VMCORE); push_putspecialobject(compiler, YP_SPECIALOBJECT_CBASE); yp_compile_node(compiler, node->names.nodes[index]); push_send(compiler, -2, yp_calldata_new(rb_intern("core#undef_method"), YP_CALLDATA_ARGS_SIMPLE, 2), Qnil); if (index < node->names.size - 1) push_pop(compiler); } return; } case YP_NODE_X_STRING_NODE: { yp_x_string_node_t *node = (yp_x_string_node_t *) base_node; push_putself(compiler); push_putobject(compiler, parse_string(&node->unescaped)); push_send(compiler, -1, yp_calldata_new(rb_intern("`"), YP_CALLDATA_FCALL | YP_CALLDATA_ARGS_SIMPLE, 1), Qnil); return; } case YP_NODE_OPTIONAL_PARAMETER_NODE: { yp_optional_parameter_node_t *node = (yp_optional_parameter_node_t *) base_node; int depth = 0; int index = local_index(compiler, node->constant_id, depth); yp_compile_node(compiler, node->value); push_setlocal(compiler, INT2FIX(index), INT2FIX(depth)); break; } default: rb_raise(rb_eNotImpError, "node type %d not implemented", base_node->type); return; } } // This function compiles the given node into a list of instructions. VALUE yp_compile(yp_node_t *node) { assert(node->type == YP_NODE_PROGRAM_NODE); yp_iseq_compiler_t compiler; yp_iseq_compiler_init( &compiler, NULL, &((yp_program_node_t *) node)->locals, "", YP_ISEQ_TYPE_TOP ); yp_compile_node(&compiler, node); return yp_iseq_new(&compiler); }