1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <iostream>
18#include <sstream>
19
20#include "Scanner.h"
21#include "Specification.h"
22#include "Utilities.h"
23
24using namespace std;
25
26// Maximum of errors we'll report before bailing out.
27const int MAX_ERRORS = 10;
28
29Scanner::Scanner(const string& fileName, FILE* file)
30    : mFileName(fileName), mFile(file), mLineNumber(0), mTagConsumed(true), mErrorCount(0) {
31}
32
33bool Scanner::atEnd() {
34    return (mTagConsumed && feof(mFile)) || mErrorCount > MAX_ERRORS;
35}
36
37int Scanner::getChar() {
38    int c = fgetc(mFile);
39    if (c == '\n') {
40        mLineNumber++;
41    }
42    return c;
43}
44
45void Scanner::readUpTo(char delimiter, string* segment) {
46    for (;;) {
47        int c = getChar();
48        if (c == EOF || c == '\n') {
49            break;
50        }
51        segment->push_back((char)c);
52        if (c == delimiter) {
53            break;
54        }
55    }
56}
57
58void Scanner::readRestOfLine(string* segment) {
59    for (;;) {
60        int c = getChar();
61        if (c == EOF || c == '\n') {
62            return;
63        }
64        segment->push_back((char)c);
65    }
66}
67
68bool Scanner::getNextEntry() {
69    mTag.clear();
70    mValue.clear();
71    for (;;) {
72        int c = getChar();
73        if (c == EOF) {
74            return false;
75        }
76        if (c == '#') {
77            // Skip the comment
78            string comment;
79            readRestOfLine(&comment);
80            continue;
81        }
82        if (c == ' ') {
83            readRestOfLine(&mValue);
84            break;
85        } else if (c == '\n') {
86            break;
87        } else {
88            mTag = c;
89            readUpTo(':', &mTag);
90            readRestOfLine(&mValue);
91            trimSpaces(&mValue);
92            break;
93        }
94    }
95    return true;
96}
97
98ostream& Scanner::error() {
99    return error(mLineNumber);
100}
101
102ostream& Scanner::error(int lineNumber) {
103    if (++mErrorCount <= MAX_ERRORS) {
104        cerr << mFileName << ":" << lineNumber << ": error: ";
105    }
106    return cerr;
107}
108
109void Scanner::skipBlankEntries() {
110    while (findOptionalTag("")) {
111        if (!mValue.empty()) {
112            error() << "Unexpected: \" " << mValue << "\".\n";
113        }
114    }
115}
116
117bool Scanner::findTag(const char* tag) {
118    bool found = findOptionalTag(tag);
119    if (!found) {
120        error() << "Found \"" << mTag << "\" while looking for \"" << tag << "\".\n";
121    }
122    mTagConsumed = true;
123    return found;
124}
125
126bool Scanner::findOptionalTag(const char* tag) {
127    if (mTagConsumed) {
128        if (!getNextEntry()) {
129            return false;
130        }
131    }
132    mTagConsumed = (mTag == tag);
133    return mTagConsumed;
134}
135
136void Scanner::skipUntilTag(const char* tag) {
137    while(!findOptionalTag(tag)) {
138        mTagConsumed = true;
139    }
140}
141
142void Scanner::checkNoValue() {
143    if (!mValue.empty()) {
144        error() << "Did not expect \"" << mValue << "\" after \"" << mTag << "\".\n";
145    }
146}
147
148void Scanner::parseDocumentation(string* s, string* documentation) {
149    size_t docStart = s->find(", \"");
150    if (docStart == string::npos) {
151        documentation->erase();
152    } else {
153        size_t first = docStart + 3;
154        size_t last = s->find('\"', first);
155        if (last == string::npos) {
156            error() << "Missing closing double quote\n";
157        }
158        *documentation = s->substr(first, last - first);
159        s->erase(docStart);
160    }
161}
162
163ParameterEntry* Scanner::parseArgString(bool isReturn) {
164    string s = mValue;
165    ParameterEntry* p = new ParameterEntry();
166    parseDocumentation(&s, &p->documentation);
167
168    size_t optionStart = s.find(", ");
169    if (optionStart != string::npos) {
170        p->testOption = s.substr(optionStart + 2);
171        s.erase(optionStart);
172    }
173
174    trimSpaces(&s);
175    if (!isReturn) {
176        size_t nameStart = s.rfind(' ');
177        if (nameStart == string::npos) {
178            if (s == "...") {
179                p->name = s;
180                p->type = s;
181                p->lineNumber = mLineNumber;
182                return p;
183            } else {
184                error() << "Missing variable name\n";
185            }
186        } else {
187            p->name = s.substr(nameStart + 1);
188            s.erase(nameStart);
189            if (p->name.find('*') != string::npos) {
190                error() << "The '*' should be attached to the type\n";
191            }
192        }
193    }
194
195    if (s == "void" && !isReturn) {
196        error() << "void is only allowed for ret:\n";
197    }
198    p->type = s;
199    p->lineNumber = mLineNumber;
200    return p;
201}
202