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