/* ==================================================================== * The Kannel Software License, Version 1.0 * * Copyright (c) 2001-2005 Kannel Group * Copyright (c) 1998-2001 WapIT Ltd. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Kannel Group (http://www.kannel.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Kannel" and "Kannel Group" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please * contact org@kannel.org. * * 5. Products derived from this software may not be called "Kannel", * nor may "Kannel" appear in their name, without prior written * permission of the Kannel Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Kannel Group. For more information on * the Kannel Group, please see . * * Portions of this software are based upon software originally written at * WapIT Ltd., Helsinki, Finland for the Kannel project. */ /* * * wsstree.c * * Author: Markku Rossi * * Copyright (c) 1999-2000 WAPIT OY LTD. * All rights reserved. * * Syntax tree creation, manipulation and byte-code assembler * generation. * */ #include "wsint.h" #include "wsgram.h" /* TODO: Constant folding. */ /********************* Misc syntax tree structures **********************/ WsVarDec *ws_variable_declaration(WsCompilerPtr compiler, char *name, WsExpression *expr) { WsVarDec *vardec = ws_f_malloc(compiler->pool_stree, sizeof(*vardec)); if (vardec == NULL) ws_error_memory(compiler); else { vardec->name = name; vardec->expr = expr; } return vardec; } WsFormalParm *ws_formal_parameter(WsCompilerPtr compiler, WsUInt32 line, char *name) { WsFormalParm *parm = ws_f_malloc(compiler->pool_stree, sizeof(*parm)); if (parm == NULL) ws_error_memory(compiler); else { parm->line = line; parm->name = name; } return parm; } /********************* Linked list **************************************/ WsList *ws_list_new(WsCompiler *compiler) { WsList *list = ws_f_calloc(compiler->pool_stree, 1, sizeof(*list)); if (list == NULL) ws_error_memory(compiler); return list; } void ws_list_append(WsCompiler *compiler, WsList *list, void *value) { WsListItem *item; if (list == NULL) /* A recovery code for previous memory allocation problems. */ return; item = ws_f_calloc(compiler->pool_stree, 1, sizeof(*item)); if (item == NULL) { ws_error_memory(compiler); return; } item->data = value; if (list->tail) { list->tail->next = item; list->tail = item; } else list->head = list->tail = item; list->num_items++; } /********************* Namespace for arguments and locals ***************/ static void variable_hash_destructor(void *item, void *context) { ws_free(item); } WsHashPtr ws_variable_hash_create(void) { return ws_hash_create(variable_hash_destructor, NULL); } 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; } WsNamespace *ws_variable_lookup(WsCompilerPtr compiler, char *name) { return ws_hash_get(compiler->variables_hash, name); } /********************* Top-level declarations ***************************/ /* External compilation units. */ static void pragma_use_hash_destructor(void *item, void *context) { ws_free(item); } WsHashPtr ws_pragma_use_hash_create(void) { return ws_hash_create(pragma_use_hash_destructor, NULL); } 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); } /* MetaBody of the `use meta' pragmas. */ WsPragmaMetaBody *ws_pragma_meta_body(WsCompilerPtr compiler, WsUtf8String *property_name, WsUtf8String *content, WsUtf8String *scheme) { WsPragmaMetaBody *mb = ws_calloc(1, sizeof(*mb)); if (mb == NULL) { ws_error_memory(compiler); return NULL; } mb->property_name = property_name; mb->content = content; mb->scheme = scheme; return mb; } void ws_pragma_meta_body_free(WsCompilerPtr compiler, WsPragmaMetaBody *mb) { if (mb == NULL) return; ws_lexer_free_utf8(compiler, mb->property_name); ws_lexer_free_utf8(compiler, mb->content); ws_lexer_free_utf8(compiler, mb->scheme); ws_free(mb); } /* Functions. */ static void function_hash_destructor(void *item, void *context) { ws_free(item); } WsHashPtr ws_function_hash_create(void) { return ws_hash_create(function_hash_destructor, NULL); } WsFunctionHash *ws_function_hash(WsCompilerPtr compiler, char *name) { WsFunctionHash *i = ws_hash_get(compiler->functions_hash, name); if (i) return i; /* Must create a new mapping. */ i = ws_calloc(1, sizeof(*i)); if (i == NULL) { ws_error_memory(compiler); return NULL; } if (!ws_hash_put(compiler->functions_hash, name, i)) { ws_free(i); ws_error_memory(compiler); return NULL; } return i; } 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; } /********************* Expressions **************************************/ 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; } } /* Constructors. */ static WsExpression *expr_alloc(WsCompiler *compiler, WsExpressionType type, WsUInt32 line) { WsExpression *expr = ws_f_calloc(compiler->pool_stree, 1, sizeof(*expr)); if (expr == NULL) ws_error_memory(compiler); else { expr->type = type; expr->line = line; } return expr; } WsExpression *ws_expr_comma(WsCompilerPtr compiler, WsUInt32 line, WsExpression *left, WsExpression *right) { WsExpression *expr = expr_alloc(compiler, WS_EXPR_COMMA, line); if (expr) { expr->u.comma.left = left; expr->u.comma.right = right; } return expr; } WsExpression *ws_expr_assign(WsCompilerPtr compiler, WsUInt32 line, char *identifier, int op, WsExpression *expr) { WsExpression *e = expr_alloc(compiler, WS_EXPR_ASSIGN, line); if (e) { e->u.assign.identifier = ws_f_strdup(compiler->pool_stree, identifier); if (e->u.assign.identifier == NULL) ws_error_memory(compiler); e->u.assign.op = op; e->u.assign.expr = expr; } /* Free the identifier symbol since it allocated from the system heap. */ ws_lexer_free_block(compiler, identifier); return e; } WsExpression *ws_expr_conditional(WsCompilerPtr compiler, WsUInt32 line, WsExpression *e_cond, WsExpression *e_then, WsExpression *e_else) { WsExpression *e = expr_alloc(compiler, WS_EXPR_CONDITIONAL, line); if (e) { e->u.conditional.e_cond = e_cond; e->u.conditional.e_then = e_then; e->u.conditional.e_else = e_else; } return e; } WsExpression *ws_expr_logical(WsCompilerPtr compiler, WsUInt32 line, int type, WsExpression *left, WsExpression *right) { WsExpression *expr = expr_alloc(compiler, WS_EXPR_LOGICAL, line); if (expr) { expr->u.logical.type = type; expr->u.logical.left = left; expr->u.logical.right = right; } return expr; } WsExpression *ws_expr_binary(WsCompilerPtr compiler, WsUInt32 line, int type, WsExpression *left, WsExpression *right) { WsExpression *expr = expr_alloc(compiler, WS_EXPR_BINARY, line); if (expr) { expr->u.binary.type = type; expr->u.binary.left = left; expr->u.binary.right = right; } return expr; } WsExpression *ws_expr_unary(WsCompilerPtr compiler, WsUInt32 line, int type, WsExpression *expression) { WsExpression *expr; /* Handle negative integers here as a special case of constant folding, * in order to get -2147483648 right. */ if (type == WS_ASM_UMINUS && expression->type == WS_EXPR_CONST_INTEGER) { expression->u.integer.sign = - expression->u.integer.sign; return expression; } expr = expr_alloc(compiler, WS_EXPR_UNARY, line); if (expr) { expr->u.unary.type = type; expr->u.unary.expr = expression; } return expr; } WsExpression *ws_expr_unary_var(WsCompilerPtr compiler, WsUInt32 line, WsBool addp, char *variable) { WsExpression *expr = expr_alloc(compiler, WS_EXPR_UNARY_VAR, line); if (expr) { expr->u.unary_var.addp = addp; expr->u.unary_var.variable = ws_f_strdup(compiler->pool_stree, variable); if (expr->u.unary_var.variable == NULL) ws_error_memory(compiler); } ws_lexer_free_block(compiler, variable); return expr; } WsExpression *ws_expr_postfix_var(WsCompilerPtr compiler, WsUInt32 line, WsBool addp, char *variable) { WsExpression *expr = expr_alloc(compiler, WS_EXPR_POSTFIX_VAR, line); if (expr) { expr->u.postfix_var.addp = addp; expr->u.postfix_var.variable = ws_f_strdup(compiler->pool_stree, variable); if (expr->u.postfix_var.variable == NULL) ws_error_memory(compiler); } ws_lexer_free_block(compiler, variable); return expr; } WsExpression *ws_expr_call(WsCompiler *compiler, WsUInt32 line, int type, char *base, char *name, WsList *arguments) { WsExpression *expr = expr_alloc(compiler, WS_EXPR_CALL, line); if (expr) { expr->u.call.type = type; expr->u.call.base = ws_f_strdup(compiler->pool_stree, base); expr->u.call.name = ws_f_strdup(compiler->pool_stree, name); expr->u.call.arguments = arguments; if ((base && expr->u.call.base == NULL) || (name && expr->u.call.name == NULL)) ws_error_memory(compiler); } ws_lexer_free_block(compiler, base); ws_lexer_free_block(compiler, name); return expr; } WsExpression *ws_expr_symbol(WsCompiler *compiler, WsUInt32 line, char *identifier) { WsExpression *expr = expr_alloc(compiler, WS_EXPR_SYMBOL, line); if (expr) { expr->u.symbol = ws_f_strdup(compiler->pool_stree, identifier); if (expr->u.symbol == NULL) ws_error_memory(compiler); } ws_lexer_free_block(compiler, identifier); return expr; } WsExpression *ws_expr_const_invalid(WsCompiler *compiler, WsUInt32 line) { return expr_alloc(compiler, WS_EXPR_CONST_INVALID, line); } WsExpression *ws_expr_const_true(WsCompiler *compiler, WsUInt32 line) { return expr_alloc(compiler, WS_EXPR_CONST_TRUE, line); } WsExpression *ws_expr_const_false(WsCompiler *compiler, WsUInt32 line) { return expr_alloc(compiler, WS_EXPR_CONST_FALSE, line); } WsExpression *ws_expr_const_integer(WsCompiler *compiler, WsUInt32 line, WsUInt32 ival) { WsExpression *expr = expr_alloc(compiler, WS_EXPR_CONST_INTEGER, line); if (expr) { expr->u.integer.sign = 1; expr->u.integer.ival = ival; } return expr; } WsExpression *ws_expr_const_float(WsCompiler *compiler, WsUInt32 line, WsFloat fval) { WsExpression *expr = expr_alloc(compiler, WS_EXPR_CONST_FLOAT, line); if (expr) expr->u.fval = fval; return expr; } WsExpression *ws_expr_const_string(WsCompiler *compiler, WsUInt32 line, WsUtf8String *string) { WsExpression *expr = expr_alloc(compiler, WS_EXPR_CONST_STRING, line); if (expr) { expr->u.string.len = string->len; expr->u.string.data = ws_f_memdup(compiler->pool_stree, string->data, string->len); if (expr->u.string.data == NULL) ws_error_memory(compiler); } ws_lexer_free_utf8(compiler, string); return expr; } /********************* Statements ***************************************/ /* Linearize the variable declaration list `list'. */ static void linearize_variable_init(WsCompiler *compiler, WsList *list, WsUInt32 line) { WsNamespace *ns; WsListItem *li; /* For each variable, declared with this list. */ for (li = list->head; li; li = li->next) { WsVarDec *vardec = li->data; ns = ws_variable_define(compiler, line, WS_TRUE, vardec->name); if (ns && vardec->expr) { ws_expr_linearize(compiler, vardec->expr); /* Emit an instruction to store the initialization value to the variable. */ ws_asm_link(compiler, ws_asm_variable(compiler, line, WS_ASM_P_STORE_VAR, ns->vindex)); } } } 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; } } /* Constructors. */ static WsStatement *stmt_alloc(WsCompiler *compiler, WsStatementType type, WsUInt32 first_line, WsUInt32 last_line) { WsStatement *stmt = ws_f_calloc(compiler->pool_stree, 1, sizeof(*stmt)); if (stmt == NULL) ws_error_memory(compiler); else { stmt->type = type; stmt->first_line = first_line; stmt->last_line = last_line; } return stmt; } WsStatement *ws_stmt_block(WsCompiler *compiler, WsUInt32 fline, WsUInt32 lline, WsList *block) { WsStatement *stmt = stmt_alloc(compiler, WS_STMT_BLOCK, fline, lline); if (stmt) stmt->u.block = block; return stmt; } WsStatement *ws_stmt_variable(WsCompilerPtr compiler, WsUInt32 line, WsList *variables) { WsStatement *stmt = stmt_alloc(compiler, WS_STMT_VARIABLE, line, line); if (stmt) stmt->u.var = variables; return stmt; } WsStatement *ws_stmt_empty(WsCompiler *compiler, WsUInt32 line) { return stmt_alloc(compiler, WS_STMT_EMPTY, line, line); } WsStatement *ws_stmt_expr(WsCompiler *compiler, WsUInt32 line, WsExpression *expr) { WsStatement *stmt = stmt_alloc(compiler, WS_STMT_EXPR, line, line); if (stmt) stmt->u.expr = expr; return stmt; } WsStatement *ws_stmt_if(WsCompiler *compiler, WsUInt32 line, WsExpression *expr, WsStatement *s_then, WsStatement *s_else) { WsStatement *stmt = stmt_alloc(compiler, WS_STMT_IF, line, line); if (stmt) { stmt->u.s_if.expr = expr; stmt->u.s_if.s_then = s_then; stmt->u.s_if.s_else = s_else; } return stmt; } WsStatement *ws_stmt_for(WsCompilerPtr compiler, WsUInt32 line, WsList *init, WsExpression *e1, WsExpression *e2, WsExpression *e3, WsStatement *stmt_body) { WsStatement *stmt = stmt_alloc(compiler, WS_STMT_FOR, line, line); if (stmt) { stmt->u.s_for.init = init; stmt->u.s_for.e1 = e1; stmt->u.s_for.e2 = e2; stmt->u.s_for.e3 = e3; stmt->u.s_for.stmt = stmt_body; } return stmt; } WsStatement *ws_stmt_while(WsCompiler *compiler, WsUInt32 line, WsExpression *expr, WsStatement *stmt_arg) { WsStatement *stmt = stmt_alloc(compiler, WS_STMT_WHILE, line, line); if (stmt) { stmt->u.s_while.expr = expr; stmt->u.s_while.stmt = stmt_arg; } return stmt; } WsStatement *ws_stmt_continue(WsCompiler *compiler, WsUInt32 line) { return stmt_alloc(compiler, WS_STMT_CONTINUE, line, line); } WsStatement *ws_stmt_break(WsCompiler *compiler, WsUInt32 line) { return stmt_alloc(compiler, WS_STMT_BREAK, line, line); } WsStatement *ws_stmt_return(WsCompilerPtr compiler, WsUInt32 line, WsExpression *expr) { WsStatement *stmt = stmt_alloc(compiler, WS_STMT_RETURN, line, line); if (stmt) stmt->u.expr = expr; return stmt; }