/* ====================================================================
* 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;
}