Пример #1
0
astring *astring_cpych(astring *dst, const char *src, int count)
{
	/* make room; if we fail or if dst is the dummy, do nothing */
	if (!ensure_room(dst, count))
		return dst;

	/* copy the raw data and NULL-terminate */
	if (count > 0)
		memcpy(dst->text, src, count);
	dst->text[count] = 0;
	return dst;
}
Пример #2
0
astring &astring::cpy(const char *src, int count)
{
    // make room; if we fail or if we are the dummy, do nothing
    if (!ensure_room(count))
        return *this;

    // copy the raw data and NULL-terminate
    if (count > 0 && m_text != src)
        memcpy(m_text, src, count);
    m_text[count] = 0;
    return *this;
}
Пример #3
0
astring &astring::ins(int insbefore, const char *src, int count)
{
    // make room; if we fail or if we are the dummy, do nothing
    int dstlength = len();
    if (!ensure_room(dstlength + count))
        return *this;

    // adjust insbefore to be logical
    if (insbefore < 0 || insbefore > dstlength)
        insbefore = dstlength;

    // copy the data an NULL-terminate
    if (insbefore < dstlength)
        memmove(m_text + insbefore + count, m_text + insbefore, dstlength - insbefore);
    memcpy(m_text + insbefore, src, count);
    m_text[dstlength + count] = 0;
    return *this;
}
Пример #4
0
astring *astring_insch(astring *dst, int insbefore, const char *src, int count)
{
	int dstlength = strlen(dst->text);

	/* make room; if we fail or if dst is the dummy, do nothing */
	if (!ensure_room(dst, dstlength + count))
		return dst;

	/* adjust insbefore to be logical */
	if (insbefore < 0 || insbefore > dstlength)
		insbefore = dstlength;

	/* copy the data an NULL-terminate */
	if (insbefore < dstlength)
		memmove(dst->text + insbefore + count, dst->text + insbefore, dstlength - insbefore);
	memcpy(dst->text + insbefore, src, count);
	dst->text[dstlength + count] = 0;
	return dst;
}
Пример #5
0
void astring_expand(astring *str, int length)
{
    ensure_room(str, length);
}
Пример #6
0
Файл: compile.c Проект: xiaq/ngs
void compile_main_section(COMPILATION_CONTEXT *ctx, ast_node *node, char **buf, size_t *idx, size_t *allocated, int need_result) {
	ast_node *ptr;
	int argc, have_arr_splat, have_hash_splat, params_flags;
	int doing_named_args = 0;
	LOCAL_VAR_INDEX n_locals, n_params_required, n_params_optional;
	UPVAR_INDEX n_uplevels;
	size_t loop_beg_idx, cond_jump, continue_target_idx, func_jump, end_of_func_idx, if_jump, while_jump;
	int old_break_addrs_ptr, old_continue_addrs_ptr, i, saved_stack_depth;

	ensure_room(buf, *idx, allocated, 1024); // XXX - magic number

	// printf("compile_main_section() node=%p type=%s last_child=%p need_result=%d\n", node, NGS_AST_NODE_TYPES_NAMES[node->type], node->last_child, need_result);
	if(node->location.first_line) {
		source_tracking_entry *ste = NULL;
		// printf("LOC: ip=%lu\n %d:%d %d:%d\n", *idx, node->location.first_line, node->location.first_column, node->location.last_line, node->location.last_column);
		if(ctx->source_tracking_entries_count) {
			if(ctx->source_tracking_entries[ctx->source_tracking_entries_count-1].ip == *idx) {
				// Override because deeper ast nodes have more precise location
				ste = &ctx->source_tracking_entries[ctx->source_tracking_entries_count-1];
			} else {
				ste = NULL;
			}
		}
		if(!ste) {
			if (ctx->source_tracking_entries_count == ctx->source_tracking_entries_allocated) {
				ctx->source_tracking_entries_allocated *= 2;
				ctx->source_tracking_entries = NGS_REALLOC(ctx->source_tracking_entries, ctx->source_tracking_entries_allocated * sizeof(source_tracking_entry));
			}
			ste = &ctx->source_tracking_entries[ctx->source_tracking_entries_count++];
		}
		ste->source_file_name_idx = 0; // XXX: currently only one source file per compile() is supported
		ste->ip = *idx;
		ste->source_location[0] = node->location.first_line;
		ste->source_location[1] = node->location.first_column;
		ste->source_location[2] = node->location.last_line;
		ste->source_location[3] = node->location.last_column;
	}
	switch(node->type) {
		case CALL_NODE:
			DEBUG_COMPILER("COMPILER: %s %zu\n", "CALL NODE", *idx);
			OPCODE(*buf, OP_PUSH_NULL); // Placeholder for return value
			saved_stack_depth = STACK_DEPTH;
			STACK_DEPTH++;
			// print_ast(node, 0);
			assert(node->first_child->next_sibling->type == ARGS_NODE);
			for(ptr=node->first_child->next_sibling->first_child, have_arr_splat=0, have_hash_splat=0; ptr; ptr=ptr->next_sibling) {
				assert(ptr->type == ARG_NODE);
				if(ptr->first_child->type == ARR_SPLAT_NODE) {
					have_arr_splat = 1;
				}
				if(ptr->first_child->type == HASH_SPLAT_NODE) {
					have_hash_splat = 1;
				}
			}
			if(have_arr_splat) {
				OPCODE(*buf, OP_PUSH_INT); DATA_INT(*buf, 0);
				OPCODE(*buf, OP_MAKE_ARR);
				STACK_DEPTH++;
			}
			doing_named_args = 0;
			argc = 0;
			if(node->first_child->type == ATTR_NODE) {
				compile_main_section(ctx, node->first_child->first_child, buf, idx, allocated, NEED_RESULT);
				if(have_arr_splat) {
					OPCODE(*buf, OP_ARR_APPEND);
				}
				argc++;
			}
			for(ptr=node->first_child->next_sibling->first_child; ptr; ptr=ptr->next_sibling) {
				assert(ptr->type == ARG_NODE);
				if(ptr->first_child->next_sibling) {
					// Got named argument
					if(!doing_named_args) {
						// Setup named arguments
						doing_named_args = 1;
						// TODO: maybe special opcode for creating an empty hash?
						OPCODE(*buf, OP_PUSH_INT); DATA_INT(*buf, 0);
						OPCODE(*buf, OP_MAKE_HASH);
						STACK_DEPTH++;
						argc++;
					}
					// argument name
					compile_main_section(ctx, ptr->first_child->next_sibling, buf, idx, allocated, NEED_RESULT);
					// argument value
					compile_main_section(ctx, ptr->first_child, buf, idx, allocated, NEED_RESULT);
					OPCODE(*buf, OP_HASH_SET);
					continue;
				}
				if(ptr->first_child->type == ARR_SPLAT_NODE) {
					assert(!doing_named_args);
					compile_main_section(ctx, ptr->first_child->first_child, buf, idx, allocated, NEED_RESULT);
					OPCODE(*buf, OP_TO_ARR);
					OPCODE(*buf, OP_ARR_CONCAT);
					continue;
				}
				if(ptr->first_child->type == HASH_SPLAT_NODE) {
					if(!doing_named_args) {
						// Setup named arguments
						doing_named_args = 1;
						// TODO: maybe special opcode for creating an empty hash?
						OPCODE(*buf, OP_PUSH_INT); DATA_INT(*buf, 0);
						OPCODE(*buf, OP_MAKE_HASH);
						argc++;
					}
					compile_main_section(ctx, ptr->first_child->first_child, buf, idx, allocated, NEED_RESULT);
					OPCODE(*buf, OP_HASH_UPDATE);
					continue;
				}
				assert(!doing_named_args);
				compile_main_section(ctx, ptr->first_child, buf, idx, allocated, NEED_RESULT);
				STACK_DEPTH++;
				argc++;
				if(have_arr_splat) {
					OPCODE(*buf, ptr->first_child->type == ARR_SPLAT_NODE ? OP_ARR_CONCAT : OP_ARR_APPEND);
				}
			}
			if(doing_named_args) {
				// Marker at the end
				OPCODE(*buf, OP_PUSH_KWARGS_MARKER);
				argc++;
				if(have_arr_splat) {
					OPCODE(*buf, OP_ARR_APPEND);
				}
			}
			if(!have_arr_splat) {
				assert(argc <= MAX_ARGS); // TODO: Exception
				OPCODE(*buf, OP_PUSH_INT); DATA(*buf, argc);
				STACK_DEPTH++;
			}
			if(node->first_child->type == ATTR_NODE) {
				// printf("---\n");
				// print_ast(node->first_child->first_child->next_sibling, 0);
				compile_main_section(ctx, node->first_child->first_child->next_sibling, buf, idx, allocated, NEED_RESULT);
			} else {
				compile_main_section(ctx, node->first_child, buf, idx, allocated, NEED_RESULT);
			}
			OPCODE(*buf, have_arr_splat ? OP_CALL_ARR : OP_CALL);
			POP_IF_DONT_NEED_RESULT(*buf);
			STACK_DEPTH = saved_stack_depth;
			break;
		case INDEX_NODE:
		case ATTR_NODE:
			DEBUG_COMPILER("COMPILER: %s %zu\n", "INDEX NODE", *idx);
			OPCODE(*buf, OP_PUSH_NULL); // Placeholder for return value
			saved_stack_depth = STACK_DEPTH;
			STACK_DEPTH++;
			compile_main_section(ctx, node->first_child, buf, idx, allocated, NEED_RESULT);
			STACK_DEPTH++;
			if(node->type == ATTR_NODE) {
				compile_attr_name(ctx, node->first_child->next_sibling, buf, idx, allocated, NEED_RESULT);
			} else {
				compile_main_section(ctx, node->first_child->next_sibling, buf, idx, allocated, NEED_RESULT);
			}
			STACK_DEPTH++;
			OPCODE(*buf, OP_PUSH_INT); DATA_INT(*buf, 2);
			STACK_DEPTH++;
			compile_identifier(ctx, buf, idx, node->type == INDEX_NODE ? "[]" : ".", OP_FETCH_LOCAL, OP_FETCH_UPVAR, OP_FETCH_GLOBAL);
			OPCODE(*buf, OP_CALL);
			POP_IF_DONT_NEED_RESULT(*buf);
			STACK_DEPTH = saved_stack_depth;
			break;
		case INT_NODE:
			/*printf("Compiling tNUMBER @ %d\n", *idx);*/
			if(need_result) {
				OPCODE(*buf, OP_PUSH_INT); DATA(*buf, node->number);
			}
			break;
		case REAL_NODE:
			if(need_result) {
				OPCODE(*buf, OP_PUSH_REAL); DATA(*buf, *(NGS_REAL *)node->data);
			}
			break;
		case IDENTIFIER_NODE:
			compile_identifier(ctx, buf, idx, node->name, OP_FETCH_LOCAL, OP_FETCH_UPVAR, OP_FETCH_GLOBAL);
			POP_IF_DONT_NEED_RESULT(*buf);
			break;
		case ASSIGNMENT_NODE:
			ptr = node->first_child;
			switch(ptr->type) {
				case IDENTIFIER_NODE:
					DEBUG_COMPILER("COMPILER: %s %zu\n", "identifier <- expression", *idx);
					compile_main_section(ctx, ptr->next_sibling, buf, idx, allocated, NEED_RESULT);
					DUP_IF_NEED_RESULT(*buf);
					compile_identifier(ctx, buf, idx, ptr->name, OP_STORE_LOCAL, OP_STORE_UPVAR, OP_STORE_GLOBAL);
					break;
				case INDEX_NODE:
					OPCODE(*buf, OP_PUSH_NULL); // Placeholder for return value
					compile_main_section(ctx, ptr->first_child, buf, idx, allocated, NEED_RESULT);
					compile_main_section(ctx, ptr->first_child->next_sibling, buf, idx, allocated, NEED_RESULT);
					compile_main_section(ctx, node->first_child->next_sibling, buf, idx, allocated, NEED_RESULT);
					OPCODE(*buf, OP_PUSH_INT); DATA_INT(*buf, 3);
					compile_identifier(ctx, buf, idx, "[]=", OP_FETCH_LOCAL, OP_FETCH_UPVAR, OP_FETCH_GLOBAL);
					OPCODE(*buf, OP_CALL);
					POP_IF_DONT_NEED_RESULT(*buf);
					break;
				case ATTR_NODE:
					OPCODE(*buf, OP_PUSH_NULL); // Placeholder for return value
					compile_main_section(ctx, ptr->first_child, buf, idx, allocated, NEED_RESULT);
					compile_attr_name(ctx, ptr->first_child->next_sibling, buf, idx, allocated, NEED_RESULT);
					compile_main_section(ctx, node->first_child->next_sibling, buf, idx, allocated, NEED_RESULT);
					OPCODE(*buf, OP_PUSH_INT); DATA_INT(*buf, 3);
					compile_identifier(ctx, buf, idx, ".=", OP_FETCH_LOCAL, OP_FETCH_UPVAR, OP_FETCH_GLOBAL);
					OPCODE(*buf, OP_CALL);
					POP_IF_DONT_NEED_RESULT(*buf);
					break;
				default:
					assert(0=="compile_main_section(): assignment to unknown node type");
			}
			break;
		case EXPRESSIONS_NODE:
			if(!node->first_child && need_result) {
				OPCODE(*buf, OP_PUSH_NULL);
				break;
			}
			for(ptr=node->first_child; ptr; ptr=ptr->next_sibling) {
				// printf("EXPRESSIONS_NODE ptr=%p type=%s need_result=%d will_do_result=%d\n", ptr, NGS_AST_NODE_TYPES_NAMES[ptr->type], need_result, (ptr == node->last_child) && need_result);
				compile_main_section(ctx, ptr, buf, idx, allocated, (!ptr->next_sibling) && need_result);
			}
			break;
		case FOR_NODE:
			// setup
			compile_main_section(ctx, node->first_child, buf, idx, allocated, DONT_NEED_RESULT);
			// condition
			loop_beg_idx = *idx;
			compile_main_section(ctx, node->first_child->next_sibling, buf, idx, allocated, NEED_RESULT);
			OPCODE(*buf, OP_JMP_FALSE);
			cond_jump = *idx;
			DATA_JUMP_OFFSET_PLACEHOLDER(*buf);
			// body
			SETUP_ADDRESS_FILLING();
			compile_main_section(ctx, node->first_child->next_sibling->next_sibling->next_sibling, buf, idx, allocated, DONT_NEED_RESULT);
			// increment
			continue_target_idx = *idx;
			compile_main_section(ctx, node->first_child->next_sibling->next_sibling, buf, idx, allocated, DONT_NEED_RESULT);
			// jump to condition
			OPCODE(*buf, OP_JMP);
			assert(*idx - cond_jump < 0x7FFF);
			*(JUMP_OFFSET *)&(*buf)[cond_jump] = *idx - cond_jump;
			assert((*idx - loop_beg_idx) < 0x7FFF);
			DATA_JUMP_OFFSET(*buf, -(*idx - loop_beg_idx + sizeof(JUMP_OFFSET)));
			HANDLE_ADDRESS_FILLING();
			if(need_result) OPCODE(*buf, OP_PUSH_NULL);
			break;
		case EMPTY_NODE:
			break;
		case ARR_LIT_NODE:
			DEBUG_COMPILER("COMPILER: %s %zu\n", "ARRAY NODE", *idx);
			for(ptr=node->first_child, have_arr_splat=0; ptr; ptr=ptr->next_sibling) {
				if(ptr->type == ARR_SPLAT_NODE) {
					have_arr_splat = 1;
					break;
				}
			}
			if(have_arr_splat) {
				OPCODE(*buf, OP_PUSH_INT); DATA_INT(*buf, 0);
				OPCODE(*buf, OP_MAKE_ARR);
			}
			for(argc=0, ptr=node->first_child; ptr; argc++, ptr=ptr->next_sibling) {
				if(ptr->type == ARR_SPLAT_NODE) {
					compile_main_section(ctx, ptr->first_child, buf, idx, allocated, NEED_RESULT);
					OPCODE(*buf, OP_TO_ARR);
				} else {
					compile_main_section(ctx, ptr, buf, idx, allocated, NEED_RESULT);
				}
				if(have_arr_splat) {
					OPCODE(*buf, ptr->type == ARR_SPLAT_NODE ? OP_ARR_CONCAT : OP_ARR_APPEND);
				}
			}
			if(!have_arr_splat) {
				OPCODE(*buf, OP_PUSH_INT); DATA(*buf, argc);
				OPCODE(*buf, OP_MAKE_ARR);
			}
			POP_IF_DONT_NEED_RESULT(*buf);
			break;
		case FUNC_NODE:
			// FUNC_NODE children: arguments, body
			DEBUG_COMPILER("COMPILER: %s %zu\n", "FUNC NODE", *idx);
			OPCODE(*buf, OP_JMP);
			func_jump = *idx;
			DATA_JUMP_OFFSET_PLACEHOLDER(*buf);
			ctx->locals_ptr++;
			assert(ctx->locals_ptr < COMPILE_MAX_FUNC_DEPTH);
			LOCALS = NULL;
			IDENTIFIERS_SCOPES = NULL;
			N_LOCALS = 0;
			N_UPLEVELS = 0;
			STACK_DEPTH = 0;
			params_flags = 0;
			// Arguments
			for(ptr=node->first_child->first_child; ptr; ptr=ptr->next_sibling) {
				// ptr children: identifier, type, (default value | splat indicator)
				register_local_var(ctx, ptr->first_child->name);
			}
			// Body
			register_local_vars(ctx, node->first_child->next_sibling);
			compile_main_section(ctx, node->first_child->next_sibling, buf, idx, allocated, NEED_RESULT);
			n_locals = N_LOCALS;
			n_uplevels = N_UPLEVELS;
			ctx->locals_ptr--;
			OPCODE(*buf, OP_RET);
			end_of_func_idx = *idx;

			// Arguments' types and default values
			for(ptr=node->first_child->first_child, n_params_required=0, n_params_optional=0; ptr; ptr=ptr->next_sibling) {
				// ptr children: identifier, type, (default value | splat indicator)
				OPCODE(*buf, OP_PUSH_L_STR);
				L_STR(*buf, ptr->first_child->name);
				// printf("PT 0 %s\n", ptr->first_child->name);
				compile_main_section(ctx, ptr->first_child->next_sibling, buf, idx, allocated, NEED_RESULT);
				if(ptr->first_child->next_sibling->next_sibling) {
					// Either array/hash splat or default value
					if(ptr->first_child->next_sibling->next_sibling->type == ARR_SPLAT_NODE) {
						if(ptr->next_sibling && (ptr->next_sibling->first_child->next_sibling->next_sibling->type != HASH_SPLAT_NODE)) {
							assert(0 == "splat function parameter must be the last one or followed by keyword splat only");
						}
						params_flags |= PARAMS_FLAG_ARR_SPLAT;
						continue;
					}
					if(ptr->first_child->next_sibling->next_sibling->type == HASH_SPLAT_NODE) {
						if(ptr->next_sibling) {
							assert(0 == "keyword splat function parameter must be the last one");
						}
						params_flags |= PARAMS_FLAG_HASH_SPLAT;
						continue;
					}
					// Splat's handled, we have default value
					compile_main_section(ctx, ptr->first_child->next_sibling->next_sibling, buf, idx, allocated, NEED_RESULT);
					n_params_optional++;
					continue;
				}
				// Optional parameters can not be followed by required parameters
				assert(n_params_optional == 0);
				n_params_required++;
			}

			*(JUMP_OFFSET *)&(*buf)[func_jump] = (end_of_func_idx - func_jump - sizeof(JUMP_OFFSET));
			OPCODE(*buf, OP_MAKE_CLOSURE);
			DATA_JUMP_OFFSET(*buf, -(*idx - func_jump + 3*sizeof(LOCAL_VAR_INDEX) + sizeof(UPVAR_INDEX) + sizeof(int)));
			DATA_N_LOCAL_VARS(*buf, n_params_required);
			DATA_N_LOCAL_VARS(*buf, n_params_optional);
			DATA_N_LOCAL_VARS(*buf, n_locals);
			DATA_N_UPVAR_INDEX(*buf, n_uplevels);
			DATA_INT(*buf, params_flags);

			// Doc
			compile_main_section(ctx, node->first_child->next_sibling->next_sibling, buf, idx, allocated, NEED_RESULT);
			OPCODE(*buf, OP_SET_CLOSURE_DOC);

			// Name
			if(node->first_child->next_sibling->next_sibling->next_sibling) {
				// Function has a name
				compile_identifier(ctx, buf, idx, node->first_child->next_sibling->next_sibling->next_sibling->name, OP_DEF_LOCAL_FUNC, OP_DEF_UPVAR_FUNC, OP_DEF_GLOBAL_FUNC);
				OPCODE(*buf, OP_SET_CLOSURE_NAME);
				L_STR(*buf, node->first_child->next_sibling->next_sibling->next_sibling->name);
			}
			POP_IF_DONT_NEED_RESULT(*buf);
			break;
		case STR_COMPS_NODE:
			for(argc=0, ptr=node->first_child; ptr; argc++, ptr=ptr->next_sibling) {
				compile_main_section(ctx, ptr, buf, idx, allocated, NEED_RESULT);
				if(ptr->type != STR_COMP_IMM_NODE) {
					OPCODE(*buf, OP_TO_STR);
				}
			}
			switch(argc) {
				case 0:
					OPCODE(*buf, OP_PUSH_EMPTY_STR);
					break;
				case 1:
					break;
				default:
					OPCODE(*buf, OP_PUSH_INT);
					DATA_INT(*buf, argc);
					OPCODE(*buf, OP_MAKE_STR);
			}
			POP_IF_DONT_NEED_RESULT(*buf);
			break;
		case STR_COMP_IMM_NODE:
			OPCODE(*buf, OP_PUSH_L_STR);
			L_STR(*buf, node->name);
			break;
		case NULL_NODE:  if(need_result) { OPCODE(*buf, OP_PUSH_NULL); } break;
		case TRUE_NODE:  if(need_result) { OPCODE(*buf, OP_PUSH_TRUE); } break;
		case FALSE_NODE: if(need_result) { OPCODE(*buf, OP_PUSH_FALSE); } break;
		case DEFINED_NODE:
			compile_identifier(ctx, buf, idx, node->first_child->name, OP_LOCAL_DEF_P, OP_UPVAR_DEF_P, OP_GLOBAL_DEF_P);
			POP_IF_DONT_NEED_RESULT(*buf);
			break;
		case IF_NODE:
			compile_main_section(ctx, node->first_child, buf, idx, allocated, NEED_RESULT);
			OPCODE(*buf, OP_TO_BOOL);
			OPCODE(*buf, OP_JMP_FALSE);
			if_jump = *idx;
			DATA_JUMP_OFFSET_PLACEHOLDER(*buf);
			compile_main_section(ctx, node->first_child->next_sibling, buf, idx, allocated, need_result);
			OPCODE(*buf, OP_JMP);
			DATA_JUMP_OFFSET_PLACEHOLDER(*buf);
			*(JUMP_OFFSET *)&(*buf)[if_jump] = *idx - if_jump - sizeof(JUMP_OFFSET); // Jump is OP_JMP_FALSE JUMP_OFFSET shorter
			if_jump = *idx - sizeof(JUMP_OFFSET);
			compile_main_section(ctx, node->first_child->next_sibling->next_sibling, buf, idx, allocated, need_result);
			*(JUMP_OFFSET *)&(*buf)[if_jump] = *idx - if_jump - sizeof(JUMP_OFFSET);
			break;
		case WHILE_NODE:
			loop_beg_idx = *idx;
			compile_main_section(ctx, node->first_child, buf, idx, allocated, NEED_RESULT);
			OPCODE(*buf, OP_TO_BOOL);
			OPCODE(*buf, OP_JMP_FALSE);
			while_jump = *idx;
			DATA_JUMP_OFFSET_PLACEHOLDER(*buf);
			SETUP_ADDRESS_FILLING();
			continue_target_idx = *idx; // For HANDLE_ADDRESS_FILLING
			compile_main_section(ctx, node->first_child->next_sibling, buf, idx, allocated, DONT_NEED_RESULT);
			OPCODE(*buf, OP_JMP);
			DATA_JUMP_OFFSET(*buf, -(*idx - loop_beg_idx + sizeof(JUMP_OFFSET)));
			*(JUMP_OFFSET *)&(*buf)[while_jump] = *idx - while_jump - sizeof(JUMP_OFFSET);
			HANDLE_ADDRESS_FILLING();
			if(need_result) { OPCODE(*buf, OP_PUSH_NULL); }
			break;
		case LOCAL_NODE:
		case UPVAR_NODE:
		case GLOBAL_NODE:
			for(ptr=node->first_child; ptr; ptr=ptr->next_sibling) {
				if(ptr->type != IDENTIFIER_NODE) {
					compile_main_section(ctx, ptr, buf, idx, allocated, DONT_NEED_RESULT);
				}
			}
			if(need_result) { OPCODE(*buf, OP_PUSH_NULL); }
			break;
		case HASH_LIT_NODE:
			DEBUG_COMPILER("COMPILER: %s %zu\n", "HASH NODE", *idx);
			for(ptr=node->first_child, have_hash_splat=0; ptr; ptr=ptr->next_sibling) {
				if(ptr->type == HASH_SPLAT_NODE) {
					have_hash_splat = 1;
					break;
				}
			}
			if(have_hash_splat) {
				OPCODE(*buf, OP_PUSH_INT); DATA_INT(*buf, 0);
				OPCODE(*buf, OP_MAKE_HASH);
				for(argc=0, ptr=node->first_child; ptr; argc++, ptr=ptr->next_sibling) {
					if(ptr->type == HASH_SPLAT_NODE) {
						compile_main_section(ctx, ptr->first_child, buf, idx, allocated, NEED_RESULT);
						OPCODE(*buf, OP_TO_HASH);
						OPCODE(*buf, OP_HASH_UPDATE);
					} else {
						compile_main_section(ctx, ptr->first_child, buf, idx, allocated, NEED_RESULT);
						compile_main_section(ctx, ptr->first_child->next_sibling, buf, idx, allocated, NEED_RESULT);
						OPCODE(*buf, OP_HASH_SET);
					}
				}
			} else {
				for(argc=0, ptr=node->first_child; ptr; argc++, ptr=ptr->next_sibling) {
					compile_main_section(ctx, ptr->first_child, buf, idx, allocated, NEED_RESULT);
					compile_main_section(ctx, ptr->first_child->next_sibling, buf, idx, allocated, NEED_RESULT);
				}
				OPCODE(*buf, OP_PUSH_INT); DATA_INT(*buf, argc);
				OPCODE(*buf, OP_MAKE_HASH);
			}
			POP_IF_DONT_NEED_RESULT(*buf);
			break;
		case RETURN_NODE:
			for(i=0; i<STACK_DEPTH; i++) {
				OPCODE(*buf, OP_POP);
			}
			if(node->first_child) {
				compile_main_section(ctx, node->first_child, buf, idx, allocated, NEED_RESULT);
			} else {
				OPCODE(*buf, OP_PUSH_NULL);
			}
			OPCODE(*buf, OP_RET);
			break;
		case AND_NODE:
		case OR_NODE:
			// TODO: optimize more for DONT_NEED_RESULT case
			compile_main_section(ctx, node->first_child, buf, idx, allocated, NEED_RESULT);
			OPCODE(*buf, OP_DUP);
			OPCODE(*buf, OP_TO_BOOL);
			if_jump = *idx;
			OPCODE(*buf, node->type == AND_NODE ? OP_JMP_FALSE : OP_JMP_TRUE);
			DATA_JUMP_OFFSET_PLACEHOLDER(*buf);
			OPCODE(*buf, OP_POP);
			compile_main_section(ctx, node->first_child->next_sibling, buf, idx, allocated, NEED_RESULT);
			*(JUMP_OFFSET *)&(*buf)[if_jump+1] = *idx - if_jump - 1 - sizeof(JUMP_OFFSET); // Jump is OP_JMP_FALSE JUMP_OFFSET shorter
			if_jump = *idx - 1 - sizeof(JUMP_OFFSET);
			POP_IF_DONT_NEED_RESULT(*buf);
			break;
		case GUARD_NODE:
			compile_main_section(ctx, node->first_child, buf, idx, allocated, NEED_RESULT);
			OPCODE(*buf, OP_TO_BOOL);
			OPCODE(*buf, OP_GUARD);
			break;

		case TRY_CATCH_NODE:

			if_jump = *idx;
			OPCODE(*buf, OP_TRY_START);
				DATA_JUMP_OFFSET_PLACEHOLDER(*buf); // Set handler code location

			compile_main_section(ctx, node->first_child, buf, idx, allocated, need_result);

			end_of_func_idx = *idx;
			OPCODE(*buf, OP_TRY_END);
				DATA_JUMP_OFFSET_PLACEHOLDER(*buf); // Jump over handler code

			*(JUMP_OFFSET *)&(*buf)[if_jump+1] = *idx - if_jump - 1 - sizeof(JUMP_OFFSET); // Jump is OP_TRY_START JUMP_OFFSET shorter

			if(node->first_child->next_sibling) {
				// Room for return value
				OPCODE(*buf, OP_PUSH_NULL);
				OPCODE(*buf, OP_XCHG);

				OPCODE(*buf, OP_PUSH_INT); DATA_INT(*buf, 1); // One argument for the call of handler function(s)
				OPCODE(*buf, OP_PUSH_INT); DATA_INT(*buf, 0); // Make array with zero elements
				OPCODE(*buf, OP_MAKE_ARR);
				for(ptr=node->first_child->next_sibling; ptr; ptr=ptr->next_sibling) {
					compile_main_section(ctx, ptr, buf, idx, allocated, NEED_RESULT);
					OPCODE(*buf, OP_ARR_APPEND);
				}
				OPCODE(*buf, OP_ARR_REVERSE);
				OPCODE(*buf, OP_CALL_EXC);
				POP_IF_DONT_NEED_RESULT(*buf);
			} else {
				// No handlers, return null
				OPCODE(*buf, OP_POP); // Ignore the exception value
				if(need_result) {
					OPCODE(*buf, OP_PUSH_NULL);
				}
			}
			*(JUMP_OFFSET *)&(*buf)[end_of_func_idx+1] = *idx - end_of_func_idx - 1 - sizeof(JUMP_OFFSET); // Jump is OP_TRY_START JUMP_OFFSET shorter
			break;

		case THROW_NODE:
			compile_main_section(ctx, node->first_child, buf, idx, allocated, NEED_RESULT);
			OPCODE(*buf, OP_THROW);
			break;

		case COMMAND_NODE:
			OPCODE(*buf, OP_PUSH_NULL); // Placeholder for return value
			// argv
			OPCODE(*buf, OP_PUSH_INT); DATA_INT(*buf, 0); // Make array with zero elements
			OPCODE(*buf, OP_MAKE_ARR);
			for(ptr=node->first_child->next_sibling->first_child; ptr; ptr=ptr->next_sibling) {
				if(ptr->type == ARR_SPLAT_NODE) {
					compile_main_section(ctx, ptr->first_child, buf, idx, allocated, NEED_RESULT);
					OPCODE(*buf, OP_TO_ARR);
					OPCODE(*buf, OP_ARR_CONCAT);
					continue;
				}
				compile_main_section(ctx, ptr, buf, idx, allocated, NEED_RESULT);
				OPCODE(*buf, OP_ARR_APPEND);
			}
			// redirects
			OPCODE(*buf, OP_PUSH_INT); DATA_INT(*buf, 0); // Make array with zero elements
			OPCODE(*buf, OP_MAKE_ARR);
			for(ptr=node->first_child->next_sibling->next_sibling->first_child; ptr; ptr=ptr->next_sibling) {
				compile_main_section(ctx, ptr, buf, idx, allocated, NEED_RESULT);
				OPCODE(*buf, OP_ARR_APPEND);
			}
			OPCODE(*buf, node->number ? OP_PUSH_TRUE : OP_PUSH_FALSE);
			OPCODE(*buf, OP_MAKE_CMD);

			OPCODE(*buf, OP_PUSH_INT); DATA_INT(*buf, 1);
			compile_identifier(ctx, buf, idx, node->first_child->name, OP_FETCH_LOCAL, OP_FETCH_UPVAR, OP_FETCH_GLOBAL);
			OPCODE(*buf, OP_CALL);
			POP_IF_DONT_NEED_RESULT(*buf);

			break;

		case BREAK_NODE:
			assert(ctx->fill_in_break_addrs_ptr < COMPILE_MAX_FILL_IN_LEN);
			OPCODE(*buf, OP_JMP);
			ctx->fill_in_break_addrs[ctx->fill_in_break_addrs_ptr++] = *idx;
			DATA_JUMP_OFFSET_PLACEHOLDER(*buf);
			break;

		case CONTINUE_NODE:
			assert(ctx->fill_in_continue_addrs_ptr < COMPILE_MAX_FILL_IN_LEN);
			OPCODE(*buf, OP_JMP);
			ctx->fill_in_continue_addrs[ctx->fill_in_continue_addrs_ptr++] = *idx;
			DATA_JUMP_OFFSET_PLACEHOLDER(*buf);
			break;

		case REDIR_NODE:
			compile_main_section(ctx, node->first_child, buf, idx, allocated, NEED_RESULT);
			compile_main_section(ctx, node->first_child->next_sibling, buf, idx, allocated, NEED_RESULT);
			OPCODE(*buf, OP_MAKE_REDIR);
			break;

		case SWITCH_NODE:
			// XXX: Fix and test STACK_DEPTH
			// XXX: Check for/while { ... case { ... break } ... } situation because break addresses are used in switch too.
			// TODO: assert jump ranges
			IF_NOT_SWITCH_COND {
				compile_main_section(ctx, node->first_child, buf, idx, allocated, NEED_RESULT);
				STACK_DEPTH++;
			}
			SETUP_ADDRESS_FILLING();
			continue_target_idx = 0; // Should not appear there!
			// TODO: make sure that leaving the section pops the switch value from the stack
			cond_jump = 0;
			for(ptr=node->first_child->next_sibling; ptr; ptr=ptr->next_sibling) {
				if(cond_jump) {
					// Jump to next comparison
					*(JUMP_OFFSET *)&(*buf)[cond_jump] = *idx - (cond_jump + sizeof(JUMP_OFFSET));
					cond_jump = 0;
				}
				IF_NOT_SWITCH_COND {
					OPCODE(*buf, OP_DUP);
					OPCODE(*buf, OP_PUSH_NULL); // Result placeholder
					OPCODE(*buf, OP_XCHG);
				}
				// The value to compare to
				compile_main_section(ctx, ptr->first_child, buf, idx, allocated, NEED_RESULT);
				switch((switch_node_subtype)node->number) {
					case SWITCH_NODE_SWITCH:
					case SWITCH_NODE_ESWITCH:
						OPCODE(*buf, OP_PUSH_INT); DATA_INT(*buf, 2);
						compile_identifier(ctx, buf, idx, "==", OP_FETCH_LOCAL, OP_FETCH_UPVAR, OP_FETCH_GLOBAL);
						OPCODE(*buf, OP_CALL);
						break;
					case SWITCH_NODE_MATCH:
					case SWITCH_NODE_EMATCH:
						OPCODE(*buf, OP_PUSH_INT); DATA_INT(*buf, 2);
						compile_identifier(ctx, buf, idx, "match", OP_FETCH_LOCAL, OP_FETCH_UPVAR, OP_FETCH_GLOBAL);
						OPCODE(*buf, OP_CALL);
					case SWITCH_NODE_COND:
					case SWITCH_NODE_ECOND:
						OPCODE(*buf, OP_TO_BOOL);
						break;
					default:
						fprintf(stderr, "ERROR: SWITCH_NODE subtype %i %i\n", node->number, SWITCH_NODE_COND);
						assert(0 == "Unsupported SWITCH_NODE subtype");
				}
				OPCODE(*buf, OP_JMP_FALSE);
				cond_jump = *idx;
				DATA_JUMP_OFFSET_PLACEHOLDER(*buf);
				// Code block to execute when values match
				compile_main_section(ctx, ptr->first_child->next_sibling, buf, idx, allocated, NEED_RESULT);
				IF_NOT_SWITCH_COND {
					// Get rid of original value, preserving the match result
					OPCODE(*buf, OP_XCHG);
					OPCODE(*buf, OP_POP);
				}
				// Break
				assert(ctx->fill_in_break_addrs_ptr < COMPILE_MAX_FILL_IN_LEN);
				OPCODE(*buf, OP_JMP);
				ctx->fill_in_break_addrs[ctx->fill_in_break_addrs_ptr++] = *idx;
				DATA_JUMP_OFFSET_PLACEHOLDER(*buf);
			}
			if(cond_jump) {
				// Jump to next comparison
				*(JUMP_OFFSET *)&(*buf)[cond_jump] = *idx - (cond_jump + sizeof(JUMP_OFFSET));
			}
			// TOOD: optimize - OP_PUSH_NULL is not needed if result is not needed
			if(node->number & 1) {
				OPCODE(*buf, OP_PUSH_INT); DATA_INT(*buf, 0);
				compile_identifier(ctx, buf, idx, "SwitchFail", OP_FETCH_LOCAL, OP_FETCH_UPVAR, OP_FETCH_GLOBAL);
				// TODO: attribute with offending value
				OPCODE(*buf, OP_CALL);
				OPCODE(*buf, OP_THROW);
			} else {
				IF_NOT_SWITCH_COND {
					OPCODE(*buf, OP_POP); // Get rid of original value
				}
				OPCODE(*buf, OP_PUSH_NULL);
			}
			HANDLE_ADDRESS_FILLING();
			POP_IF_DONT_NEED_RESULT(*buf);
			IF_NOT_SWITCH_COND {
				STACK_DEPTH--;
			}
			break;

		default:
			fprintf(stderr, "Node type %i\n", node->type);
			assert(0=="compile_main_section(): unknown node type");
	}
}