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; }
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); }
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; }
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; }
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; } }
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; } }
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; }
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; }