| /* | |
| * Copyright (C) 2015 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| #include <iostream> | |
| #include <sstream> | |
| #include "Scanner.h" | |
| #include "Specification.h" | |
| #include "Utilities.h" | |
| using namespace std; | |
| // Maximum of errors we'll report before bailing out. | |
| const int MAX_ERRORS = 10; | |
| Scanner::Scanner(const string& fileName, FILE* file) | |
| : mFileName(fileName), mFile(file), mLineNumber(0), mTagConsumed(true), mErrorCount(0) { | |
| } | |
| bool Scanner::atEnd() { | |
| return (mTagConsumed && feof(mFile)) || mErrorCount > MAX_ERRORS; | |
| } | |
| int Scanner::getChar() { | |
| int c = fgetc(mFile); | |
| if (c == '\n') { | |
| mLineNumber++; | |
| } | |
| return c; | |
| } | |
| void Scanner::readUpTo(char delimiter, string* segment) { | |
| for (;;) { | |
| int c = getChar(); | |
| if (c == EOF || c == '\n') { | |
| break; | |
| } | |
| segment->push_back((char)c); | |
| if (c == delimiter) { | |
| break; | |
| } | |
| } | |
| } | |
| void Scanner::readRestOfLine(string* segment) { | |
| for (;;) { | |
| int c = getChar(); | |
| if (c == EOF || c == '\n') { | |
| return; | |
| } | |
| segment->push_back((char)c); | |
| } | |
| } | |
| bool Scanner::getNextEntry() { | |
| mTag.clear(); | |
| mValue.clear(); | |
| for (;;) { | |
| int c = getChar(); | |
| if (c == EOF) { | |
| return false; | |
| } | |
| if (c == '#') { | |
| // Skip the comment | |
| string comment; | |
| readRestOfLine(&comment); | |
| continue; | |
| } | |
| if (c == ' ') { | |
| readRestOfLine(&mValue); | |
| break; | |
| } else if (c == '\n') { | |
| break; | |
| } else { | |
| mTag = c; | |
| readUpTo(':', &mTag); | |
| readRestOfLine(&mValue); | |
| trimSpaces(&mValue); | |
| break; | |
| } | |
| } | |
| return true; | |
| } | |
| ostream& Scanner::error() { | |
| return error(mLineNumber); | |
| } | |
| ostream& Scanner::error(int lineNumber) { | |
| if (++mErrorCount <= MAX_ERRORS) { | |
| cerr << mFileName << ":" << lineNumber << ": error: "; | |
| } | |
| return cerr; | |
| } | |
| void Scanner::skipBlankEntries() { | |
| while (findOptionalTag("")) { | |
| if (!mValue.empty()) { | |
| error() << "Unexpected: \" " << mValue << "\".\n"; | |
| } | |
| } | |
| } | |
| bool Scanner::findTag(const char* tag) { | |
| bool found = findOptionalTag(tag); | |
| if (!found) { | |
| error() << "Found \"" << mTag << "\" while looking for \"" << tag << "\".\n"; | |
| } | |
| mTagConsumed = true; | |
| return found; | |
| } | |
| bool Scanner::findOptionalTag(const char* tag) { | |
| if (mTagConsumed) { | |
| if (!getNextEntry()) { | |
| return false; | |
| } | |
| } | |
| mTagConsumed = (mTag == tag); | |
| return mTagConsumed; | |
| } | |
| void Scanner::skipUntilTag(const char* tag) { | |
| while(!findOptionalTag(tag)) { | |
| mTagConsumed = true; | |
| } | |
| } | |
| void Scanner::checkNoValue() { | |
| if (!mValue.empty()) { | |
| error() << "Did not expect \"" << mValue << "\" after \"" << mTag << "\".\n"; | |
| } | |
| } | |
| void Scanner::parseDocumentation(string* s, string* documentation) { | |
| size_t docStart = s->find(", \""); | |
| if (docStart == string::npos) { | |
| documentation->erase(); | |
| } else { | |
| size_t first = docStart + 3; | |
| size_t last = s->find('\"', first); | |
| if (last == string::npos) { | |
| error() << "Missing closing double quote\n"; | |
| } | |
| *documentation = s->substr(first, last - first); | |
| s->erase(docStart); | |
| } | |
| } | |
| ParameterEntry* Scanner::parseArgString(bool isReturn) { | |
| string s = mValue; | |
| ParameterEntry* p = new ParameterEntry(); | |
| parseDocumentation(&s, &p->documentation); | |
| size_t optionStart = s.find(", "); | |
| if (optionStart != string::npos) { | |
| p->testOption = s.substr(optionStart + 2); | |
| s.erase(optionStart); | |
| } | |
| trimSpaces(&s); | |
| if (!isReturn) { | |
| size_t nameStart = s.rfind(' '); | |
| if (nameStart == string::npos) { | |
| if (s == "...") { | |
| p->name = s; | |
| p->type = s; | |
| p->lineNumber = mLineNumber; | |
| return p; | |
| } else { | |
| error() << "Missing variable name\n"; | |
| } | |
| } else { | |
| p->name = s.substr(nameStart + 1); | |
| s.erase(nameStart); | |
| if (p->name.find('*') != string::npos) { | |
| error() << "The '*' should be attached to the type\n"; | |
| } | |
| } | |
| } | |
| if (s == "void" && !isReturn) { | |
| error() << "void is only allowed for ret:\n"; | |
| } | |
| p->type = s; | |
| p->lineNumber = mLineNumber; | |
| return p; | |
| } |