Пример #1
0
void ws_function(WsCompiler *compiler, WsBool externp, char *name,
                 WsUInt32 line, WsList *params, WsList *block)
{
    WsFunctionHash *hash;
    WsFunction *f = ws_realloc(compiler->functions,
                               ((compiler->num_functions + 1)
                                * sizeof(WsFunction)));

    if (f == NULL) {
        ws_free(name);
        ws_error_memory(compiler);
        return;
    }

    if (externp)
        compiler->num_extern_functions++;
    else
        compiler->num_local_functions++;

    compiler->functions = f;
    f = &compiler->functions[compiler->num_functions];

    f->findex = compiler->num_functions++;

    f->externp = externp;
    f->name = name;
    f->line = line;
    f->params = params;
    f->block = block;

    /* Update the function name hash. */

    hash = ws_function_hash(compiler, name);
    if (hash == NULL) {
        ws_error_memory(compiler);
        return;
    }

    if (hash->defined) {
        ws_src_error(compiler, line, "redefinition of `%s'", name);
        ws_src_error(compiler,
                     compiler->functions[hash->findex].line,
                     "`%s' previously defined here", name);
        return;
    }

    hash->defined = WS_TRUE;
    hash->findex = f->findex;
}
Пример #2
0
void ws_pragma_use(WsCompilerPtr compiler, WsUInt32 line, char *identifier,
                   WsUtf8String *url)
{
    WsPragmaUse *u = ws_calloc(1, sizeof(*u));
    WsPragmaUse *uold;

    /* Do we already know this pragma? */
    uold = ws_hash_get(compiler->pragma_use_hash, identifier);
    if (uold) {
        ws_src_error(compiler, line, "redefinition of pragma `%s'", identifier);
        ws_src_error(compiler, uold->line, "`%s' previously defined here",
                     identifier);
        goto error_cleanup;
    }

    if (u == NULL)
        goto error;

    u->line = line;

    /* Insert the URL to the byte-code module. */
    if (!ws_bc_add_const_utf8_string(compiler->bc, &u->urlindex, url->data,
                                     url->len))
        goto error;

    /* Add it to the use pragma hash. */
    if (!ws_hash_put(compiler->pragma_use_hash, identifier, u))
        goto error;

    /* Cleanup. */

    ws_lexer_free_block(compiler, identifier);
    ws_lexer_free_utf8(compiler, url);

    return;

    /* Error handling. */

error:

    ws_error_memory(compiler);

error_cleanup:

    ws_free(u);
    ws_lexer_free_block(compiler, identifier);
    ws_lexer_free_utf8(compiler, url);
}
Пример #3
0
static WsUInt32 buffer_to_int(WsCompilerPtr compiler, WsBuffer *buffer)
{
    unsigned char *p;
    unsigned long value;

    /* Terminate the string. */
    if (!ws_buffer_append_space(buffer, &p, 1)) {
        ws_error_memory(compiler);
        return 0;
    }
    p[0] = '\0';

    /* Convert the buffer into an integer number.  The base is taken
       from the bufer. */
    errno = 0;
    value = strtoul((char *) ws_buffer_ptr(buffer), NULL, 0);

    /* Check for overflow.  We accept WS_INT32_MAX + 1 because we might
     * be parsing the numeric part of '-2147483648'. */
    if (errno == ERANGE || value > (WsUInt32) WS_INT32_MAX + 1)
        ws_src_error(compiler, 0, "integer literal too large");

    /* All done. */
    return (WsUInt32) value;
}
Пример #4
0
WsNamespace *ws_variable_define(WsCompilerPtr compiler, WsUInt32 line,
                                WsBool variablep, char *name)
{
    WsNamespace *ns;

    /* Is the symbol already defined? */
    ns = ws_hash_get(compiler->variables_hash, name);
    if (ns) {
        ws_src_error(compiler, line, "redeclaration of `%s'", name);
        ws_src_error(compiler, ns->line, "`%s' previously declared here", name);
        return NULL;
    }

    /* Can we still define more variables? */
    if (compiler->next_vindex > 255) {
        /* No we can't. */
        ws_src_error(compiler, line, "too many local variables");
        return NULL;
    }

    ns = ws_calloc(1, sizeof(*ns));
    if (ns == NULL) {
        ws_error_memory(compiler);
        return NULL;
    }

    ns->line = line;
    ns->vindex = compiler->next_vindex++;

    if (!ws_hash_put(compiler->variables_hash, name, ns)) {
        ws_free(ns);
        ws_error_memory(compiler);
        return NULL;
    }

    return ns;
}
Пример #5
0
void ws_expr_linearize(WsCompiler *compiler, WsExpression *expr)
{
    WsListItem *li;
    WsAsmIns *ins;

    switch (expr->type) {
    case WS_EXPR_COMMA:
        /* Linearize left. */
        ws_expr_linearize(compiler, expr->u.comma.left);

        /* Pop its result. */
        ws_asm_link(compiler, ws_asm_ins(compiler, expr->line, WS_ASM_POP));

        /* Linearize right */
        ws_expr_linearize(compiler, expr->u.comma.right);
        break;

    case WS_EXPR_ASSIGN:
        {
            WsNamespace *ns = ws_variable_lookup(compiler,
                                                 expr->u.assign.identifier);

            if (ns == NULL) {
                /* Unknown identifier. */
                ws_src_error(compiler, expr->line, "unknown variable `%s'",
                             expr->u.symbol);
                return;
            }

            if (expr->u.assign.op == '=') {
                /* Evaluate the expression. */
                ws_expr_linearize(compiler, expr->u.assign.expr);

                /* Store the value to the variable. */
                ws_asm_link(compiler,
                            ws_asm_variable(compiler, expr->line,
                                            WS_ASM_P_STORE_VAR, ns->vindex));
            } else if (expr->u.assign.op == tADDA) {
                /* Linearize the expression. */
                ws_expr_linearize(compiler, expr->u.assign.expr);

                /* Add it to the variable. */
                ws_asm_link(compiler,
                            ws_asm_variable(compiler, expr->line,
                                            WS_ASM_ADD_ASG, ns->vindex));
            } else if (expr->u.assign.op == tSUBA) {
                /* Linearize the expression. */
                ws_expr_linearize(compiler, expr->u.assign.expr);

                /* Substract it from the variable. */
                ws_asm_link(compiler,
                            ws_asm_variable(compiler, expr->line,
                                            WS_ASM_SUB_ASG, ns->vindex));
            } else {
                /* Load the old value from the variable. */
                ws_asm_link(compiler,
                            ws_asm_variable(compiler, expr->line,
                                            WS_ASM_P_LOAD_VAR, ns->vindex));

                /* Evaluate the expression. */
                ws_expr_linearize(compiler, expr->u.assign.expr);

                /* Perform the operand. */
                ins = NULL;
                switch (expr->u.assign.op) {
                case tMULA:
                    ins = ws_asm_ins(compiler, expr->line, WS_ASM_MUL);
                    break;

                case tDIVA:
                    ins = ws_asm_ins(compiler, expr->line, WS_ASM_DIV);
                    break;

                case tREMA:
                    ins = ws_asm_ins(compiler, expr->line, WS_ASM_REM);
                    break;

                case tADDA:
                    ins = ws_asm_ins(compiler, expr->line, WS_ASM_ADD);
                    break;

                case tSUBA:
                    ins = ws_asm_ins(compiler, expr->line, WS_ASM_SUB);
                    break;

                case tLSHIFTA:
                    ins = ws_asm_ins(compiler, expr->line, WS_ASM_B_LSHIFT);
                    break;

                case tRSSHIFTA:
                    ins = ws_asm_ins(compiler, expr->line, WS_ASM_B_RSSHIFT);
                    break;

                case tRSZSHIFTA:
                    ins = ws_asm_ins(compiler, expr->line, WS_ASM_B_RSZSHIFT);
                    break;

                case tANDA:
                    ins = ws_asm_ins(compiler, expr->line, WS_ASM_B_AND);
                    break;

                case tXORA:
                    ins = ws_asm_ins(compiler, expr->line, WS_ASM_B_XOR);
                    break;

                case tORA:
                    ins = ws_asm_ins(compiler, expr->line, WS_ASM_B_OR);
                    break;

                case tIDIVA:
                    ins = ws_asm_ins(compiler, expr->line, WS_ASM_IDIV);
                    break;

                default:
                    ws_fatal("ws_expr_linearize(): unknown assignment operand %x",
                             expr->u.assign.op);
                    break;
                }
                ws_asm_link(compiler, ins);

                /* Store the value to the variable. */
                ws_asm_link(compiler,
                            ws_asm_variable(compiler, expr->line,
                                            WS_ASM_P_STORE_VAR, ns->vindex));
            }
            /* The value of the assignment expression is the value
               assigned.  So, we must load the value from the variable.
               This would also be a good place for the `dup' operand but
               we lose since we don't have it. */
            ws_asm_link(compiler, ws_asm_variable(compiler, expr->line,
                                                  WS_ASM_P_LOAD_VAR, ns->vindex));
        }
        break;

    case WS_EXPR_CONDITIONAL:
        {
            WsAsmIns *l_else = ws_asm_label(compiler, expr->line);
            WsAsmIns *l_end = ws_asm_label(compiler, expr->line);

            /* Linearize condition. */
            ws_expr_linearize(compiler, expr->u.conditional.e_cond);

            /* If the result if false, jump to the else-branch. */
            ws_asm_link(compiler, ws_asm_branch(compiler, expr->line,
                                                WS_ASM_P_TJUMP, l_else));

            /* Linearize the then-expression and jump out. */
            ws_expr_linearize(compiler, expr->u.conditional.e_then);
            ws_asm_link(compiler, ws_asm_branch(compiler, expr->line,
                                                WS_ASM_P_JUMP, l_end));

            /* The else-branch. */
            ws_asm_link(compiler, l_else);
            ws_expr_linearize(compiler, expr->u.conditional.e_else);

            /* Insert the end label. */
            ws_asm_link(compiler, l_end);
        }
        break;

    case WS_EXPR_LOGICAL:
        {
            WsAsmIns *l_out = ws_asm_label(compiler, expr->line);

            /* Linearize the left-hand size expression. */
            ws_expr_linearize(compiler, expr->u.logical.left);

            /* Short-circuit check.  The type of the logical expression is
                      the short-circuit byte-code operand. */
            ws_asm_link(compiler, ws_asm_ins(compiler, expr->line,
                                             expr->u.logical.type));
            ws_asm_link(compiler, ws_asm_branch(compiler, expr->line,
                                                WS_ASM_P_TJUMP, l_out));

            /* Linearize the right-hand size expression. */
            ws_expr_linearize(compiler, expr->u.logical.right);

            /* The result of a logical expression should be boolean.
             * Control statements do automatic conversion, but typeof()
	     * does not. */
            ws_asm_link(compiler, ws_asm_ins(compiler, expr->line,
                                         WS_ASM_TOBOOL));

            /* Insert the end label. */
            ws_asm_link(compiler, l_out);
        }
        break;

    case WS_EXPR_BINARY:
        /* Linearize left and right. */
        ws_expr_linearize(compiler, expr->u.binary.left);
        ws_expr_linearize(compiler, expr->u.binary.right);

        /* The type of the binary expression is the byte-code opcode. */
        ws_asm_link(compiler, ws_asm_ins(compiler, expr->line,
                                         expr->u.binary.type));
        break;

    case WS_EXPR_UNARY:
        /* Linearize the expression. */
        ws_expr_linearize(compiler, expr->u.unary.expr);

        /* The type of the unary expression is the byte-code opcode. */
        ws_asm_link(compiler, ws_asm_ins(compiler, expr->line,
                                         expr->u.unary.type));
        break;

    case WS_EXPR_UNARY_VAR:
        {
            WsNamespace *ns = ws_variable_lookup(compiler,
                                                 expr->u.unary_var.variable);
            if (ns == NULL) {
                /* An unknown identifier. */
                ws_src_error(compiler, expr->line, "unknown variable `%s'",
                             expr->u.unary_var.variable);
                return;
            }

            /* First, do the operation. */
            if (expr->u.unary_var.addp)
                ws_asm_link(compiler,
                            ws_asm_variable(compiler, expr->line, WS_ASM_P_INCR_VAR,
                                            ns->vindex));
            else
                ws_asm_link(compiler,
                            ws_asm_variable(compiler, expr->line, WS_ASM_DECR_VAR,
                                            ns->vindex));

            /* Second, load the new value of the variable. */
            ws_asm_link(compiler, ws_asm_variable(compiler, expr->line,
                                                  WS_ASM_P_LOAD_VAR, ns->vindex));
        }
        break;

    case WS_EXPR_POSTFIX_VAR:
        {
            WsNamespace *ns = ws_variable_lookup(compiler,
                                                 expr->u.postfix_var.variable);
            if (ns == NULL) {
                /* An unknown identifier. */
                ws_src_error(compiler, expr->line, "unknown variable `%s'",
                             expr->u.postfix_var.variable);
                return;
            }

            /* First, load the old value of the variable. */
            ws_asm_link(compiler, ws_asm_variable(compiler, expr->line,
                                                  WS_ASM_P_LOAD_VAR, ns->vindex));

            /* Second, do the operation. */
            if (expr->u.unary_var.addp)
                ws_asm_link(compiler,
                            ws_asm_variable(compiler, expr->line, WS_ASM_P_INCR_VAR,
                                            ns->vindex));
            else
                ws_asm_link(compiler,
                            ws_asm_variable(compiler, expr->line, WS_ASM_DECR_VAR,
                                            ns->vindex));
        }
        break;

    case WS_EXPR_CALL:
        /* First, evaluate the arguments. */
        for (li = expr->u.call.arguments->head; li; li = li->next)
            ws_expr_linearize(compiler, li->data);

        /* Second, emit the call instruction. */
        switch (expr->u.call.type) {
        case ' ': 		/* LocalScriptFunctionCall */
            {
                WsFunctionHash *f = ws_function_hash(compiler, expr->u.call.name);

                if (f == NULL || !f->defined)
                {
                    ws_src_error(compiler, expr->line,
                                 "unknown local function `%s'",
                                 expr->u.call.name);
                    return;
                }

                /* Check that the function is called with correct amount
                          of arguments. */
                if (expr->u.call.arguments->num_items
                    != compiler->functions[f->findex].params->num_items)
                {
                    ws_src_error(compiler, expr->line,
                                 "invalid amount of arguments for `%s': "
                                 "expected %u, got %u",
                                 expr->u.call.name,
                                 compiler->functions[f->findex].params->num_items,
                                 expr->u.call.arguments->num_items);
                    return;
                }

                /* Emit assembler. */
                ws_asm_link(compiler, ws_asm_call(compiler, expr->line,
                                                  f->findex));
            }
            break;

        case '#': 		/* ExternalScriptFunctionCall */
            {
                WsPragmaUse *use = ws_hash_get(compiler->pragma_use_hash,
                                               expr->u.call.base);
                WsUInt16 findex;

                if (use == NULL)
                {
                    ws_src_error(compiler, expr->line,
                                 "unknown external compilation unit `%s'",
                                 expr->u.call.base);
                    return;
                }

                /* Insert the function name to the byte-code pool. */
                if (!ws_bc_add_const_utf8_string(
                        compiler->bc, &findex,
                        (unsigned char *) expr->u.call.name,
                        strlen(expr->u.call.name)))
                {
                    ws_error_memory(compiler);
                    return;
                }

                /* Emit assembler. */
                ws_asm_link(compiler,
                            ws_asm_call_url(compiler, expr->line,
                                            findex, use->urlindex,
                                            expr->u.call.arguments->num_items));
            }
            break;

        case '.': 		/* LibraryFunctionCall */
            {
                WsUInt16 lindex;
                WsUInt8 findex;
                WsUInt8 num_args;
                WsBool lindex_found;
                WsBool findex_found;

                if (!ws_stdlib_function(expr->u.call.base, expr->u.call.name,
                                        &lindex, &findex, &num_args,
                                        &lindex_found, &findex_found))
                {
                    if (!lindex_found)
                        ws_src_error(compiler, expr->line,
                                     "unknown system library `%s'",
                                     expr->u.call.base);
                    else
                        ws_src_error(compiler, expr->line,
                                     "unknown library function `%s.%s'",
                                     expr->u.call.base, expr->u.call.name);

                    return;
                }
                /* Check the argument count. */
                if (expr->u.call.arguments->num_items != num_args)
                {
                    ws_src_error(compiler, expr->line,
                                 "invalid amount of arguments for `%s.%s': "
                                 "expected %u, got %u",
                                 expr->u.call.base, expr->u.call.name,
                                 num_args, expr->u.call.arguments->num_items);
                    return;
                }

                /* Emit assembler. */
                ws_asm_link(compiler, ws_asm_call_lib(compiler, expr->line, findex,
                                                      lindex));
            }
            break;

        default:
            ws_fatal("ws_expr_linearize(): unknown call expression type %x",
                     expr->u.call.type);
            break;
        }
        break;

    case WS_EXPR_SYMBOL:
        {
            WsNamespace *ns = ws_variable_lookup(compiler, expr->u.symbol);

            if (ns == NULL) {
                /* An unknown identifier. */
                ws_src_error(compiler, expr->line, "unknown variable `%s'",
                             expr->u.symbol);
                return;
            }

            /* Create a load instruction for the variable. */
            ws_asm_link(compiler, ws_asm_variable(compiler, expr->line,
                                                  WS_ASM_P_LOAD_VAR, ns->vindex));
        }
        break;

    case WS_EXPR_CONST_INVALID:
        ws_asm_link(compiler, ws_asm_ins(compiler, expr->line,
                                         WS_ASM_CONST_INVALID));
        break;

    case WS_EXPR_CONST_TRUE:
        ws_asm_link(compiler, ws_asm_ins(compiler, expr->line,
                                         WS_ASM_CONST_TRUE));
        break;

    case WS_EXPR_CONST_FALSE:
        ws_asm_link(compiler, ws_asm_ins(compiler, expr->line,
                                         WS_ASM_CONST_FALSE));
        break;


    case WS_EXPR_CONST_INTEGER:
        if (expr->u.integer.ival == 0)
            ins = ws_asm_ins(compiler, expr->line, WS_ASM_CONST_0);
        else if (expr->u.integer.ival == 1 && expr->u.integer.sign == 1)
            ins = ws_asm_ins(compiler, expr->line, WS_ASM_CONST_1);
        else {
            WsUInt16 cindex;
	    WsInt32 ival;

            if (expr->u.integer.sign >= 0) {
		if (expr->u.integer.ival > (WsUInt32) WS_INT32_MAX)
		    ws_src_error(compiler, expr->line,
                                 "integer literal too large");
                ival = expr->u.integer.ival;
	    } else {
                if (expr->u.integer.ival > (WsUInt32) WS_INT32_MAX + 1)
		    ws_src_error(compiler, expr->line, "integer too small");
                ival = - (WsInt32) expr->u.integer.ival;
	    }

            if (!ws_bc_add_const_int(compiler->bc, &cindex, ival)) {
                ws_error_memory(compiler);
                return;
            }
            ins = ws_asm_load_const(compiler, expr->line, cindex);
        }

        ws_asm_link(compiler, ins);
        break;

    case WS_EXPR_CONST_FLOAT:
        {
            WsUInt16 cindex;

            if (!ws_bc_add_const_float(compiler->bc, &cindex, expr->u.fval)) {
                ws_error_memory(compiler);
                return;
            }

            ws_asm_link(compiler, ws_asm_load_const(compiler, expr->line, cindex));
        }
        break;

    case WS_EXPR_CONST_STRING:
        if (expr->u.string.len == 0)
            ins = ws_asm_ins(compiler, expr->line, WS_ASM_CONST_ES);
        else {
            WsUInt16 cindex;

            if (!ws_bc_add_const_utf8_string(compiler->bc, &cindex,
                                             expr->u.string.data,
                                             expr->u.string.len)) {
                ws_error_memory(compiler);
                return;
            }
            ins = ws_asm_load_const(compiler, expr->line, cindex);
        }

        ws_asm_link(compiler, ins);
        break;
    }
}
Пример #6
0
void ws_stmt_linearize(WsCompiler *compiler, WsStatement *stmt)
{
    WsListItem *li;
    WsAsmIns *ins;

    switch (stmt->type) {
    case WS_STMT_BLOCK:
        for (li = stmt->u.block->head; li; li = li->next)
            ws_stmt_linearize(compiler, li->data);
        break;

    case WS_STMT_VARIABLE:
        linearize_variable_init(compiler, stmt->u.var, stmt->first_line);
        break;

    case WS_STMT_EMPTY:
        /* Nothing here. */
        break;

    case WS_STMT_EXPR:
        ws_expr_linearize(compiler, stmt->u.expr);

        /* Pop the expressions result from the stack.  Otherwise loops
           could eventually cause stack overflows. */
        ws_asm_link(compiler, ws_asm_ins(compiler, stmt->last_line, WS_ASM_POP));
        break;

    case WS_STMT_IF:
        {
            WsAsmIns *l_else = ws_asm_label(compiler,
                                            (stmt->u.s_if.s_else
                                             ? stmt->u.s_if.s_else->first_line
                                             : stmt->last_line));
            WsAsmIns *l_end = ws_asm_label(compiler, stmt->last_line);

            /* Linearize the expression. */
            ws_expr_linearize(compiler, stmt->u.s_if.expr);

            /* If the result is false, jump to the else-branch. */
            ws_asm_link(compiler, ws_asm_branch(compiler, stmt->first_line,
                                                WS_ASM_P_TJUMP, l_else));

            /* Else, execute the then-branch and jump to the end. */
            ws_stmt_linearize(compiler, stmt->u.s_if.s_then);
            ws_asm_link(compiler, ws_asm_branch(compiler, stmt->last_line,
                                                WS_ASM_P_JUMP, l_end));

            /* Then else-branch. */
            ws_asm_link(compiler, l_else);

            /* Linearize the else-branch if it is present. */
            if (stmt->u.s_if.s_else)
                ws_stmt_linearize(compiler, stmt->u.s_if.s_else);

            /* Insert the end label. */
            ws_asm_link(compiler, l_end);
        }
        break;

    case WS_STMT_FOR:
        {
            WsAsmIns *l_loop = ws_asm_label(compiler, stmt->first_line);
            WsAsmIns *l_cont = ws_asm_label(compiler, stmt->first_line);
            WsAsmIns *l_break = ws_asm_label(compiler, stmt->first_line);
            WsContBreak *cb;

            /* Store the labels to the compiler. */

            cb = ws_f_calloc(compiler->pool_stree, 1, sizeof(*cb));
            if (cb == NULL) {
                ws_error_memory(compiler);
                return;
            }

            cb->next = compiler->cont_break;
            compiler->cont_break = cb;

            cb->l_cont = l_cont;
            cb->l_break = l_break;

            /* Linearize the possible init code. */
            if (stmt->u.s_for.init)
                linearize_variable_init(compiler, stmt->u.s_for.init,
                                        stmt->first_line);
            else if (stmt->u.s_for.e1) {
                /* Linearize the init. */
                ws_expr_linearize(compiler, stmt->u.s_for.e1);

                /* Pop the result. */
                ws_asm_link(compiler, ws_asm_ins(compiler, stmt->first_line,
                                                 WS_ASM_POP));
            }

            /* Insert the loop label. */
            ws_asm_link(compiler, l_loop);

            /* Linearize the condition. */
            if (stmt->u.s_for.e2) {
                ws_expr_linearize(compiler, stmt->u.s_for.e2);

                /* If false, jump out. */
                ws_asm_link(compiler, ws_asm_branch(compiler, stmt->first_line,
                                                    WS_ASM_P_TJUMP, l_break));
            }

            /* Linearize the body statement. */
            ws_stmt_linearize(compiler, stmt->u.s_for.stmt);

            /* Link the continue label. */
            ws_asm_link(compiler, l_cont);

            /* Linearize the update expression. */
            if (stmt->u.s_for.e3) {
                ws_expr_linearize(compiler, stmt->u.s_for.e3);

                /* Pop the result. */
                ws_asm_link(compiler, ws_asm_ins(compiler, stmt->first_line,
                                                 WS_ASM_POP));
            }

            /* Jump to the loop label to check the condition. */
            ws_asm_link(compiler, ws_asm_branch(compiler, stmt->last_line,
                                                WS_ASM_P_JUMP, l_loop));

            /* Insert the break label. */
            ws_asm_link(compiler, l_break);

            /* Pop the cont-break block. */
            compiler->cont_break = compiler->cont_break->next;
        }
        break;

    case WS_STMT_WHILE:
        {
            WsAsmIns *l_cont = ws_asm_label(compiler, stmt->first_line);
            WsAsmIns *l_break = ws_asm_label(compiler,
                                             stmt->u.s_while.stmt->last_line);
            WsContBreak *cb;

            /* Store the labels to the compiler. */

            cb = ws_f_calloc(compiler->pool_stree, 1, sizeof(*cb));
            if (cb == NULL) {
                ws_error_memory(compiler);
                return;
            }

            cb->next = compiler->cont_break;
            compiler->cont_break = cb;

            cb->l_cont = l_cont;
            cb->l_break = l_break;

            /* Insert the continue label. */
            ws_asm_link(compiler, l_cont);

            /* Linearize the expression. */
            ws_expr_linearize(compiler, stmt->u.s_while.expr);

            /* If false, jump out. */
            ws_asm_link(compiler, ws_asm_branch(compiler, stmt->first_line,
                                                WS_ASM_P_TJUMP, l_break));

            /* Linearize the body statement. */
            ws_stmt_linearize(compiler, stmt->u.s_while.stmt);

            /* And jump to the continue label to check the expression. */
            ws_asm_link(compiler, ws_asm_branch(compiler, stmt->last_line,
                                                WS_ASM_P_JUMP, l_cont));

            /* Insert the break label. */
            ws_asm_link(compiler, l_break);

            /* Pop the cont-break block. */
            compiler->cont_break = compiler->cont_break->next;
        }
        break;

    case WS_STMT_CONTINUE:
        if (compiler->cont_break == NULL)
            ws_src_error(compiler, stmt->first_line,
                         "continue statement not within a loop");

        ws_asm_link(compiler, ws_asm_branch(compiler, stmt->first_line,
                                            WS_ASM_P_JUMP,
                                            compiler->cont_break->l_cont));
        break;

    case WS_STMT_BREAK:
        if (compiler->cont_break == NULL)
            ws_src_error(compiler, stmt->first_line,
                         "break statement not within a loop");

        ws_asm_link(compiler, ws_asm_branch(compiler, stmt->first_line,
                                            WS_ASM_P_JUMP,
                                            compiler->cont_break->l_break));
        break;

    case WS_STMT_RETURN:
        if (stmt->u.expr) {
            /* Linearize the return value and return it. */
            ws_expr_linearize(compiler, stmt->u.expr);
            ins = ws_asm_ins(compiler, stmt->first_line, WS_ASM_RETURN);
        } else
            /* Return an empty string. */
            ins = ws_asm_ins(compiler, stmt->first_line, WS_ASM_RETURN_ES);

        ws_asm_link(compiler, ins);
        break;
    }
}
Пример #7
0
static WsBool read_float_from_exp(WsCompiler *compiler, WsBuffer *buffer,
                                  WsFloat *result)
{
    WsUInt32 ch;
    unsigned char *p;
    int sign = '+';
    unsigned char buf[4];

    /* Do we have an exponent part. */
    if (!ws_stream_getc(compiler->input, &ch))
        goto done;
    if (ch != 'e' && ch != 'E') {
        /* No exponent part. */
        ws_stream_ungetc(compiler->input, ch);
        goto done;
    }

    /* Sign. */
    if (!ws_stream_getc(compiler->input, &ch)) {
        /* This is an error. */
        ws_src_error(compiler, 0, "truncated float literal");
        return WS_FALSE;
    }
    if (ch == '-')
        sign = '-';
    else if (ch == '+')
        sign = '+';
    else
        ws_stream_ungetc(compiler->input, ch);

    /* DecimalDigits. */
    if (!ws_stream_getc(compiler->input, &ch)) {
        ws_src_error(compiler, 0, "truncated float literal");
        return WS_FALSE;
    }
    if (!WS_IS_DECIMAL_DIGIT(ch)) {
        ws_src_error(compiler, 0, "no decimal digits in exponent part");
        return WS_FALSE;
    }

    /* Append exponent part read so far. */
    if (!ws_buffer_append_space(buffer, &p, 2)) {
        ws_error_memory(compiler);
        return WS_FALSE;
    }
    p[0] = 'e';
    p[1] = sign;

    /* Read decimal digits. */
    while (WS_IS_DECIMAL_DIGIT(ch)) {
        if (!ws_buffer_append_space(buffer, &p, 1)) {
            ws_error_memory(compiler);
            return WS_FALSE;
        }
        p[0] = (unsigned char) ch;

        if (!ws_stream_getc(compiler->input, &ch))
            /* EOF.  This is ok. */
            goto done;
    }
    /* Unget the extra character. */
    ws_stream_ungetc(compiler->input, ch);

    /* FALLTHROUGH */

done:

    if (!ws_buffer_append_space(buffer, &p, 1)) {
        ws_error_memory(compiler);
        return WS_FALSE;
    }
    p[0] = 0;

    /* Now the buffer contains a valid floating point number. */
    *result = (WsFloat) strtod((char *) ws_buffer_ptr(buffer), NULL);

    /* Check that the generated floating point number fits to
       `float32'. */
    if (*result == HUGE_VAL || *result == -HUGE_VAL
        || ws_ieee754_encode_single(*result, buf) != WS_IEEE754_OK)
        ws_src_error(compiler, 0, "floating point literal too large");

    return WS_TRUE;
}
Пример #8
0
int ws_yy_lex(YYSTYPE *yylval, YYLTYPE *yylloc, void *context)
{
    WsCompiler *compiler = (WsCompiler *) context;
    WsUInt32 ch, ch2;
    WsBuffer buffer;
    unsigned char *p;
    WsBool success;

    /* Just check that we get the correct amount of arguments. */
    gw_assert(compiler->magic == COMPILER_MAGIC);

    while (ws_stream_getc(compiler->input, &ch)) {
        /* Save the token's line number. */
        yylloc->first_line = compiler->linenum;

        switch (ch) {
        case '\t': 		/* Whitespace characters. */
        case '\v':
        case '\f':
        case ' ':
            continue;

        case '\n': 		/* Line terminators. */
        case '\r':
            if (ch == '\r' && ws_stream_getc(compiler->input, &ch2)) {
                if (ch2 != '\n')
                    ws_stream_ungetc(compiler->input, ch2);
            }
            compiler->linenum++;
            continue;

        case '!': 		/* !, != */
            if (ws_stream_getc(compiler->input, &ch2)) {
                if (ch2 == '=')
                    return tNE;

                ws_stream_ungetc(compiler->input, ch2);
            }
            return '!';

        case '%': 		/* %, %= */
            if (ws_stream_getc(compiler->input, &ch2)) {
                if (ch2 == '=')
                    return tREMA;

                ws_stream_ungetc(compiler->input, ch2);
            }
            return '%';

        case '&': 		/* &, &&, &= */
            if (ws_stream_getc(compiler->input, &ch2)) {
                if (ch2 == '&')
                    return tAND;
                if (ch2 == '=')
                    return tANDA;

                ws_stream_ungetc(compiler->input, ch2);
            }
            return '&';

        case '*': 		/* *, *= */
            if (ws_stream_getc(compiler->input, &ch2)) {
                if (ch2 == '=')
                    return tMULA;

                ws_stream_ungetc(compiler->input, ch2);
            }
            return '*';

        case '+': 		/* +, ++, += */
            if (ws_stream_getc(compiler->input, &ch2)) {
                if (ch2 == '+')
                    return tPLUSPLUS;
                if (ch2 == '=')
                    return tADDA;

                ws_stream_ungetc(compiler->input, ch2);
            }
            return '+';

        case '-': 		/* -, --, -= */
            if (ws_stream_getc(compiler->input, &ch2)) {
                if (ch2 == '-')
                    return tMINUSMINUS;
                if (ch2 == '=')
                    return tSUBA;

                ws_stream_ungetc(compiler->input, ch2);
            }
            return '-';

        case '.':
            if (ws_stream_getc(compiler->input, &ch2)) {
                if (WS_IS_DECIMAL_DIGIT(ch2)) {
                    /* DecimalFloatLiteral. */
                    ws_buffer_init(&buffer);

                    if (!ws_buffer_append_space(&buffer, &p, 2)) {
                        ws_error_memory(compiler);
                        ws_buffer_uninit(&buffer);
                        return EOF;
                    }

                    p[0] = '.';
                    p[1] = (unsigned char) ch2;

                    success = read_float_from_point(compiler, &buffer,
                                                    &yylval->vfloat);
                    ws_buffer_uninit(&buffer);

                    if (!success)
                        return EOF;

                    return tFLOAT;
                }

                ws_stream_ungetc(compiler->input, ch2);
            }
            return '.';

        case '/': 		/* /, /=, block or a single line comment */
            if (ws_stream_getc(compiler->input, &ch2)) {
                if (ch2 == '*') {
                    /* Block comment. */
                    while (1) {
                        if (!ws_stream_getc(compiler->input, &ch)) {
                            ws_src_error(compiler, 0, "EOF in comment");
                            return EOF;
                        }

                        if (ch == '\n' || ch == '\r') {
                            /* Line terminators. */
                            if (ch == '\r' && ws_stream_getc(compiler->input,
                                                             &ch2)) {
                                if (ch2 != '\n')
                                    ws_stream_ungetc(compiler->input, ch2);
                            }
                            compiler->linenum++;

                            /* Continue reading the block comment. */
                            continue;
                        }

                        if (ch == '*' && ws_stream_getc(compiler->input, &ch2)) {
                            if (ch2 == '/')
                                /* The end of the comment found. */
                                break;
                            ws_stream_ungetc(compiler->input, ch2);
                        }
                    }
                    /* Continue after the comment. */
                    continue;
                }
                if (ch2 == '/') {
                    /* Single line comment. */
                    while (1) {
                        if (!ws_stream_getc(compiler->input, &ch))
                            /* The end of input stream reached.  We accept
                               this as a valid comment terminator. */
                            break;

                        if (ch == '\n' || ch == '\r') {
                            /* Line terminators. */
                            if (ch == '\r' && ws_stream_getc(compiler->input,
                                                             &ch2)) {
                                if (ch2 != '\n')
                                    ws_stream_ungetc(compiler->input, ch2);
                            }
                            /* The end of the line (and the comment)
                                                    reached. */
                            compiler->linenum++;
                            break;
                        }
                    }
                    /* Continue after the comment. */
                    continue;
                }
                if (ch2 == '=')
                    return tDIVA;

                ws_stream_ungetc(compiler->input, ch2);
            }
            return '/';

        case '<': 		/* <, <<, <<=, <= */
            if (ws_stream_getc(compiler->input, &ch2)) {
                if (ch2 == '<') {
                    if (ws_stream_getc(compiler->input, &ch2)) {
                        if (ch2 == '=')
                            return tLSHIFTA;

                        ws_stream_ungetc(compiler->input, ch2);
                    }
                    return tLSHIFT;
                }
                if (ch2 == '=')
                    return tLE;

                ws_stream_ungetc(compiler->input, ch2);
            }
            return '<';

        case '=': 		/* =, == */
            if (ws_stream_getc(compiler->input, &ch2)) {
                if (ch2 == '=')
                    return tEQ;

                ws_stream_ungetc(compiler->input, ch2);
            }
            return '=';

        case '>': 		/* >, >=, >>, >>=, >>>, >>>= */
            if (ws_stream_getc(compiler->input, &ch2)) {
                if (ch2 == '>') {
                    if (ws_stream_getc(compiler->input, &ch2)) {
                        if (ch2 == '>') {
                            if (ws_stream_getc(compiler->input, &ch2)) {
                                if (ch2 == '=')
                                    return tRSZSHIFTA;

                                ws_stream_ungetc(compiler->input, ch2);
                            }
                            return tRSZSHIFT;
                        }
                        if (ch2 == '=')
                            return tRSSHIFTA;

                        ws_stream_ungetc(compiler->input, ch2);
                    }
                    return tRSSHIFT;
                }
                if (ch2 == '=')
                    return tGE;

                ws_stream_ungetc(compiler->input, ch2);
            }
            return '>';

        case '^': 		/* ^, ^= */
            if (ws_stream_getc(compiler->input, &ch2)) {
                if (ch2 == '=')
                    return tXORA;

                ws_stream_ungetc(compiler->input, ch2);
            }
            return '^';

        case '|': 		/* |, |=, || */
            if (ws_stream_getc(compiler->input, &ch2)) {
                if (ch2 == '=')
                    return tORA;
                if (ch2 == '|')
                    return tOR;

                ws_stream_ungetc(compiler->input, ch2);
            }
            return '|';

        case '#': 		/* The simple cases. */
        case '(':
        case ')':
        case ',':
        case ':':
        case ';':
        case '?':
        case '{':
        case '}':
        case '~':
            return (int) ch;

        case '\'': 		/* String literals. */
        case '"':
            {
                WsUInt32 string_end_ch = ch;
                WsUtf8String *str = ws_utf8_alloc();

                if (str == NULL) {
                    ws_error_memory(compiler);
                    return EOF;
                }

                while (1) {
                    if (!ws_stream_getc(compiler->input, &ch)) {
eof_in_string_literal:
                        ws_src_error(compiler, 0, "EOF in string literal");
                        ws_utf8_free(str);
                        return EOF;
                    }
                    if (ch == string_end_ch)
                        /* The end of string reached. */
                        break;

                    if (ch == '\\') {
                        /* An escape sequence. */
                        if (!ws_stream_getc(compiler->input, &ch))
                            goto eof_in_string_literal;

                        switch (ch) {
                        case '\'':
                        case '"':
                        case '\\':
                        case '/':
                            /* The character as-is. */
                            break;

                        case 'b':
                            ch = '\b';
                            break;

                        case 'f':
                            ch = '\f';
                            break;

                        case 'n':
                            ch = '\n';
                            break;

                        case 'r':
                            ch = '\r';
                            break;

                        case 't':
                            ch = '\t';
                            break;

                        case 'x':
                        case 'u':
                            {
                                int i, len;
                                int type = ch;

                                if (ch == 'x')
                                    len = 2;
                                else
                                    len = 4;

                                ch = 0;
                                for (i = 0; i < len; i++) {
                                    if (!ws_stream_getc(compiler->input, &ch2))
                                        goto eof_in_string_literal;
                                    if (!WS_IS_HEX_DIGIT(ch2)) {
                                        ws_src_error(compiler, 0,
                                                     "malformed `\\%c' escape in "
                                                     "string literal", (char) type);
                                        ch = 0;
                                        break;
                                    }
                                    ch *= 16;
                                    ch += WS_HEX_TO_INT(ch2);
                                }
                            }
                            break;

                        default:
                            if (WS_IS_OCTAL_DIGIT(ch)) {
                                int i;
                                int limit = 3;

                                ch = WS_OCTAL_TO_INT(ch);
                                if (ch > 3)
                                    limit = 2;

                                for (i = 1; i < limit; i++) {
                                    if (!ws_stream_getc(compiler->input, &ch2))
                                        goto eof_in_string_literal;
                                    if (!WS_IS_OCTAL_DIGIT(ch2)) {
                                        ws_stream_ungetc(compiler->input, ch2);
                                        break;
                                    }

                                    ch *= 8;
                                    ch += WS_OCTAL_TO_INT(ch2);
                                }
                            } else {
                                ws_src_error(compiler, 0,
                                             "unknown escape sequence `\\%c' in "
                                             "string literal", (char) ch);
                                ch = 0;
                            }
                            break;
                        }
                        /* FALLTHROUGH */
                    }

                    if (!ws_utf8_append_char(str, ch)) {
                        ws_error_memory(compiler);
                        ws_utf8_free(str);
                        return EOF;
                    }
                }

                if (!ws_lexer_register_utf8(compiler, str)) {
                    ws_error_memory(compiler);
                    ws_utf8_free(str);
                    return EOF;
                }

                gw_assert(str != NULL);
                yylval->string = str;

                return tSTRING;
            }
            break;

        default:
            /* Identifiers, keywords and number constants. */

            if (WS_IS_IDENTIFIER_LETTER(ch)) {
                WsBool got;
                int token;
                unsigned char *p;
                unsigned char *np;
                size_t len = 0;

                /* An identifier or a keyword.  We start with a 256
                 * bytes long buffer but it is expanded dynamically if
                 * needed.  However, 256 should be enought for most
                 * cases since the byte-code format limits the function
                 * names to 255 characters. */
                p = ws_malloc(256);
                if (p == NULL) {
                    ws_error_memory(compiler);
                    return EOF;
                }

                do {
                    /* Add one extra for the possible terminator
                       character. */
                    np = ws_realloc(p, len + 2);
                    if (np == NULL) {
                        ws_error_memory(compiler);
                        ws_free(p);
                        return EOF;
                    }

                    p = np;

                    /* This is ok since the only valid identifier names
                     * can be written in 7 bit ASCII. */
                    p[len++] = (unsigned char) ch;
                } while ((got = ws_stream_getc(compiler->input, &ch))
                         && (WS_IS_IDENTIFIER_LETTER(ch)
                             || WS_IS_DECIMAL_DIGIT(ch)));

                if (got)
                    /* Put back the terminator character. */
                    ws_stream_ungetc(compiler->input, ch);

                /* Is it a keyword? */
                if (lookup_keyword((char *) p, len, &token)) {
                    /* Yes it is... */
                    ws_free(p);

                    /* ...except one case: `div='. */
                    if (token == tIDIV) {
                        if (ws_stream_getc(compiler->input, &ch)) {
                            if (ch == '=')
                                return tIDIVA;

                            ws_stream_ungetc(compiler->input, ch);
                        }
                    }

                    /* Return the token value. */
                    return token;
                }

                /* It is a normal identifier.  Let's pad the name with a
                          null-character.  We have already allocated space for
                          it. */
                p[len] = '\0';

                if (!ws_lexer_register_block(compiler, p)) {
                    ws_error_memory(compiler);
                    ws_free(p);
                    return EOF;
                }

                gw_assert(p != NULL);
                yylval->identifier = (char *) p;

                return tIDENTIFIER;
            }

            if (WS_IS_NON_ZERO_DIGIT(ch)) {
                /* A decimal integer literal or a decimal float
                          literal. */

                ws_buffer_init(&buffer);
                if (!ws_buffer_append_space(&buffer, &p, 1)) {
number_error_memory:
                    ws_error_memory(compiler);
                    ws_buffer_uninit(&buffer);
                    return EOF;
                }
                p[0] = ch;

                while (ws_stream_getc(compiler->input, &ch)) {
                    if (WS_IS_DECIMAL_DIGIT(ch)) {
                        if (!ws_buffer_append_space(&buffer, &p, 1))
                            goto number_error_memory;
                        p[0] = ch;
                    } else if (ch == '.' || ch == 'e' || ch == 'E') {
                        /* DecimalFloatLiteral. */
                        if (ch == '.') {
                            if (!ws_buffer_append_space(&buffer, &p, 1))
                                goto number_error_memory;
                            p[0] = '.';

                            success = read_float_from_point(compiler, &buffer,
                                                            &yylval->vfloat);
                        } else {
                            ws_stream_ungetc(compiler->input, ch);

                            success = read_float_from_exp(compiler, &buffer,
                                                          &yylval->vfloat);
                        }
                        ws_buffer_uninit(&buffer);

                        if (!success)
                            return EOF;

                        return tFLOAT;
                    } else {
                        ws_stream_ungetc(compiler->input, ch);
                        break;
                    }
                }

                /* Now the buffer contains an integer number as a
                          string.  Let's convert it to an integer number. */
                yylval->integer = buffer_to_int(compiler, &buffer);
                ws_buffer_uninit(&buffer);

                /* Read a DecimalIntegerLiteral. */
                return tINTEGER;
            }

            if (ch == '0') {
                /* The integer constant 0, an octal number or a
                   HexIntegerLiteral. */
                if (ws_stream_getc(compiler->input, &ch2)) {
                    if (ch2 == 'x' || ch2 == 'X') {
                        /* HexIntegerLiteral. */

                        ws_buffer_init(&buffer);
                        if (!ws_buffer_append_space(&buffer, &p, 2))
                            goto number_error_memory;

                        p[0] = '0';
                        p[1] = 'x';

                        while (ws_stream_getc(compiler->input, &ch)) {
                            if (WS_IS_HEX_DIGIT(ch)) {
                                if (!ws_buffer_append_space(&buffer, &p, 1))
                                    goto number_error_memory;
                                p[0] = ch;
                            } else {
                                ws_stream_ungetc(compiler->input, ch);
                                break;
                            }
                        }

                        if (ws_buffer_len(&buffer) == 2) {
                            ws_buffer_uninit(&buffer);
                            ws_src_error(compiler, 0,
                                         "numeric constant with no digits");
                            yylval->integer = 0;
                            return tINTEGER;
                        }

                        /* Now the buffer contains an integer number as
                         * a string.  Let's convert it to an integer
                         * number. */
                        yylval->integer = buffer_to_int(compiler, &buffer);
                        ws_buffer_uninit(&buffer);

                        /* Read a HexIntegerLiteral. */
                        return tINTEGER;
                    }
                    if (WS_IS_OCTAL_DIGIT(ch2)) {
                        /* OctalIntegerLiteral. */

                        ws_buffer_init(&buffer);
                        if (!ws_buffer_append_space(&buffer, &p, 2))
                            goto number_error_memory;

                        p[0] = '0';
                        p[1] = ch2;

                        while (ws_stream_getc(compiler->input, &ch)) {
                            if (WS_IS_OCTAL_DIGIT(ch)) {
                                if (!ws_buffer_append_space(&buffer, &p, 1))
                                    goto number_error_memory;
                                p[0] = ch;
                            } else {
                                ws_stream_ungetc(compiler->input, ch);
                                break;
                            }
                        }

                        /* Convert the buffer into an intger number. */
                        yylval->integer = buffer_to_int(compiler, &buffer);
                        ws_buffer_uninit(&buffer);

                        /* Read an OctalIntegerLiteral. */
                        return tINTEGER;
                    }
                    if (ch2 == '.' || ch2 == 'e' || ch2 == 'E') {
                        /* DecimalFloatLiteral. */
                        ws_buffer_init(&buffer);

                        if (ch2 == '.') {
                            if (!ws_buffer_append_space(&buffer, &p, 1))
                                goto number_error_memory;
                            p[0] = '.';

                            success = read_float_from_point(compiler, &buffer,
                                                            &yylval->vfloat);
                        } else {
                            ws_stream_ungetc(compiler->input, ch);

                            success = read_float_from_exp(compiler, &buffer,
                                                          &yylval->vfloat);
                        }
                        ws_buffer_uninit(&buffer);

                        if (!success)
                            return EOF;

                        return tFLOAT;
                    }

                    ws_stream_ungetc(compiler->input, ch2);
                }

                /* Integer literal 0. */
                yylval->integer = 0;
                return tINTEGER;
            }

            /* Garbage found from the input stream. */
            ws_src_error(compiler, 0,
                         "garbage found from the input stream: character=0x%x",
                         ch);
            return EOF;
            break;
        }
    }

    return EOF;
}