/* ====================================================================
* 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.
*/
/*
*
* wslexer.c
*
* Author: Markku Rossi
*
* Copyright (c) 1999-2000 WAPIT OY LTD.
* All rights reserved.
*
* Lexical analyzer.
*
*/
#include "wsint.h"
#include "wsstree.h"
#include "wsgram.h"
/********************* Types and definitions ****************************/
/* A predicate to check whether the character `ch' is a decimal
digit. */
#define WS_IS_DECIMAL_DIGIT(ch) ('0' <= (ch) && (ch) <= '9')
/* Convert the decimal digit `ch' to an integer number. */
#define WS_DECIMAL_TO_INT(ch) ((ch) - '0')
/* A predicate to check whether the character `ch' is a non-zero
decimal digit. */
#define WS_IS_NON_ZERO_DIGIT(ch) ('1' <= (ch) && (ch) <= '9')
/* A predicate to check whether the character `ch' is an octal digit. */
#define WS_IS_OCTAL_DIGIT(ch) ('0' <= (ch) && (ch) <= '7')
/* Convert the octal digit `ch' to an integer number. */
#define WS_OCTAL_TO_INT(ch) ((ch) - '0')
/* A predicate to check whether the character `ch' is a hex digit. */
#define WS_IS_HEX_DIGIT(ch) (('0' <= (ch) && (ch) <= '9') \
|| ('a' <= (ch) && (ch) <= 'f') \
|| ('A' <= (ch) && (ch) <= 'F'))
/* Convert the hex digit `ch' to an integer number. */
#define WS_HEX_TO_INT(ch) \
('0' <= (ch) && (ch) <= '9' \
? ((ch) - '0') \
: ('a' <= (ch) && (ch) <= 'f' \
? ((ch) - 'a' + 10) \
: (ch) - 'A' + 10))
/* A predicate to check whether the character `ch' is an identifier
starter letter. */
#define WS_IS_IDENTIFIER_LETTER(ch) \
(('a' <= (ch) && (ch) <= 'z') \
|| ('A' <= (ch) && (ch) <= 'Z') \
|| (ch) == '_')
/********************* Prototypes for static functions ******************/
/* Check whether the identifier `id', `len' is a keyword. If the
identifier is a keyword, the function returns WS_TRUE and sets the
keywords token ID to `token_return'. Otherwise the function
returns WS_FALSE. */
static WsBool lookup_keyword(char *id, size_t len, int *token_return);
/* Convert literal integer number, stored to the buffer `buffer', into
a 32 bit integer number. The function will report possible integer
overflows to the compiler `compiler'. The function modifies the
contents of the buffer `buffer' but it does not free it. */
static WsUInt32 buffer_to_int(WsCompilerPtr compiler, WsBuffer *buffer);
/* Read a floating point number from the decimal point to the buffer
`buffer'. The buffer `buffer' might already contain some leading
digits of the number and it always contains the decimal point. If
the operation is successful, the function returns WS_TRUE and it
returns the resulting floating point number in `result'. Otherwise
the function returns WS_FALSE. The buffer `buffer' must be
initialized before this function is called and it must be
uninitialized by the caller. */
static WsBool read_float_from_point(WsCompiler *compiler, WsBuffer *buffer,
WsFloat *result);
/* Read a floating point number from the exponent part to the buffer
`buffer'. The buffer might already contain some leading digits and
fields of the floating poit number. Otherwise, the function works
like read_float_from_point(). */
static WsBool read_float_from_exp(WsCompiler *compiler, WsBuffer *buffer,
WsFloat *result);
/********************* Static variables *********************************/
/* A helper macro which expands to a strings and its length excluding
the trailing '\0' character. */
#define N(n) n, sizeof(n) - 1
/* They keywords of the WMLScript language. This array must be sorted
by the keyword names. */
static struct
{
char *name;
size_t name_len;
int token;
} keywords[] = {
{N("access"), tACCESS},
{N("agent"), tAGENT},
{N("break"), tBREAK},
{N("case"), tCASE},
{N("catch"), tCATCH},
{N("class"), tCLASS},
{N("const"), tCONST},
{N("continue"), tCONTINUE},
{N("debugger"), tDEBUGGER},
{N("default"), tDEFAULT},
{N("delete"), tDELETE},
{N("div"), tIDIV},
{N("do"), tDO},
{N("domain"), tDOMAIN},
{N("else"), tELSE},
{N("enum"), tENUM},
{N("equiv"), tEQUIV},
{N("export"), tEXPORT},
{N("extends"), tEXTENDS},
{N("extern"), tEXTERN},
{N("false"), tFALSE},
{N("finally"), tFINALLY},
{N("for"), tFOR},
{N("function"), tFUNCTION},
{N("header"), tHEADER},
{N("http"), tHTTP},
{N("if"), tIF},
{N("import"), tIMPORT},
{N("in"), tIN},
{N("invalid"), tINVALID},
{N("isvalid"), tISVALID},
{N("lib"), tLIB},
{N("meta"), tMETA},
{N("name"), tNAME},
{N("new"), tNEW},
{N("null"), tNULL},
{N("path"), tPATH},
{N("private"), tPRIVATE},
{N("public"), tPUBLIC},
{N("return"), tRETURN},
{N("sizeof"), tSIZEOF},
{N("struct"), tSTRUCT},
{N("super"), tSUPER},
{N("switch"), tSWITCH},
{N("this"), tTHIS},
{N("throw"), tTHROW},
{N("true"), tTRUE},
{N("try"), tTRY},
{N("typeof"), tTYPEOF},
{N("url"), tURL},
{N("use"), tUSE},
{N("user"), tUSER},
{N("var"), tVAR},
{N("void"), tVOID},
{N("while"), tWHILE},
{N("with"), tWITH},
};
static int num_keywords = sizeof(keywords) / sizeof(keywords[0]);
/********************* Global functions *********************************/
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;
}
/********************* Static functions *********************************/
static WsBool lookup_keyword(char *id, size_t len, int *token_return)
{
int left = 0, center, right = num_keywords;
while (left < right) {
size_t l;
int result;
center = left + (right - left) / 2;
l = keywords[center].name_len;
if (len < l)
l = len;
result = memcmp(id, keywords[center].name, l);
if (result < 0 || (result == 0 && len < keywords[center].name_len))
/* The possible match is smaller. */
right = center;
else if (result > 0 || (result == 0 && len > keywords[center].name_len))
/* The possible match is bigger. */
left = center + 1;
else {
/* Found a match. */
*token_return = keywords[center].token;
return WS_TRUE;
}
}
/* No match. */
return WS_FALSE;
}
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;
}
static WsBool read_float_from_point(WsCompiler *compiler, WsBuffer *buffer,
WsFloat *result)
{
WsUInt32 ch;
unsigned char *p;
while (ws_stream_getc(compiler->input, &ch)) {
if (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;
} else {
ws_stream_ungetc(compiler->input, ch);
break;
}
}
return read_float_from_exp(compiler, buffer, result);
}
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;
}