root/compiler/generate.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. LabelTable
  2. OpcodeBuf
  3. alloc_executable
  4. add_constant_pool
  5. copy_parameter_list
  6. copy_local_variables
  7. copy_type_specifier
  8. add_global_variable
  9. generate_code
  10. generate_boolean_expression
  11. generate_int_expression
  12. generate_double_expression
  13. generate_string_expression
  14. get_opcode_type_offset
  15. generate_identifier_expression
  16. generate_pop_to_identifier
  17. generate_assign_expression
  18. generate_binary_expression
  19. get_label
  20. set_label
  21. generate_logical_and_expression
  22. generate_logical_or_expression
  23. generate_cast_expression
  24. generate_inc_dec_expression
  25. generate_function_call_expression
  26. generate_expression
  27. generate_expression_statement
  28. generate_if_statement
  29. generate_while_statement
  30. generate_for_statement
  31. generate_return_statement
  32. generate_break_statement
  33. generate_continue_statement
  34. generate_initializer
  35. generate_statement_list
  36. init_opcode_buf
  37. fix_labels
  38. fix_opcode_buf
  39. copy_function
  40. add_functions
  41. add_top_level
  42. dkc_generate

#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include "MEM.h"
#include "DBG.h"
#include "diksamc.h"

extern OpcodeInfo dvm_opcode_info[];

#define OPCODE_ALLOC_SIZE (256)
#define LABEL_TABLE_ALLOC_SIZE (256)

typedef struct {
    int label_address;
} LabelTable;

typedef struct {
    int         size;
    int         alloc_size;
    DVM_Byte    *code;
    int         label_table_size;
    int         label_table_alloc_size;
    LabelTable  *label_table;
} OpcodeBuf;

static DVM_Executable *
alloc_executable(void)
{
    DVM_Executable      *exe;

    exe = MEM_malloc(sizeof(DVM_Executable));
    exe->constant_pool_count = 0;
    exe->constant_pool = NULL;
    exe->global_variable_count = 0;
    exe->global_variable = NULL;
    exe->function_count = 0;
    exe->function = NULL;
    exe->code_size = 0;
    exe->code = NULL;

    return exe;
}

static int
add_constant_pool(DVM_Executable *exe, DVM_ConstantPool *cp)
{
    int ret;

    exe->constant_pool
        = MEM_realloc(exe->constant_pool,
                      sizeof(DVM_ConstantPool)
                      * (exe->constant_pool_count + 1));
    exe->constant_pool[exe->constant_pool_count] = *cp;

    ret = exe->constant_pool_count;
    exe->constant_pool_count++;

    return ret;
}

static DVM_TypeSpecifier *copy_type_specifier(TypeSpecifier *src);

static DVM_LocalVariable *
copy_parameter_list(ParameterList *src, int *param_count_p)
{
    int param_count = 0;
    ParameterList *param;
    DVM_LocalVariable *dest;
    int i;

    for (param = src; param; param = param->next) {
        param_count++;
    }
    *param_count_p = param_count;
    dest = MEM_malloc(sizeof(DVM_LocalVariable) * param_count);
    
    for (param = src, i = 0; param; param = param->next, i++) {
        dest[i].name = MEM_strdup(param->name);
        dest[i].type = copy_type_specifier(param->type);
    }

    return dest;
}

static DVM_LocalVariable *
copy_local_variables(FunctionDefinition *fd, int param_count)
{
    int i;
    int local_variable_count;
    DVM_LocalVariable *dest;

    local_variable_count = fd->local_variable_count - param_count;

    dest = MEM_malloc(sizeof(DVM_LocalVariable) * local_variable_count);

    for (i = 0; i < local_variable_count; i++) {
        dest[i].name
            = MEM_strdup(fd->local_variable[i+param_count]->name);
        dest[i].type
            = copy_type_specifier(fd->local_variable[i+param_count]->type);
    }

    return dest;
}

static DVM_TypeSpecifier *
copy_type_specifier(TypeSpecifier *src)
{
    DVM_TypeSpecifier *dest;
    int derive_count = 0;
    TypeDerive *derive;
    int param_count;
    int i;

    dest = MEM_malloc(sizeof(DVM_TypeSpecifier));

    dest->basic_type = src->basic_type;

    for (derive = src->derive; derive; derive = derive->next) {
        derive_count++;
    }
    dest->derive_count = derive_count;
    dest->derive = MEM_malloc(sizeof(DVM_TypeDerive) * derive_count);
    for (i = 0, derive = src->derive; derive;
         derive = derive->next, i++) {
        switch (derive->tag) {
        case DVM_FUNCTION_DERIVE:
            dest->derive[i].tag = DVM_FUNCTION_DERIVE;
            dest->derive[i].u.function_d.parameter
                = copy_parameter_list(derive->u.function_d.parameter_list,
                                      &param_count);
            dest->derive[i].u.function_d.parameter_count = param_count;
            break;
        default:
            DBG_assert(0, ("derive->tag..%d\n", derive->tag));
        }
    }

    return dest;
}

static void
add_global_variable(DKC_Compiler *compiler, DVM_Executable *exe)
{
    DeclarationList *dl;
    int i;
    int var_count = 0;

    for (dl = compiler->declaration_list; dl; dl = dl->next) {
        var_count++;
    }
    exe->global_variable_count = var_count;
    exe->global_variable = MEM_malloc(sizeof(DVM_Variable) * var_count);

    for (dl = compiler->declaration_list, i = 0; dl; dl = dl->next, i++) {
        exe->global_variable[i].name = MEM_strdup(dl->declaration->name);
        exe->global_variable[i].type
            = copy_type_specifier(dl->declaration->type);
    }
}

static void
generate_code(OpcodeBuf *ob, DVM_Opcode code, ...)
{
    va_list     ap;
    int         i;
    char        *param;
    int         param_count;

    va_start(ap, code);

    param = dvm_opcode_info[(int)code].parameter;
    param_count = strlen(param);
    if (ob->alloc_size < ob->size + 1 + (param_count * 2)) {
        ob->code = MEM_realloc(ob->code, ob->alloc_size + OPCODE_ALLOC_SIZE);
        ob->alloc_size += OPCODE_ALLOC_SIZE;
    }

    ob->code[ob->size] = code;
    ob->size++;
    for (i = 0; param[i] != '\0'; i++) {
        unsigned int value = va_arg(ap, int);
        switch (param[i]) {
        case 'b': /* byte */
            ob->code[ob->size] = (DVM_Byte)value;
            ob->size++;
            break;
        case 's': /* short(2byte int) */
            ob->code[ob->size] = (DVM_Byte)(value >> 8);
            ob->code[ob->size+1] = (DVM_Byte)(value & 0xff);
            ob->size += 2;
            break;
        case 'p': /* constant pool index */
            ob->code[ob->size] = (DVM_Byte)(value >> 8);
            ob->code[ob->size+1] = (DVM_Byte)(value & 0xff);
            ob->size += 2;
            break;
        default:
            DBG_assert(0, ("param..%s, i..%d", param, i));
        }
    }

    va_end(ap);
}

static void
generate_boolean_expression(DVM_Executable *cf, DVM_Boolean boolean_value,
                            OpcodeBuf *ob)
{
    if (boolean_value) {
        generate_code(ob, DVM_PUSH_INT_1BYTE, 1);
    } else {
        generate_code(ob, DVM_PUSH_INT_1BYTE, 0);
    }
}

static void
generate_int_expression(DVM_Executable *cf, int int_value,
                        OpcodeBuf *ob)
{
    DVM_ConstantPool cp;
    int cp_idx;

    if (int_value >= 0 && int_value < 256) {
        generate_code(ob, DVM_PUSH_INT_1BYTE, int_value);
    } else if (int_value >= 0 && int_value < 65536) {
        generate_code(ob, DVM_PUSH_INT_2BYTE, int_value);
    } else {
        cp.tag = DVM_CONSTANT_INT;
        cp.u.c_int = int_value;
        cp_idx = add_constant_pool(cf, &cp);

        generate_code(ob, DVM_PUSH_INT, cp_idx);
    }
}

static void
generate_double_expression(DVM_Executable *cf, double double_value,
                           OpcodeBuf *ob)
{
    DVM_ConstantPool cp;
    int cp_idx;

    if (double_value == 0.0) {
        generate_code(ob, DVM_PUSH_DOUBLE_0);
    } else if (double_value == 1.0) {
        generate_code(ob, DVM_PUSH_DOUBLE_1);
    } else {
        cp.tag = DVM_CONSTANT_DOUBLE;
        cp.u.c_double = double_value;
        cp_idx = add_constant_pool(cf, &cp);

        generate_code(ob, DVM_PUSH_DOUBLE, cp_idx);
    }
}

static void
generate_string_expression(DVM_Executable *cf, DVM_Char *string_value,
                           OpcodeBuf *ob)
{
    DVM_ConstantPool cp;
    int cp_idx;

    cp.tag = DVM_CONSTANT_STRING;
    cp.u.c_string = string_value;
    cp_idx = add_constant_pool(cf, &cp);
    generate_code(ob, DVM_PUSH_STRING, cp_idx);
}

static int
get_opcode_type_offset(DVM_BasicType basic_type)
{
    switch (basic_type) {
    case DVM_BOOLEAN_TYPE:
        return 0;
        break;
    case DVM_INT_TYPE:
        return 0;
        break;
    case DVM_DOUBLE_TYPE:
        return 1;
        break;
    case DVM_STRING_TYPE:
        return 2;
        break;
    default:
        DBG_assert(0, ("basic_type..%d", basic_type));
    }

    return 0;
}

static void
generate_identifier_expression(DVM_Executable *exe, Block *block,
                               IdentifierExpression *identifier,
                               OpcodeBuf *ob)
{
    if (identifier->is_function) {
        generate_code(ob, DVM_PUSH_FUNCTION,
                      identifier->u.function->index);
        return;
    }

    if (identifier->u.declaration->is_local) {
        generate_code(ob, DVM_PUSH_STACK_INT
                      + get_opcode_type_offset(identifier->u.declaration
                                               ->type->basic_type),
                      identifier->u.declaration->variable_index);
    } else {
        generate_code(ob, DVM_PUSH_STATIC_INT
                      + get_opcode_type_offset(identifier->u.declaration
                                               ->type->basic_type),
                      identifier->u.declaration->variable_index);
    }
}

static void generate_expression(DVM_Executable *exe, Block *current_block,
                                Expression *expr, OpcodeBuf *ob);

static void
generate_pop_to_identifier(Declaration *decl, OpcodeBuf *ob)
{
    if (decl->is_local) {
        generate_code(ob, DVM_POP_STACK_INT
                      + get_opcode_type_offset(decl->type->basic_type),
                      decl->variable_index);
    } else {
        generate_code(ob, DVM_POP_STATIC_INT
                      + get_opcode_type_offset(decl->type->basic_type),
                      decl->variable_index);
    }
}

static void
generate_assign_expression(DVM_Executable *exe, Block *block,
                           Expression *expr, OpcodeBuf *ob,
                           DVM_Boolean is_toplevel)
{
    if (expr->u.assign_expression.operator != NORMAL_ASSIGN) {
        generate_identifier_expression(exe, block, 
                                       &expr->u.assign_expression.left
                                       ->u.identifier,
                                       ob);
    }
    generate_expression(exe, block, expr->u.assign_expression.operand, ob);

    switch (expr->u.assign_expression.operator) {
    case NORMAL_ASSIGN : /* FALLTHRU */
        break;
    case ADD_ASSIGN:
        generate_code(ob, DVM_ADD_INT
                      + get_opcode_type_offset(expr->type->basic_type));
        break;
    case SUB_ASSIGN:
        generate_code(ob, DVM_SUB_INT
                      + get_opcode_type_offset(expr->type->basic_type));
        break;
    case MUL_ASSIGN:
        generate_code(ob, DVM_MUL_INT
                      + get_opcode_type_offset(expr->type->basic_type));
        break;
    case DIV_ASSIGN:
        generate_code(ob, DVM_DIV_INT
                      + get_opcode_type_offset(expr->type->basic_type));
        break;
    case MOD_ASSIGN:
        generate_code(ob, DVM_MOD_INT
                      + get_opcode_type_offset(expr->type->basic_type));
        break;
    default:
        DBG_assert(0, ("operator..%d\n", expr->u.assign_expression.operator));
    }

    if (!is_toplevel) {
        generate_code(ob, DVM_DUPLICATE);
    }
    generate_pop_to_identifier(expr->u.assign_expression.left
                               ->u.identifier.u.declaration,
                               ob);
}

static void
generate_binary_expression(DVM_Executable *exe, Block *block,
                           Expression *expr, DVM_Opcode code,
                           OpcodeBuf *ob)
{
    DBG_assert(expr->u.binary_expression.left->type->basic_type
               == expr->u.binary_expression.right->type->basic_type,
               ("left..%d, right..%d",
                expr->u.binary_expression.left->type->basic_type,
                expr->u.binary_expression.right->type->basic_type));

    generate_expression(exe, block, expr->u.binary_expression.left, ob);
    generate_expression(exe, block, expr->u.binary_expression.right, ob);
    generate_code(ob, code
                  + get_opcode_type_offset(expr->u.binary_expression.left
                                           ->type->basic_type));
}

static int
get_label(OpcodeBuf *ob)
{
    int ret;

    if (ob->label_table_alloc_size < ob->label_table_size + 1) {
        ob->label_table = MEM_realloc(ob->label_table,
                                      (ob->label_table_alloc_size
                                       + LABEL_TABLE_ALLOC_SIZE)
                                      * sizeof(LabelTable));
        ob->label_table_alloc_size += LABEL_TABLE_ALLOC_SIZE;
    }
    ret = ob->label_table_size;
    ob->label_table_size++;

    return ret;
}

static void
set_label(OpcodeBuf *ob, int label)
{
    ob->label_table[label].label_address = ob->size;
}

static void
generate_logical_and_expression(DVM_Executable *exe, Block *block,
                                Expression *expr,
                                OpcodeBuf *ob)
{
    int false_label;

    false_label = get_label(ob);
    generate_expression(exe, block, expr->u.binary_expression.left, ob);
    generate_code(ob, DVM_DUPLICATE);
    generate_code(ob, DVM_JUMP_IF_FALSE, false_label);
    generate_expression(exe, block, expr->u.binary_expression.right, ob);
    generate_code(ob, DVM_LOGICAL_AND);
    set_label(ob, false_label);
}

static void
generate_logical_or_expression(DVM_Executable *exe, Block *block,
                               Expression *expr,
                               OpcodeBuf *ob)
{
    int true_label;

    true_label = get_label(ob);
    generate_expression(exe, block, expr->u.binary_expression.left, ob);
    generate_code(ob, DVM_DUPLICATE);
    generate_code(ob, DVM_JUMP_IF_TRUE, true_label);
    generate_expression(exe, block, expr->u.binary_expression.right, ob);
    generate_code(ob, DVM_LOGICAL_OR);
    set_label(ob, true_label);
}

static void
generate_cast_expression(DVM_Executable *exe, Block *block,
                         Expression *expr, OpcodeBuf *ob)
{
    generate_expression(exe, block, expr->u.cast.operand, ob);
    switch (expr->u.cast.type) {
    case INT_TO_DOUBLE_CAST:
        generate_code(ob, DVM_CAST_INT_TO_DOUBLE);
        break;
    case DOUBLE_TO_INT_CAST:
        generate_code(ob, DVM_CAST_DOUBLE_TO_INT);
        break;
    case BOOLEAN_TO_STRING_CAST:
        generate_code(ob, DVM_CAST_BOOLEAN_TO_STRING);
        break;
    case INT_TO_STRING_CAST:
        generate_code(ob, DVM_CAST_INT_TO_STRING);
        break;
    case DOUBLE_TO_STRING_CAST:
        generate_code(ob, DVM_CAST_DOUBLE_TO_STRING);
        break;
    default:
        DBG_assert(0, ("expr->u.cast.type..%d", expr->u.cast.type));
    }
}

static void
generate_inc_dec_expression(DVM_Executable *exe, Block *block,
                            Expression *expr, ExpressionKind kind,
                            OpcodeBuf *ob, DVM_Boolean is_toplevel)
{
    generate_expression(exe, block, expr->u.inc_dec.operand, ob);

    if (kind == INCREMENT_EXPRESSION) {
        generate_code(ob, DVM_INCREMENT);
    } else {
        DBG_assert(kind == DECREMENT_EXPRESSION, ("kind..%d\n", kind));
        generate_code(ob, DVM_DECREMENT);
    }
    if (!is_toplevel) {
        generate_code(ob, DVM_DUPLICATE);
    }
    generate_pop_to_identifier(expr->u.inc_dec.operand
                               ->u.identifier.u.declaration,
                               ob);
}

static void
generate_function_call_expression(DVM_Executable *exe, Block *block,
                                  FunctionCallExpression *fce,
                                  OpcodeBuf *ob)
{
    ArgumentList *arg_pos;

    for (arg_pos = fce->argument; arg_pos; arg_pos = arg_pos->next) {
        generate_expression(exe, block, arg_pos->expression, ob);
    }
    generate_expression(exe, block, fce->function, ob);
    generate_code(ob, DVM_INVOKE);
}

static void
generate_expression(DVM_Executable *exe, Block *current_block,
                    Expression *expr, OpcodeBuf *ob)
{
    switch (expr->kind) {
    case BOOLEAN_EXPRESSION:
        generate_boolean_expression(exe, expr->u.boolean_value, ob);
        break;
    case INT_EXPRESSION:
        generate_int_expression(exe, expr->u.int_value, ob);
        break;
    case DOUBLE_EXPRESSION:
        generate_double_expression(exe, expr->u.double_value, ob);
        break;
    case STRING_EXPRESSION:
        generate_string_expression(exe, expr->u.string_value, ob);
        break;
    case IDENTIFIER_EXPRESSION:
        generate_identifier_expression(exe, current_block,
                                       &expr->u.identifier, ob);
        break;
    case COMMA_EXPRESSION:
        generate_expression(exe, current_block, expr->u.comma.left, ob);
        generate_expression(exe, current_block, expr->u.comma.right, ob);
        break;
    case ASSIGN_EXPRESSION:
        generate_assign_expression(exe, current_block, expr, ob, DVM_FALSE);
        break;
    case ADD_EXPRESSION:
        generate_binary_expression(exe, current_block, expr,
                                   DVM_ADD_INT, ob);
        break;
    case SUB_EXPRESSION:
        generate_binary_expression(exe, current_block, expr,
                                   DVM_SUB_INT, ob);
        break;
    case MUL_EXPRESSION:
        generate_binary_expression(exe, current_block, expr,
                                   DVM_MUL_INT, ob);
        break;
    case DIV_EXPRESSION:
        generate_binary_expression(exe, current_block, expr,
                                   DVM_DIV_INT, ob);
        break;
    case MOD_EXPRESSION:
        generate_binary_expression(exe, current_block, expr,
                                   DVM_MOD_INT, ob);
        break;
    case EQ_EXPRESSION:
        generate_binary_expression(exe, current_block, expr,
                                   DVM_EQ_INT, ob);
        break;
    case NE_EXPRESSION:
        generate_binary_expression(exe, current_block, expr,
                                   DVM_NE_INT, ob);
        break;
    case GT_EXPRESSION:
        generate_binary_expression(exe, current_block, expr,
                                   DVM_GT_INT, ob);
        break;
    case GE_EXPRESSION:
        generate_binary_expression(exe, current_block, expr,
                                   DVM_GE_INT, ob);
        break;
    case LT_EXPRESSION:
        generate_binary_expression(exe, current_block, expr,
                                   DVM_LT_INT, ob);
        break;
    case LE_EXPRESSION:
        generate_binary_expression(exe, current_block, expr,
                                   DVM_LE_INT, ob);
        break;
    case LOGICAL_AND_EXPRESSION:
        generate_logical_and_expression(exe, current_block, expr, ob);
        break;
    case LOGICAL_OR_EXPRESSION:
        generate_logical_or_expression(exe, current_block, expr, ob);
        break;
    case MINUS_EXPRESSION:
        generate_expression(exe, current_block, expr->u.minus_expression, ob);
        generate_code(ob, DVM_MINUS_INT
                      + get_opcode_type_offset(expr->type->basic_type));
        break;
    case LOGICAL_NOT_EXPRESSION:
        generate_expression(exe, current_block, expr->u.logical_not, ob);
        generate_code(ob, DVM_LOGICAL_NOT);
        break;
    case FUNCTION_CALL_EXPRESSION:
        generate_function_call_expression(exe, current_block,
                                          &expr->u.function_call_expression,
                                          ob);
        break;
    case MEMBER_EXPRESSION:
        break;
    case NULL_EXPRESSION:
        break;
    case ARRAY_EXPRESSION:
        break;
    case INDEX_EXPRESSION:
        break;
    case INCREMENT_EXPRESSION:  /* FALLTHRU */
    case DECREMENT_EXPRESSION:
        generate_inc_dec_expression(exe, current_block, expr, expr->kind,
                                    ob, DVM_FALSE);
        break;
    case CAST_EXPRESSION:
        generate_cast_expression(exe, current_block, expr, ob);
        break;
    case EXPRESSION_KIND_COUNT_PLUS_1:  /* FALLTHRU */
    default:
        DBG_assert(0, ("expr->kind..%d", expr->kind));
    }
}

static void
generate_expression_statement(DVM_Executable *exe, Block *block,
                              Expression *expr, OpcodeBuf *ob)
{
    if (expr->kind == ASSIGN_EXPRESSION) {
        generate_assign_expression(exe, block, expr, ob, DVM_TRUE);
    } else if (expr->kind == INCREMENT_EXPRESSION
               || expr->kind == DECREMENT_EXPRESSION) {
        generate_inc_dec_expression(exe, block, expr, expr->kind, ob,
                                    DVM_TRUE);
    } else {
        generate_expression(exe, block, expr, ob);
        generate_code(ob, DVM_POP);
    }
}

static void generate_statement_list(DVM_Executable *exe, Block *current_block,
                                    StatementList *statement_list,
                                    OpcodeBuf *ob);

static void
generate_if_statement(DVM_Executable *exe, Block *block,
                      IfStatement *if_s, OpcodeBuf *ob)
{
    int if_false_label;
    int end_label;
    Elsif *elsif;

    generate_expression(exe, block, if_s->condition, ob);
    if_false_label = get_label(ob);
    generate_code(ob, DVM_JUMP_IF_FALSE, if_false_label);
    generate_statement_list(exe, if_s->then_block,
                            if_s->then_block->statement_list, ob);
    end_label = get_label(ob);
    generate_code(ob, DVM_JUMP, end_label);
    set_label(ob, if_false_label);

    for (elsif = if_s->elsif_list; elsif; elsif = elsif->next) {
        generate_expression(exe, block, elsif->condition, ob);
        if_false_label = get_label(ob);
        generate_code(ob, DVM_JUMP_IF_FALSE, if_false_label);
        generate_statement_list(exe, elsif->block,
                                elsif->block->statement_list, ob);
        generate_code(ob, DVM_JUMP, end_label);
        set_label(ob, if_false_label);
    }
    if (if_s->else_block) {
        generate_statement_list(exe, if_s->else_block,
                                if_s->else_block->statement_list,
                                ob);
    }
    set_label(ob, end_label);
}

static void
generate_while_statement(DVM_Executable *exe, Block *block,
                         WhileStatement *while_s, OpcodeBuf *ob)
{
    int loop_label;

    loop_label = get_label(ob);
    set_label(ob, loop_label);

    generate_expression(exe, block, while_s->condition, ob);

    while_s->block->parent.statement.break_label = get_label(ob);
    while_s->block->parent.statement.continue_label = get_label(ob);

    generate_code(ob, DVM_JUMP_IF_FALSE,
                  while_s->block->parent.statement.break_label);
    generate_statement_list(exe, while_s->block,
                            while_s->block->statement_list, ob);

    set_label(ob, while_s->block->parent.statement.continue_label);
    generate_code(ob, DVM_JUMP, loop_label);
    set_label(ob, while_s->block->parent.statement.break_label);
}

static void
generate_for_statement(DVM_Executable *exe, Block *block,
                       ForStatement *for_s, OpcodeBuf *ob)
{
    int loop_label;

    if (for_s->init) {
        generate_expression_statement(exe, block, for_s->init, ob);
    }
    loop_label = get_label(ob);
    set_label(ob, loop_label);

    if (for_s->condition) {
        generate_expression(exe, block, for_s->condition, ob);
    }

    for_s->block->parent.statement.break_label = get_label(ob);
    for_s->block->parent.statement.continue_label = get_label(ob);

    if (for_s->condition) {
        generate_code(ob, DVM_JUMP_IF_FALSE,
                      for_s->block->parent.statement.break_label);
    }

    generate_statement_list(exe, for_s->block,
                            for_s->block->statement_list, ob);
    set_label(ob, for_s->block->parent.statement.continue_label);

    if (for_s->post) {
        generate_expression_statement(exe, block, for_s->post, ob);
    }

    generate_code(ob, DVM_JUMP, loop_label);
    set_label(ob, for_s->block->parent.statement.break_label);
}

static void
generate_return_statement(DVM_Executable *exe, Block *block,
                          ReturnStatement *return_s, OpcodeBuf *ob)
{
    DBG_assert(return_s->return_value != NULL, ("return value is null."));

    generate_expression(exe, block, return_s->return_value, ob);
    generate_code(ob, DVM_RETURN);
}

static void
generate_break_statement(DVM_Executable *exe, Block *block,
                         Statement *statement, OpcodeBuf *ob)
{
    BreakStatement *break_s = &statement->u.break_s;
    Block       *block_p;

    for (block_p = block; block_p; block_p = block_p->outer_block) {
        if (block_p->type != WHILE_STATEMENT_BLOCK
            && block_p->type != FOR_STATEMENT_BLOCK)
            continue;

        if (break_s->label == NULL) {
            break;
        }

        if (block_p->type == WHILE_STATEMENT_BLOCK) {
            if (block_p->parent.statement.statement->u.while_s.label == NULL)
                continue;

            if (!strcmp(break_s->label,
                        block_p->parent.statement.statement
                        ->u.while_s.label)) {
                break;
            }
        } else if (block_p->type == FOR_STATEMENT_BLOCK) {
            if (block_p->parent.statement.statement->u.for_s.label == NULL)
                continue;

            if (!strcmp(break_s->label,
                        block_p->parent.statement.statement
                        ->u.for_s.label)) {
                break;
            }
        }
    }
    if (block_p == NULL) {
        dkc_compile_error(statement->line_number,
                          LABEL_NOT_FOUND_ERR,
                          STRING_MESSAGE_ARGUMENT, "label", break_s->label,
                          MESSAGE_ARGUMENT_END);
    }

    generate_code(ob, DVM_JUMP,
                  block_p->parent.statement.break_label);

}

static void
generate_continue_statement(DVM_Executable *exe, Block *block,
                            Statement *statement, OpcodeBuf *ob)
{
    ContinueStatement *continue_s = &statement->u.continue_s;
    Block       *block_p;
    

    for (block_p = block; block_p; block_p = block_p->outer_block) {
        if (block_p->type != WHILE_STATEMENT_BLOCK
            && block_p->type != FOR_STATEMENT_BLOCK)
            continue;

        if (continue_s->label == NULL) {
            break;
        }

        if (block_p->type == WHILE_STATEMENT_BLOCK) {
            if (block_p->parent.statement.statement->u.while_s.label == NULL)
                continue;

            if (!strcmp(continue_s->label,
                        block_p->parent.statement.statement
                        ->u.while_s.label)) {
                break;
            }
        } else if (block_p->type == FOR_STATEMENT_BLOCK) {
            if (block_p->parent.statement.statement->u.for_s.label == NULL)
                continue;

            if (!strcmp(continue_s->label,
                        block_p->parent.statement.statement
                        ->u.for_s.label)) {
                break;
            }
        }
    }
    if (block_p == NULL) {
        dkc_compile_error(statement->line_number,
                          LABEL_NOT_FOUND_ERR,
                          STRING_MESSAGE_ARGUMENT, "label", continue_s->label,
                          MESSAGE_ARGUMENT_END);
    }
    generate_code(ob, DVM_JUMP,
                  block_p->parent.statement.continue_label);
}

static void
generate_initializer(DVM_Executable *exe, Block *block,
                     Declaration *decl, OpcodeBuf *ob)
{
    if (decl->initializer == NULL)
        return;

    generate_expression(exe, block, decl->initializer, ob);
    generate_pop_to_identifier(decl, ob);
}

static void
generate_statement_list(DVM_Executable *exe, Block *current_block,
                        StatementList *statement_list,
                        OpcodeBuf *ob)
{
    StatementList *pos;

    for (pos = statement_list; pos; pos = pos->next) {
        switch (pos->statement->type) {
        case EXPRESSION_STATEMENT:
            generate_expression_statement(exe, current_block,
                                          pos->statement->u.expression_s, ob);
            break;
        case IF_STATEMENT:
            generate_if_statement(exe, current_block,
                                  &pos->statement->u.if_s, ob);
            break;
        case WHILE_STATEMENT:
            generate_while_statement(exe, current_block,
                                     &pos->statement->u.while_s, ob);
            break;
        case FOR_STATEMENT:
            generate_for_statement(exe, current_block,
                                   &pos->statement->u.for_s, ob);
            break;
        case FOREACH_STATEMENT:
            break;
        case RETURN_STATEMENT:
            generate_return_statement(exe, current_block,
                                      &pos->statement->u.return_s, ob);
            break;
        case BREAK_STATEMENT:
            generate_break_statement(exe, current_block, pos->statement, ob);
            break;
        case CONTINUE_STATEMENT:
            generate_continue_statement(exe, current_block,
                                        pos->statement, ob);
            break;
        case TRY_STATEMENT:
            break;
        case THROW_STATEMENT:
            break;
        case DECLARATION_STATEMENT:
            generate_initializer(exe, current_block,
                                 pos->statement->u.declaration_s, ob);
            break;
        case STATEMENT_TYPE_COUNT_PLUS_1: /* FALLTHRU */
        default:
            DBG_assert(0, ("pos->statement->type..", pos->statement->type));
        }
    }
}

static void
init_opcode_buf(OpcodeBuf *ob)
{
    ob->size = 0;
    ob->alloc_size = 0;
    ob->code = NULL;
    ob->label_table_size = 0;
    ob->label_table_alloc_size = 0;
    ob->label_table = NULL;
}

static void
fix_labels(OpcodeBuf *ob)
{
    int i;
    int j;
    OpcodeInfo *info;
    int label;
    int address;

    for (i = 0; i < ob->size; i++) {
        if (ob->code[i] == DVM_JUMP
            || ob->code[i] == DVM_JUMP_IF_TRUE
            || ob->code[i] == DVM_JUMP_IF_FALSE) {
            label = (ob->code[i+1] << 8) + (ob->code[i+2]);
            address = ob->label_table[label].label_address;
            ob->code[i+1] = (DVM_Byte)(address >> 8);
            ob->code[i+2] = (DVM_Byte)(address &0xff);
        }
        info = &dvm_opcode_info[ob->code[i]];
        for (j = 0; info->parameter[j] != '\0'; j++) {
            switch (info->parameter[j]) {
            case 'b':
                i++;
                break;
            case 's': /* FALLTHRU */
            case 'p':
                i += 2;
                break;
            default:
                DBG_assert(0, ("param..%s, j..%d", info->parameter, j));
            }
        }
    }
}

static DVM_Byte *
fix_opcode_buf(OpcodeBuf *ob)
{
    DVM_Byte *ret;

    fix_labels(ob);
    ret = MEM_realloc(ob->code, ob->size);
    MEM_free(ob->label_table);

    return ret;
}

static void
copy_function(FunctionDefinition *src, DVM_Function *dest)
{
    dest->type = copy_type_specifier(src->type);
    dest->name = MEM_strdup(src->name);
    dest->parameter = copy_parameter_list(src->parameter,
                                          &dest->parameter_count);
    if (src->block) {
        dest->local_variable
            = copy_local_variables(src, dest->parameter_count);
        dest->local_variable_count
            = src->local_variable_count - dest->parameter_count;
    } else {
        dest->local_variable = NULL;
        dest->local_variable_count = 0;
    }
}

static void
add_functions(DKC_Compiler *compiler, DVM_Executable *exe)
{
    FunctionDefinition  *fd;
    int         i;
    int         func_count = 0;
    OpcodeBuf           ob;

    for (fd = compiler->function_list; fd; fd = fd->next) {
        func_count++;
    }
    exe->function_count = func_count;
    exe->function = MEM_malloc(sizeof(DVM_Function) * func_count);

    for (fd = compiler->function_list, i = 0; fd; fd = fd->next, i++) {
        copy_function(fd, &exe->function[i]);
        if (fd->block) {
            init_opcode_buf(&ob);
            generate_statement_list(exe, fd->block, fd->block->statement_list,
                                    &ob);

            exe->function[i].is_implemented = DVM_TRUE;
            exe->function[i].code_size = ob.size;
            exe->function[i].code = fix_opcode_buf(&ob);
        } else {
            exe->function[i].is_implemented = DVM_FALSE;
        }
    }
}

static void
add_top_level(DKC_Compiler *compiler, DVM_Executable *exe)
{
    OpcodeBuf           ob;

    init_opcode_buf(&ob);
    generate_statement_list(exe, NULL, compiler->statement_list,
                            &ob);
    
    exe->code_size = ob.size;
    exe->code = fix_opcode_buf(&ob);
}


DVM_Executable *
dkc_generate(DKC_Compiler *compiler)
{
    DVM_Executable      *exe;

    exe = alloc_executable();

    add_global_variable(compiler, exe);
    add_functions(compiler, exe);
    add_top_level(compiler, exe);

    return exe;
}

/* [<][>][^][v][top][bottom][index][help] */