Esempio n. 1
0
CSMFilter::Token CSMFilter::Parser::getNextToken()
{
    int size = static_cast<int> (mInput.size());

    char c = 0;

    for (; mIndex<size; ++mIndex)
    {
        c = mInput[mIndex];

        if (c!=' ')
            break;
    }

    if (mIndex>=size)
        return Token (Token::Type_EOS);

    switch (c)
    {
        case '(': ++mIndex; return Token (Token::Type_Open);
        case ')': ++mIndex; return Token (Token::Type_Close);
        case '[': ++mIndex; return Token (Token::Type_OpenSquare);
        case ']': ++mIndex; return Token (Token::Type_CloseSquare);
        case ',': ++mIndex; return Token (Token::Type_Comma);
        case '!': ++mIndex; return Token (Token::Type_OneShot);
    }

    if (c=='"' || c=='_' || std::isalpha (c) || c==':')
        return getStringToken();

    if (c=='-' || c=='.' || std::isdigit (c))
        return getNumberToken();

    error();
    return Token (Token::Type_None);
}
Esempio n. 2
0
tokenInfo getNumberToken(FILE *fp, buffer B)
{	
	//integers, and real number upto 2 decimal places supported
	tokenInfo numToken = malloc(sizeof(tokenStruct));
	char* alphabet = (char*)malloc(NUMSIZE*sizeof(char));
	int i=0;
	// Check if the buffer has enough space. Else change the buffer.
	if(B->fwdPointer >= BUFFERSIZE)
	{
		// Change buffers.
		getStream(fp, B->nextBuffer, BUFFERSIZE);
		// Set current buffer to next buffer
		if(B->nextBuffer == NULL)
		{
			return NULL;
		}
		B = B->nextBuffer;
		// Return next token from the next buffer.
		return getNumberToken(fp, B);
	}
	// alphabet[i] = B->buff[B->fwdPointer];


	while(((B->buff[B->fwdPointer] >= '0') && (B->buff[B->fwdPointer] <= '9')) && i< NUMSIZE)  //UPDATE:how to handle the number size limit?
	{
		alphabet[i++] = B->buff[B->fwdPointer++];	
		//++B->fwdPointer; //included inline above
	}

	//keep moving pointer ahead of numbers, till we reach . or some another character (error)
	if(B->buff[B->fwdPointer] == '.')
	{	
		if(B->buff[(B->fwdPointer)+1] >='0' && B->buff[(B->fwdPointer)+1] <='9') //+1 to ptr to peek ahead and see if nos ahead, append the . if true
		{
			alphabet[i++] = B->buff[B->fwdPointer++];
		}
				
		int maxDecimalPlace=2; int currentDecimalPlace=0;
		while(((B->buff[B->fwdPointer] >= '0') && (B->buff[B->fwdPointer] <= '9')) && i< NUMSIZE && currentDecimalPlace < maxDecimalPlace)  //UPDATE:how to handle the number size limit?
		{
			alphabet[i++] = B->buff[B->fwdPointer++];	
			++currentDecimalPlace;
		}
		if(currentDecimalPlace==2) //  B->buff[B->fwdPointer] == '.')
		{	//it is obv upto maxDecimalPlace only by code design
			numToken->token_name = RNUM;
			numToken->token_value = alphabet; //CONSIDER:this will need atof conversion later.
			//free(alphabet);
			return numToken;
		}
		//CONSIDER:clean this commented mess up.
		// else if(currentDecimalPlace ==0) //non numeric char after '.'
		// {	
			
		// 	//alphabet contains the number before period '.', fwdPointer is at '.' currently
		// 	numToken->token_value = alphabet; //this will need atoi conversion later.
		// 	numToken->token_name = "NUM";
		// 	free(alphabet);
		// 	return numToken;
		// }
		else //currentDecimalPlace bw [0,2)
		{	//eg 24.5 is invalid, need 24.50 
			char *err = "Invalid real num. Need exactly 2 decimal places.";
			showError(err,B->lineNumber);
			free(numToken);
			//free(alphabet);
			return NULL;
		}
	}
	else
	{	//non numeric and non-period character found, return token
		numToken->token_value = alphabet; //this will need atoi conversion later.
		numToken->token_name = NUM;
		//free(alphabet);
		return numToken;	//CONSIDER: the fwdPointer has increasd. handle that?
	}
	return NULL;
}
Esempio n. 3
0
tokenInfo getNextToken(FILE *fp, buffer B)
{
	// Read the input from the buffer.
	// Check if the buffer has enough space. Else change the buffer.
	if(B->fwdPointer >= BUFFERSIZE)
	{
		// Change buffers.
		getStream(fp, B->nextBuffer, BUFFERSIZE);
		// Set current buffer to next buffer
		if(B->nextBuffer == NULL)
		{
			return NULL;
		}
		B = B->nextBuffer;
		// Return next token from the next buffer.
		return getNextToken(fp, B);
	}
	char a = B->buff[B->fwdPointer];
	// Check first for the one character tokens.
	if(a == '(')
	{
		tokenInfo token = makeSingleToken(B);
		token->token_name = OP;
		token->token_value = "(";
		return token;
	}
	else if(a == ')')
	{
		tokenInfo token = makeSingleToken(B);
		token->token_name = CL;
		token->token_value = ")";
		return token;
	}
	else if(a == '[')
	{
		tokenInfo token = makeSingleToken(B);
		token->token_name = SQO;
		token->token_value = "[";
		return token;
	}
	else if(a == ']')
	{
		tokenInfo token = makeSingleToken(B);
		token->token_name = SQC;
		token->token_value = "]";
		return token;
	}
	else if(a == ';')
	{
		tokenInfo token = makeSingleToken(B);
		token->token_name = SEMICOLON;
		token->token_value = ";";
		return token;
	}
	else if(a == ',')
	{
		tokenInfo token = makeSingleToken(B);
		token->token_name = COMMA;
		token->token_value = ",";
		return token;
	}
	else if(a == '+')
	{
		tokenInfo token = makeSingleToken(B);
		token->token_name = PLUS;
		token->token_value = "+";
		return token;
	}
	else if(a == '-')
	{
		tokenInfo token = makeSingleToken(B);
		token->token_name = MINUS;
		token->token_value = "-";
		return token;
	}
	else if(a == '*')
	{
		tokenInfo token = makeSingleToken(B);
		token->token_name = MUL;
		token->token_value = "*";
		return token;
	}
	else if(a == '/')
	{
		tokenInfo token = makeSingleToken(B);
		token->token_name = DIV;
		token->token_value = "/";
		return token;
	}
	else if(a == '@')
	{
		tokenInfo token = makeSingleToken(B);
		token->token_name = SIZE;
		token->token_value = "@";
		return token;
	}
	// Check for comments token and safely ignore
	else if(a == '#')
	{
		++B->fwdPointer;
		while(B->fwdPointer < BUFFERSIZE && (B->buff[B->fwdPointer]!= '\n' && B->buff[B->fwdPointer]!= EOF))
		{
			++B->fwdPointer;
		}
		// Any of the above cases means we have reached the end of the current buffer. Check if exceeded buffer length

		if(B->buff[B->fwdPointer] == '\n' || B->buff[B->fwdPointer] == EOF)
		{
			// The comment has ended. Get the next token and return it.
			++B->lineNumber;
			getStream(fp, B->nextBuffer, BUFFERSIZE);
			// Set current buffer to next buffer
			if(B->nextBuffer == NULL)
			{
				return NULL;
			}
			B = B->nextBuffer;
			// Keep checking for how long comments go and return next token.
			return getNextToken(fp, B);
		}

		if(B->fwdPointer >= BUFFERSIZE)
		{
			// The comment has overflowed to next buffer. Finish reading comment and return the next token.
			getStream(fp, B->nextBuffer, BUFFERSIZE);
			// Set current buffer to next buffer
			if(B->nextBuffer == NULL)
			{
				return NULL;
			}
			B = B->nextBuffer;
			// Keep checking for how long comments go and return next token.
			return getEndCommentAndNextToken(fp, B);
		}
	}
	else if(a == '>' || a == '<')
	{
		// Check next token.
		++B->fwdPointer;
		tokenInfo token = malloc(sizeof(tokenStruct));
		token->charNumber = B->charNumber;
		token->lineNumber = B->lineNumber;
		int curPointer = B->curPointer; // Store the current value temporarily.
		tokenInfo nextToken = getNextToken(fp, B);
		if(nextToken->token_name == ASSIGNOP)
		{
			// Assign the correct value to this token.
			if(a == '>')
			{
				token->token_name = GE;
				B->curPointer = ++B->fwdPointer;
				B->charNumber = B->fwdPointer;
				token->token_value = ">=";
				free(nextToken);
				return token;
			}
			else
			{
				token->token_name = LE;
				B->curPointer = ++B->fwdPointer;
				B->charNumber = B->fwdPointer;
				token->token_value = "<=";
				free(nextToken);
				return token;
			}
		}
		else
		{
			// Something else. Return LT or GT.
			// TODO: Is an error possible in this case?
			if(a == '>')
			{
				token->token_name = GT;
				// Lost the next token. Need to generate that one again.
				B->curPointer = curPointer + 1;
				B->fwdPointer = B->curPointer;
				B->charNumber = B->fwdPointer;
				token->token_value = ">";
				free(nextToken);
				return token;
			}
			else if(a == '<')
			{
				token->token_name = LT;
				// Lost the next token. Need to generate that one again.
				B->curPointer = curPointer + 1;
				B->fwdPointer = B->curPointer;
				B->charNumber = B->fwdPointer;
				token->token_value = "<";
				free(nextToken);
				return token;
			}
		}
	}
	else if(a == '=')
	{
		++B->fwdPointer;
		tokenInfo token = malloc(sizeof(tokenStruct));
		token->charNumber = B->charNumber;
		token->lineNumber = B->lineNumber;
		int curPointer = B->curPointer; // Temporary store.
		tokenInfo nextToken = getNextToken(fp, B);
		if(nextToken->token_name == ASSIGNOP)
		{
			// EQ token.
			token->token_name = EQ;
			token->token_value = "==";
			B->charNumber = ++B->fwdPointer;
			free(nextToken);
			return token;
		}
		else if(nextToken->token_name == DIV)
		{
			// This should become a NE token
			if(B->buff[B->fwdPointer] == '=')
			{
				// It is the NE token.
				token->token_name = NE;
				token->token_value = "=/=";
				++B->fwdPointer;
				++B->curPointer;
				++B->charNumber;
				free(nextToken);
				return token;
			}
			else
			{
				// Not NE token. An error
				// TODO: Decide error functions
				char *err = "Bad token. Possible Token: NE .";
				showError(err, B->lineNumber);
				++B->fwdPointer;
				++B->curPointer;
				++B->charNumber;
				free(nextToken);
				free(token);
				return NULL;
			}
		}
		else
		{
			// Something else. This is the ASSIGNOP token
			token->token_name = ASSIGNOP;
			// Lost the next token. Need to generate again.
			B->curPointer = ++curPointer;
			token->token_value = "=";
			B->fwdPointer = B->curPointer;
			B->charNumber = B->fwdPointer;
			free(nextToken);
			return token;
		}
	}
	else if(a == '.')
	{
		// One of the logical operators, possibly
		++B->fwdPointer;
		tokenInfo token = malloc(sizeof(tokenStruct));
		token->charNumber = B->charNumber;
		token->lineNumber = B->lineNumber;
		int curPointer = B->curPointer;
		tokenInfo nextToken = getNextToken(fp, B);
		if(nextToken->token_name == ID)
		{
			// Check which of the logical operators
			if(!strcmp(nextToken->token_value,"and"))
			{
				// Maybe the and operator.
				if(B->buff[B->fwdPointer] == '.')
				{
					// AND token.
					token->token_name = AND;
					token->token_value = ".and.";
					// Increment current and fwd pointer.
					++B->fwdPointer;
					++B->curPointer;
					++B->charNumber;
					free(nextToken);
					return token;
				}
				else
				{
					// Something else. A Lexical error;
					char *err = "Lexical error. Possible Token: AND .";
					showError(err, B->lineNumber);
					++B->fwdPointer;
					++B->curPointer;
					++B->charNumber;
					free(nextToken);
					free(token);
					return NULL;
				}
			}
			else if(!strcmp(nextToken->token_value,"or"))
			{
				// Maybe the or operator
				if(B->buff[B->fwdPointer] == '.')
				{
					// OR token.
					token->token_name = OR;
					token->token_value = ".or.";
					++B->fwdPointer;
					++B->curPointer;
					++B->charNumber;
					free(nextToken);
					return token;
				}
				else{
					// Lexical error.
					char *err = "Lexical error. Possible token: OR .";
					showError(err, B->lineNumber);
					++B->fwdPointer;
					++B->curPointer;
					++B->charNumber;
					free(nextToken);
					free(token);
					return NULL;
				}
			}
			else if(!strcmp(nextToken->token_value,"not"))
			{
				// Maybe the not operator
				if(B->buff[B->fwdPointer] == '.')
				{
					// NOT token.
					token->token_name = NOT;
					token->token_value = ".not.";
					++B->fwdPointer;
					++B->curPointer;
					++B->charNumber;
					free(nextToken);
					return token;
				}
				else{
					// Lexical error.
					char *err = "Lexical error. Possible Token: NOT .";
					showError(err, B->lineNumber);
					++B->fwdPointer;
					++B->curPointer;
					++B->charNumber;
					free(nextToken);
					free(token);
					return NULL;
				}
			}
			else
			{
				// Some other identifier. A Lexical error;
				char *err = "Lexical error after .";
				showError(err, B->lineNumber);
				B->curPointer = ++curPointer;
				B->fwdPointer = B->curPointer;
				B->charNumber = B->fwdPointer;
				free(nextToken);
				free(token);
				return NULL;
			}
		}
		else
		{
			// A Lexical error;
			char *err = "Lexical Error after .";
			showError(err,B->lineNumber);
			B->curPointer = ++curPointer;
			B->fwdPointer = B->curPointer;
			B->charNumber = B->fwdPointer;
			free(nextToken);
			free(token);
			return NULL;
		}
	}
	else if(a == '_')
	{
		// A function identifier
		++B->fwdPointer;
		tokenInfo token = malloc(sizeof(tokenStruct));
		token->charNumber = B->charNumber;
		token->lineNumber = B->lineNumber;
		token->token_name = FUNID;
		char* funvalue = (char*)malloc((1+FUNSIZE)*sizeof(char));
		funvalue[0] = '_';
		if((B->buff[B->fwdPointer] >= 'a' && B->buff[B->fwdPointer] <= 'z') || (B->buff[B->fwdPointer] >= 'A' && B->buff[B->fwdPointer] <= 'Z'))
		{
			// Check for the function token.
			tokenInfo funToken = getFunctionToken(fp, B, FUNSIZE);
			strcpy(funvalue,funToken->token_value);
			// No need to store the underscore of the function name.
			tokenInfo keywordToken = checkForKeyword(funToken->token_value);
			if(keywordToken != NULL)
			{
				// It is a keyword token. Return as keyword.
				free(token);
				token = keywordToken;
				token->charNumber = B->charNumber;
				token->lineNumber = B->lineNumber;
				B->curPointer = B->fwdPointer;
				B->charNumber = B->fwdPointer;
				free(funvalue);
				//free(keywordToken);
				free(funToken);
				return token;
			}
			else
			{
				// It is an identifier. Return the identifier itself.
				token->token_value = funvalue;
				B->curPointer = B->fwdPointer;
				B->charNumber = B->fwdPointer;
				free(funToken);
				return token;
			}
		}
		else
		{
			// Incorrect. Show error.
			char *err = "Lexical Error. Function names may start only with alphabets.";
			showError(err, B->lineNumber);
			B->curPointer = B->fwdPointer;
			B->charNumber = B->fwdPointer;
			free(funvalue);
			free(token);
			return NULL;
		}
	}
	else if((a >= 'a' && a<= 'z') || (a >= 'A' && a<= 'Z'))
	{
		// An alphabet identifier
		++B->fwdPointer;
		tokenInfo token = malloc(sizeof(tokenStruct));
		token->charNumber = B->charNumber;
		token->lineNumber = B->lineNumber;
		token->token_name = ID;
		char* idvalue = (char*)malloc((1+IDSIZE)*sizeof(char));
		tokenInfo idToken = getIDToken(fp, B, IDSIZE);
		// Check if it is a token
		idvalue[0] = a;
		if(idToken == NULL)
		{
			// Check for one alphabet identifier only.
			token->token_value = idvalue;
			B->curPointer = B->fwdPointer;
			B->charNumber = B->fwdPointer;
			free(idToken);
			return token;
		}
		idvalue = joinStrings(idvalue,idToken->token_value);	
		idToken->token_value = idvalue;
		tokenInfo keywordToken = checkForKeyword(idToken->token_value);
		if(keywordToken != NULL)
		{
			// It is a keyword token. Return as keyword.
			free(token);
			token = keywordToken;
			token->charNumber = B->charNumber;
			token->lineNumber = B->lineNumber;
			B->curPointer = B->fwdPointer;
			B->charNumber = B->fwdPointer;
			//free(keywordToken);
			free(idToken);
			return token;
		}
		else
		{
			// It is an identifier. Return the identifier itself.
			token->token_value = idvalue;
			//printf("%s",token->token_value);
			B->curPointer = B->fwdPointer;
			B->charNumber = B->fwdPointer;
			free(keywordToken);
			free(idToken);
			return token;
		}	
	}
	else if(a >= '0' && a<= '9')
	{
		// A number identifier
		++B->fwdPointer;
		tokenInfo token = malloc(sizeof(tokenStruct));
		token->charNumber = B->charNumber;
		token->lineNumber = B->lineNumber;
		char* numvalue = (char*)malloc((1+NUMSIZE)*sizeof(char));
		tokenInfo numToken = getNumberToken(fp, B);
		token->token_name = numToken->token_name; //whether RealNum or IntNum.
		numvalue[0] = a;
		token->token_value = joinStrings(numvalue,numToken->token_value);
		B->curPointer = B->fwdPointer;
		B->charNumber = B->fwdPointer;
		free(numToken);
		return token;
	}
	else if(a == '\"')
	{
		// String type.
		++B->fwdPointer;
		tokenInfo token = malloc(sizeof(tokenStruct));
		token->charNumber = B->charNumber;
		token->lineNumber = B->lineNumber;
		token->token_name = STR;
		char* stringValue = (char*)malloc((IDSIZE)*sizeof(char));
		tokenInfo stringToken = getStringToken(fp, B, IDSIZE);
		// CONSIDER: check if end of string has a '"'
		if(B->buff[B->fwdPointer] == '"')
		{
			// It is a string ending with the '"'
			strcpy(stringValue,stringToken->token_value);
			token->token_value = stringValue;
			++B->fwdPointer;
			//printf("%s +++ %s",token->token_value,stringToken->token_value);
			B->curPointer = B->fwdPointer;
			B->charNumber = B->fwdPointer;
			free(stringToken);
			return token;
		}
		else
		{
			// It is not a token. Lexical error
			char *err = "Lexical error after \\";
			showError(err,B->lineNumber);
			++B->fwdPointer;
			B->curPointer = B->fwdPointer;
			B->charNumber = B->fwdPointer;
			free(stringToken);
			free(token);
			free(stringValue);
			return NULL;
		}
	}
	else
	{
		// All other cases.
		if(a == '\n')
		{
			// New line. Change values of line number and char number
			++B->lineNumber;
			B->charNumber = 0;
			// Change the buffer.
			FILE* check = getStream(fp, B->nextBuffer, BUFFERSIZE);
			// Set current buffer to next buffer
			if(check == NULL)
			{
				return NULL;
			}
			B = B->nextBuffer;
			return getNextToken(fp, B);
		}
		else if(a == ' ')
		{
			++B->fwdPointer;
			B->curPointer = B->fwdPointer;
			++B->charNumber;
			return getNextToken(fp, B);
		}
		else if(a == EOF)
		{
			// End of the input file. Stop here.
			return NULL;
		}
		else if(a == '\t')
		{
			++B->fwdPointer;
			B->curPointer = B->fwdPointer;
			++B->charNumber;
			return getNextToken(fp, B);
		}
		else
		{
			// Don't know what type of cases come here. CONSIDER
			return NULL;
		}
	}
	return NULL;
}
Esempio n. 4
0
int ecGetToken(EcInput *input)
{
    EcToken     *token, *tp;
    EcStream    *stream;
    int         c;

    //  TODO - functionalize this section

    token = input->token;

    if ((tp = input->putBack) != 0) {
        input->putBack = tp->next;
        input->token = tp;

        /*
         *  Move any old token to free list
         */
        if (token) {
            token->next = input->freeTokens;
            input->freeTokens = token;
        }
        return tp->tokenId;
    }

    if (token == 0) {
        //  TBD -- need an API for this
        input->token = mprAllocObjZeroed(input, EcToken);
        if (input->token == 0) {
            //  TBD -- err code
            return -1;
        }
        input->token->lineNumber = 1;
    }

    stream = input->stream;
    tp = input->token;
    mprAssert(tp);

    initializeToken(tp, stream);

    while (1) {

        c = getNextChar(stream);

        /*
         *  Overloadable operators
         *
         *  + - ~ * / % < > <= >= == << >> >>> & | === != !==
         *
         *  TODO FUTURE, we could allow also:  ".", "[", "("
         *
         *  TODO: what about unary !, ^
         */
        switch (c) {
        default:
        number:
            if (isdigit(c)) {
                return getNumberToken(input, tp, c);

            } else if (c == '\\') {
                c = getNextChar(stream);
                if (c == '\n') {
                    break;
                }
                putBackChar(stream, c);
                c = '\n';
            }
            if (isalpha(c) || c == '_' || c == '\\' || c == '$') {
                return getAlphaToken(input, tp, c);
            }
            return makeToken(tp, 0, T_ERR, 0);

        case -1:
            return makeToken(tp, 0, T_ERR, 0);

        case 0:
            if (stream->flags & EC_STREAM_EOL) {
                return makeToken(tp, 0, T_NOP, 0);
            } else {
                return makeToken(tp, 0, T_EOF, 0);
            }

        case ' ':
        case '\t':
            break;

        case '\r':
        case '\n':
            if (tp->textLen == 0 && tp->lineNumber != stream->lineNumber) {
                tp->currentLine = 0;
            }
            break;

        case '"':
        case '\'':
            return getQuotedToken(input, tp, c);

        case '#':
            return makeToken(tp, c, T_HASH, 0);

        case '[':
            //  EJS extension to consider this an operator
            return makeToken(tp, c, T_LBRACKET, G_OPERATOR);

        case ']':
            return makeToken(tp, c, T_RBRACKET, 0);

        case '(':
            //  EJS extension to consider this an operator
            return makeToken(tp, c, T_LPAREN, G_OPERATOR);

        case ')':
            return makeToken(tp, c, T_RPAREN, 0);

        case '{':
            return makeToken(tp, c, T_LBRACE, 0);

        case '}':
            return makeToken(tp, c, T_RBRACE, 0);

        case '@':
            return makeToken(tp, c, T_AT, 0);

        case ';':
            return makeToken(tp, c, T_SEMICOLON, 0);

        case ',':
            return makeToken(tp, c, T_COMMA, 0);

        case '?':
            return makeToken(tp, c, T_QUERY, 0);

        case '~':
            return makeToken(tp, c, T_TILDE, G_OPERATOR);

        case '+':
            c = getNextChar(stream);
            if (c == '+') {
                addCharToToken(tp, '+');
                return makeToken(tp, c, T_PLUS_PLUS, G_OPERATOR);
            } else if (c == '=') {
                addCharToToken(tp, '+');
                return makeSubToken(tp, c, T_ASSIGN, T_PLUS_ASSIGN, G_OPERATOR | G_COMPOUND_ASSIGN);
            }
            putBackChar(stream, c);
            return makeToken(tp, '+', T_PLUS, G_OPERATOR);

        case '-':
            c = getNextChar(stream);
            if (isdigit(c)) {
                putBackChar(stream, c);
                return makeToken(tp, '-', T_MINUS, G_OPERATOR);

            } else if (c == '-') {
                addCharToToken(tp, '-');
                return makeToken(tp, c, T_MINUS_MINUS, G_OPERATOR);

            } else if (c == '=') {
                addCharToToken(tp, '-');
                return makeSubToken(tp, c, T_ASSIGN, T_MINUS_ASSIGN, G_OPERATOR | G_COMPOUND_ASSIGN);
            }
            putBackChar(stream, c);
            return makeToken(tp, '-', T_MINUS, G_OPERATOR);

        case '*':
            c = getNextChar(stream);
            if (c == '=') {
                addCharToToken(tp, '*');
                return makeSubToken(tp, c, T_ASSIGN, T_MUL_ASSIGN, G_OPERATOR | G_COMPOUND_ASSIGN);
            }
            putBackChar(stream, c);
            return makeToken(tp, '*', T_MUL, G_OPERATOR);

        case '/':
            c = getNextChar(stream);
            if (c == '=') {
                addCharToToken(tp, '/');
                return makeSubToken(tp, c, T_ASSIGN, T_DIV_ASSIGN, G_OPERATOR | G_COMPOUND_ASSIGN);

            } else if (c == '>') {
                addCharToToken(tp, '/');
                return makeToken(tp, c, T_SLASH_GT, G_OPERATOR);

            } else if (c == '*' || c == '/') {
                /*
                 *  C and C++ comments
                 */
                if (getComment(input, tp, c) < 0) {
                    return tp->tokenId;
                }
#if BLD_FEATURE_EJS_DOC
                if (tp->text && tp->text[0] == '*') {
                    mprFree(input->doc);
                    input->doc = mprStrdup(input, (char*) tp->text);
                }
#endif
                initializeToken(tp, stream);
                break;
            }
            putBackChar(stream, c);
            return makeToken(tp, '/', T_DIV, G_OPERATOR);

        case '%':
            c = getNextChar(stream);
            if (c == '=') {
                addCharToToken(tp, '%');
                return makeSubToken(tp, c, T_ASSIGN, T_MOD_ASSIGN, G_OPERATOR | G_COMPOUND_ASSIGN);
            }
            putBackChar(stream, c);
            return makeToken(tp, '%', T_MOD, G_OPERATOR);

        case '.':
            c = getNextChar(stream);
            if (c == '.') {
                c = getNextChar(stream);
                if (c == '.') {
                    addStringToToken(tp, "..");
                    return makeToken(tp, c, T_ELIPSIS, 0);
                }
                putBackChar(stream, c);
                addCharToToken(tp, '.');
                return makeToken(tp, '.', T_DOT_DOT, 0);

            } else if (c == '<') {
                addCharToToken(tp, '.');
                return makeToken(tp, c, T_DOT_LESS, 0);

            } else if (isdigit(c)) {
                putBackChar(stream, c);
                goto number;
            }
            putBackChar(stream, c);
            //  EJS extension to consider this an operator
            return makeToken(tp, '.', T_DOT, G_OPERATOR);

        case ':':
            c = getNextChar(stream);
            if (c == ':') {
                addCharToToken(tp, ':');
                return makeToken(tp, c, T_COLON_COLON, 0);
            }
            putBackChar(stream, c);
            return makeToken(tp, ':', T_COLON, 0);

        case '!':
            c = getNextChar(stream);
            if (c == '=') {
                c = getNextChar(stream);
                if (c == '=') {
                    addStringToToken(tp, "!=");
                    return makeToken(tp, c, T_STRICT_NE, G_OPERATOR);
                }
                putBackChar(stream, c);
                addCharToToken(tp, '!');
                return makeToken(tp, '=', T_NE, G_OPERATOR);
            }
            putBackChar(stream, c);
            return makeToken(tp, '!', T_LOGICAL_NOT, G_OPERATOR);

#if UNUSED
        case '~':
            c = getNextChar(stream);
            if (c == '=') {
                addStringToToken(tp, "~=");
                return makeSubToken(tp, c, T_ASSIGN, T_BIT_NEG_ASSIGN, G_OPERATOR | G_COMPOUND_ASSIGN);
            }
            putBackChar(stream, c);
            return makeToken(tp, '~', T_BIT_NEG, G_OPERATOR);
#endif

        case '&':
            c = getNextChar(stream);
            if (c == '&') {
                addCharToToken(tp, '&');
                c = getNextChar(stream);
                if (c == '=') {
                    addCharToToken(tp, '&');
                    return makeSubToken(tp, '=', T_ASSIGN, T_LOGICAL_AND_ASSIGN, G_OPERATOR | G_COMPOUND_ASSIGN);
                }
                putBackChar(stream, c);
                return makeToken(tp, '&', T_LOGICAL_AND, G_OPERATOR);

            } else if (c == '=') {
                addCharToToken(tp, '&');
                return makeSubToken(tp, c, T_ASSIGN, T_BIT_AND_ASSIGN, G_OPERATOR | G_COMPOUND_ASSIGN);
            }
            putBackChar(stream, c);
            return makeToken(tp, '&', T_BIT_AND, G_OPERATOR);

        case '<':
            c = getNextChar(stream);
            if (c == '=') {
                addCharToToken(tp, '<');
                return makeToken(tp, c, T_LE, G_OPERATOR);
            } else if (c == '<') {
                c = getNextChar(stream);
                if (c == '=') {
                    addStringToToken(tp, "<<");
                    return makeSubToken(tp, c, T_ASSIGN, T_LSH_ASSIGN, G_OPERATOR | G_COMPOUND_ASSIGN);
                }
                putBackChar(stream, c);
                addCharToToken(tp, '<');
                return makeToken(tp, c, T_LSH, G_OPERATOR);

            } else if (c == '/') {
                addCharToToken(tp, '<');
                return makeToken(tp, c, T_LT_SLASH, 0);
            }
            putBackChar(stream, c);
            return makeToken(tp, '<', T_LT, G_OPERATOR);

        case '=':
            c = getNextChar(stream);
            if (c == '=') {
                c = getNextChar(stream);
                if (c == '=') {
                    addStringToToken(tp, "==");
                    return makeToken(tp, c, T_STRICT_EQ, G_OPERATOR);
                }
                putBackChar(stream, c);
                addCharToToken(tp, '=');
                return makeToken(tp, c, T_EQ, G_OPERATOR);
            }
            putBackChar(stream, c);
            return makeToken(tp, '=', T_ASSIGN, G_OPERATOR);

        case '>':
            c = getNextChar(stream);
            if (c == '=') {
                addCharToToken(tp, '<');
                return makeToken(tp, c, T_GE, G_OPERATOR);
            } else if (c == '>') {
                c = getNextChar(stream);
                if (c == '=') {
                    addStringToToken(tp, ">>");
                    return makeSubToken(tp, c, T_ASSIGN, T_RSH_ASSIGN, G_OPERATOR | G_COMPOUND_ASSIGN);
                } else if (c == '>') {
                    c = getNextChar(stream);
                    if (c == '=') {
                        addStringToToken(tp, ">>>");
                        return makeSubToken(tp, c, T_ASSIGN, T_RSH_ZERO_ASSIGN, G_OPERATOR | G_COMPOUND_ASSIGN);
                    }
                    putBackChar(stream, c);
                    addStringToToken(tp, ">>");
                    return makeToken(tp, '>', T_RSH_ZERO, G_OPERATOR);
                }
                putBackChar(stream, c);
                addCharToToken(tp, '>');
                return makeToken(tp, '>', T_RSH, G_OPERATOR);
            }
            putBackChar(stream, c);
            return makeToken(tp, '>', T_GT, G_OPERATOR);

        case '^':
            c = getNextChar(stream);
            if (c == '^') {
                addCharToToken(tp, '^');
                c = getNextChar(stream);
                if (c == '=') {
                    addCharToToken(tp, '^');
                    return makeSubToken(tp, '=', T_ASSIGN, T_LOGICAL_XOR_ASSIGN, G_OPERATOR | G_COMPOUND_ASSIGN);
                }
                putBackChar(stream, c);
                return makeToken(tp, '^', T_LOGICAL_XOR, G_OPERATOR);

            } else if (c == '=') {
                addCharToToken(tp, '^');
                return makeSubToken(tp, '=', T_ASSIGN, T_BIT_XOR_ASSIGN, G_OPERATOR | G_COMPOUND_ASSIGN);
            }
            putBackChar(stream, c);
            return makeToken(tp, '^', T_BIT_XOR, G_OPERATOR);

        case '|':
            c = getNextChar(stream);
            if (c == '|') {
                addCharToToken(tp, '|');
                c = getNextChar(stream);
                if (c == '=') {
                    addCharToToken(tp, '|');
                    return makeSubToken(tp, '=', T_ASSIGN, T_LOGICAL_OR_ASSIGN, G_OPERATOR | G_COMPOUND_ASSIGN);
                }
                putBackChar(stream, c);
                return makeToken(tp, '|', T_LOGICAL_OR, G_OPERATOR);

            } else if (c == '=') {
                addCharToToken(tp, '|');
                return makeSubToken(tp, '=', T_ASSIGN, T_BIT_OR_ASSIGN, G_OPERATOR | G_COMPOUND_ASSIGN);

            }
            putBackChar(stream, c);
            return makeToken(tp, '|', T_BIT_OR, G_OPERATOR);
        }
    }
}