Specification.cpp revision 12398d81f32e5e0479d02b8608a83c75cd991bb3
1/*
2 * Copyright (C) 2013 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 <stdio.h>
18#include <cctype>
19#include <cstdlib>
20#include <fstream>
21#include <functional>
22#include <iostream>
23#include <memory>
24#include <sstream>
25#include <strings.h>
26
27#include "Generator.h"
28#include "Scanner.h"
29#include "Specification.h"
30#include "Utilities.h"
31
32using namespace std;
33
34// API level when RenderScript was added.
35const unsigned int MIN_API_LEVEL = 9;
36
37const NumericalType TYPES[] = {
38            {"f16", "FLOAT_16", "half", "float", FLOATING_POINT, 11, 5},
39            {"f32", "FLOAT_32", "float", "float", FLOATING_POINT, 24, 8},
40            {"f64", "FLOAT_64", "double", "double", FLOATING_POINT, 53, 11},
41            {"i8", "SIGNED_8", "char", "byte", SIGNED_INTEGER, 7, 0},
42            {"u8", "UNSIGNED_8", "uchar", "byte", UNSIGNED_INTEGER, 8, 0},
43            {"i16", "SIGNED_16", "short", "short", SIGNED_INTEGER, 15, 0},
44            {"u16", "UNSIGNED_16", "ushort", "short", UNSIGNED_INTEGER, 16, 0},
45            {"i32", "SIGNED_32", "int", "int", SIGNED_INTEGER, 31, 0},
46            {"u32", "UNSIGNED_32", "uint", "int", UNSIGNED_INTEGER, 32, 0},
47            {"i64", "SIGNED_64", "long", "long", SIGNED_INTEGER, 63, 0},
48            {"u64", "UNSIGNED_64", "ulong", "long", UNSIGNED_INTEGER, 64, 0},
49};
50
51const int NUM_TYPES = sizeof(TYPES) / sizeof(TYPES[0]);
52
53static const char kTagUnreleased[] = "UNRELEASED";
54
55// The singleton of the collected information of all the spec files.
56SystemSpecification systemSpecification;
57
58// Returns the index in TYPES for the provided cType
59static int findCType(const string& cType) {
60    for (int i = 0; i < NUM_TYPES; i++) {
61        if (cType == TYPES[i].cType) {
62            return i;
63        }
64    }
65    return -1;
66}
67
68/* Converts a string like "u8, u16" to a vector of "ushort", "uint".
69 * For non-numerical types, we don't need to convert the abbreviation.
70 */
71static vector<string> convertToTypeVector(const string& input) {
72    // First convert the string to an array of strings.
73    vector<string> entries;
74    stringstream stream(input);
75    string entry;
76    while (getline(stream, entry, ',')) {
77        trimSpaces(&entry);
78        entries.push_back(entry);
79    }
80
81    /* Second, we look for present numerical types. We do it this way
82     * so the order of numerical types is always the same, no matter
83     * how specified in the spec file.
84     */
85    vector<string> result;
86    for (auto t : TYPES) {
87        for (auto i = entries.begin(); i != entries.end(); ++i) {
88            if (*i == t.specType) {
89                result.push_back(t.cType);
90                entries.erase(i);
91                break;
92            }
93        }
94    }
95
96    // Add the remaining; they are not numerical types.
97    for (auto s : entries) {
98        result.push_back(s);
99    }
100
101    return result;
102}
103
104void ParameterDefinition::parseParameterDefinition(const string& type, const string& name,
105                                                   const string& testOption, int lineNumber,
106                                                   bool isReturn, Scanner* scanner) {
107    rsType = type;
108    specName = name;
109
110    // Determine if this is an output.
111    isOutParameter = isReturn || charRemoved('*', &rsType);
112
113    rsBaseType = rsType;
114    mVectorSize = "1";
115    /* If it's a vector type, we need to split the base type from the size.
116     * We know that's it's a vector type if the last character is a digit and
117     * the rest is an actual base type.   We used to only verify the first part,
118     * which created a problem with rs_matrix2x2.
119     */
120    const int last = rsType.size() - 1;
121    const char lastChar = rsType[last];
122    if (lastChar >= '0' && lastChar <= '9') {
123        const string trimmed = rsType.substr(0, last);
124        int i = findCType(trimmed);
125        if (i >= 0) {
126            rsBaseType = trimmed;
127            mVectorSize = lastChar;
128        }
129    }
130    typeIndex = findCType(rsBaseType);
131
132    if (mVectorSize == "3") {
133        vectorWidth = "4";
134    } else {
135        vectorWidth = mVectorSize;
136    }
137
138    /* Create variable names to be used in the java and .rs files.  Because x and
139     * y are reserved in .rs files, we prefix variable names with "in" or "out".
140     */
141    if (isOutParameter) {
142        variableName = "out";
143        if (!specName.empty()) {
144            variableName += capitalize(specName);
145        } else if (!isReturn) {
146            scanner->error(lineNumber) << "Should have a name.\n";
147        }
148    } else {
149        variableName = "in";
150        if (specName.empty()) {
151            scanner->error(lineNumber) << "Should have a name.\n";
152        }
153        variableName += capitalize(specName);
154    }
155    rsAllocName = "gAlloc" + capitalize(variableName);
156    javaAllocName = variableName;
157    javaArrayName = "array" + capitalize(javaAllocName);
158
159    // Process the option.
160    undefinedIfOutIsNan = false;
161    compatibleTypeIndex = -1;
162    if (!testOption.empty()) {
163        if (testOption.compare(0, 6, "range(") == 0) {
164            size_t pComma = testOption.find(',');
165            size_t pParen = testOption.find(')');
166            if (pComma == string::npos || pParen == string::npos) {
167                scanner->error(lineNumber) << "Incorrect range " << testOption << "\n";
168            } else {
169                minValue = testOption.substr(6, pComma - 6);
170                maxValue = testOption.substr(pComma + 1, pParen - pComma - 1);
171            }
172        } else if (testOption.compare(0, 6, "above(") == 0) {
173            size_t pParen = testOption.find(')');
174            if (pParen == string::npos) {
175                scanner->error(lineNumber) << "Incorrect testOption " << testOption << "\n";
176            } else {
177                smallerParameter = testOption.substr(6, pParen - 6);
178            }
179        } else if (testOption.compare(0, 11, "compatible(") == 0) {
180            size_t pParen = testOption.find(')');
181            if (pParen == string::npos) {
182                scanner->error(lineNumber) << "Incorrect testOption " << testOption << "\n";
183            } else {
184                compatibleTypeIndex = findCType(testOption.substr(11, pParen - 11));
185            }
186        } else if (testOption.compare(0, 11, "conditional") == 0) {
187            undefinedIfOutIsNan = true;
188        } else {
189            scanner->error(lineNumber) << "Unrecognized testOption " << testOption << "\n";
190        }
191    }
192
193    isFloatType = false;
194    if (typeIndex >= 0) {
195        javaBaseType = TYPES[typeIndex].javaType;
196        specType = TYPES[typeIndex].specType;
197        isFloatType = TYPES[typeIndex].exponentBits > 0;
198    }
199    if (!minValue.empty()) {
200        if (typeIndex < 0 || TYPES[typeIndex].kind != FLOATING_POINT) {
201            scanner->error(lineNumber) << "range(,) is only supported for floating point\n";
202        }
203    }
204}
205
206bool VersionInfo::scan(Scanner* scanner, unsigned int maxApiLevel) {
207    if (scanner->findOptionalTag("version:")) {
208        const string s = scanner->getValue();
209        if (s.compare(0, sizeof(kTagUnreleased), kTagUnreleased) == 0) {
210            // The API is still under development and does not have
211            // an official version number.
212            minVersion = maxVersion = kUnreleasedVersion;
213        } else {
214            sscanf(s.c_str(), "%u %u", &minVersion, &maxVersion);
215            if (minVersion && minVersion < MIN_API_LEVEL) {
216                scanner->error() << "Minimum version must >= 9\n";
217            }
218            if (minVersion == MIN_API_LEVEL) {
219                minVersion = 0;
220            }
221            if (maxVersion && maxVersion < MIN_API_LEVEL) {
222                scanner->error() << "Maximum version must >= 9\n";
223            }
224        }
225    }
226    if (scanner->findOptionalTag("size:")) {
227        sscanf(scanner->getValue().c_str(), "%i", &intSize);
228    }
229
230    if (maxVersion > maxApiLevel) {
231        maxVersion = maxApiLevel;
232    }
233
234    return minVersion == 0 || minVersion <= maxApiLevel;
235}
236
237Definition::Definition(const std::string& name)
238    : mName(name), mDeprecatedApiLevel(0), mHidden(false), mFinalVersion(-1) {
239}
240
241void Definition::updateFinalVersion(const VersionInfo& info) {
242    /* We set it if:
243     * - We have never set mFinalVersion before, or
244     * - The max version is 0, which means we have not expired this API, or
245     * - We have a max that's later than what we currently have.
246     */
247    if (mFinalVersion < 0 || info.maxVersion == 0 ||
248        (mFinalVersion > 0 && info.maxVersion > mFinalVersion)) {
249        mFinalVersion = info.maxVersion;
250    }
251}
252
253void Definition::scanDocumentationTags(Scanner* scanner, bool firstOccurence,
254                                       const SpecFile* specFile) {
255    if (scanner->findOptionalTag("hidden:")) {
256        scanner->checkNoValue();
257        mHidden = true;
258    }
259    if (scanner->findOptionalTag("deprecated:")) {
260        string value = scanner->getValue();
261        size_t pComma = value.find(", ");
262        if (pComma != string::npos) {
263            mDeprecatedMessage = value.substr(pComma + 2);
264            value.erase(pComma);
265        }
266        sscanf(value.c_str(), "%i", &mDeprecatedApiLevel);
267        if (mDeprecatedApiLevel <= 0) {
268            scanner->error() << "deprecated entries should have a level > 0\n";
269        }
270    }
271    if (firstOccurence) {
272        if (scanner->findTag("summary:")) {
273            mSummary = scanner->getValue();
274        }
275        if (scanner->findTag("description:")) {
276            scanner->checkNoValue();
277            while (scanner->findOptionalTag("")) {
278                mDescription.push_back(scanner->getValue());
279            }
280        }
281        mUrl = specFile->getDetailedDocumentationUrl() + "#android_rs:" + mName;
282    } else if (scanner->findOptionalTag("summary:")) {
283        scanner->error() << "Only the first specification should have a summary.\n";
284    }
285}
286
287Constant::~Constant() {
288    for (auto i : mSpecifications) {
289        delete i;
290    }
291}
292
293Type::~Type() {
294    for (auto i : mSpecifications) {
295        delete i;
296    }
297}
298
299Function::Function(const string& name) : Definition(name) {
300    mCapitalizedName = capitalize(mName);
301}
302
303Function::~Function() {
304    for (auto i : mSpecifications) {
305        delete i;
306    }
307}
308
309bool Function::someParametersAreDocumented() const {
310    for (auto p : mParameters) {
311        if (!p->documentation.empty()) {
312            return true;
313        }
314    }
315    return false;
316}
317
318void Function::addParameter(ParameterEntry* entry, Scanner* scanner) {
319    for (auto i : mParameters) {
320        if (i->name == entry->name) {
321            // It's a duplicate.
322            if (!entry->documentation.empty()) {
323                scanner->error(entry->lineNumber)
324                            << "Only the first occurence of an arg should have the "
325                               "documentation.\n";
326            }
327            return;
328        }
329    }
330    mParameters.push_back(entry);
331}
332
333void Function::addReturn(ParameterEntry* entry, Scanner* scanner) {
334    if (entry->documentation.empty()) {
335        return;
336    }
337    if (!mReturnDocumentation.empty()) {
338        scanner->error() << "ret: should be documented only for the first variant\n";
339    }
340    mReturnDocumentation = entry->documentation;
341}
342
343void ConstantSpecification::scanConstantSpecification(Scanner* scanner, SpecFile* specFile,
344                                                      unsigned int maxApiLevel) {
345    string name = scanner->getValue();
346    VersionInfo info;
347    if (!info.scan(scanner, maxApiLevel)) {
348        cout << "Skipping some " << name << " definitions.\n";
349        scanner->skipUntilTag("end:");
350        return;
351    }
352
353    bool created = false;
354    Constant* constant = systemSpecification.findOrCreateConstant(name, &created);
355    ConstantSpecification* spec = new ConstantSpecification(constant);
356    constant->addSpecification(spec);
357    constant->updateFinalVersion(info);
358    specFile->addConstantSpecification(spec, created);
359    spec->mVersionInfo = info;
360
361    if (scanner->findTag("value:")) {
362        spec->mValue = scanner->getValue();
363    }
364    constant->scanDocumentationTags(scanner, created, specFile);
365
366    scanner->findTag("end:");
367}
368
369void TypeSpecification::scanTypeSpecification(Scanner* scanner, SpecFile* specFile,
370                                              unsigned int maxApiLevel) {
371    string name = scanner->getValue();
372    VersionInfo info;
373    if (!info.scan(scanner, maxApiLevel)) {
374        cout << "Skipping some " << name << " definitions.\n";
375        scanner->skipUntilTag("end:");
376        return;
377    }
378
379    bool created = false;
380    Type* type = systemSpecification.findOrCreateType(name, &created);
381    TypeSpecification* spec = new TypeSpecification(type);
382    type->addSpecification(spec);
383    type->updateFinalVersion(info);
384    specFile->addTypeSpecification(spec, created);
385    spec->mVersionInfo = info;
386
387    if (scanner->findOptionalTag("simple:")) {
388        spec->mKind = SIMPLE;
389        spec->mSimpleType = scanner->getValue();
390    }
391    if (scanner->findOptionalTag("rs_object:")) {
392        spec->mKind = RS_OBJECT;
393    }
394    if (scanner->findOptionalTag("struct:")) {
395        spec->mKind = STRUCT;
396        spec->mStructName = scanner->getValue();
397        while (scanner->findOptionalTag("field:")) {
398            string s = scanner->getValue();
399            string comment;
400            scanner->parseDocumentation(&s, &comment);
401            spec->mFields.push_back(s);
402            spec->mFieldComments.push_back(comment);
403        }
404    }
405    if (scanner->findOptionalTag("enum:")) {
406        spec->mKind = ENUM;
407        spec->mEnumName = scanner->getValue();
408        while (scanner->findOptionalTag("value:")) {
409            string s = scanner->getValue();
410            string comment;
411            scanner->parseDocumentation(&s, &comment);
412            spec->mValues.push_back(s);
413            spec->mValueComments.push_back(comment);
414        }
415    }
416    if (scanner->findOptionalTag("attrib:")) {
417        spec->mAttribute = scanner->getValue();
418    }
419    type->scanDocumentationTags(scanner, created, specFile);
420
421    scanner->findTag("end:");
422}
423
424FunctionSpecification::~FunctionSpecification() {
425    for (auto i : mParameters) {
426        delete i;
427    }
428    delete mReturn;
429    for (auto i : mPermutations) {
430        delete i;
431    }
432}
433
434string FunctionSpecification::expandString(string s,
435                                           int replacementIndexes[MAX_REPLACEABLES]) const {
436    if (mReplaceables.size() > 0) {
437        s = stringReplace(s, "#1", mReplaceables[0][replacementIndexes[0]]);
438    }
439    if (mReplaceables.size() > 1) {
440        s = stringReplace(s, "#2", mReplaceables[1][replacementIndexes[1]]);
441    }
442    if (mReplaceables.size() > 2) {
443        s = stringReplace(s, "#3", mReplaceables[2][replacementIndexes[2]]);
444    }
445    if (mReplaceables.size() > 3) {
446        s = stringReplace(s, "#4", mReplaceables[3][replacementIndexes[3]]);
447    }
448    return s;
449}
450
451void FunctionSpecification::expandStringVector(const vector<string>& in,
452                                               int replacementIndexes[MAX_REPLACEABLES],
453                                               vector<string>* out) const {
454    out->clear();
455    for (vector<string>::const_iterator iter = in.begin(); iter != in.end(); iter++) {
456        out->push_back(expandString(*iter, replacementIndexes));
457    }
458}
459
460void FunctionSpecification::createPermutations(Function* function, Scanner* scanner) {
461    int start[MAX_REPLACEABLES];
462    int end[MAX_REPLACEABLES];
463    for (int i = 0; i < MAX_REPLACEABLES; i++) {
464        if (i < (int)mReplaceables.size()) {
465            start[i] = 0;
466            end[i] = mReplaceables[i].size();
467        } else {
468            start[i] = -1;
469            end[i] = 0;
470        }
471    }
472    int replacementIndexes[MAX_REPLACEABLES];
473    // TODO: These loops assume that MAX_REPLACEABLES is 4.
474    for (replacementIndexes[3] = start[3]; replacementIndexes[3] < end[3];
475         replacementIndexes[3]++) {
476        for (replacementIndexes[2] = start[2]; replacementIndexes[2] < end[2];
477             replacementIndexes[2]++) {
478            for (replacementIndexes[1] = start[1]; replacementIndexes[1] < end[1];
479                 replacementIndexes[1]++) {
480                for (replacementIndexes[0] = start[0]; replacementIndexes[0] < end[0];
481                     replacementIndexes[0]++) {
482                    auto p = new FunctionPermutation(function, this, replacementIndexes, scanner);
483                    mPermutations.push_back(p);
484                }
485            }
486        }
487    }
488}
489
490string FunctionSpecification::getName(int replacementIndexes[MAX_REPLACEABLES]) const {
491    return expandString(mUnexpandedName, replacementIndexes);
492}
493
494void FunctionSpecification::getReturn(int replacementIndexes[MAX_REPLACEABLES],
495                                      std::string* retType, int* lineNumber) const {
496    *retType = expandString(mReturn->type, replacementIndexes);
497    *lineNumber = mReturn->lineNumber;
498}
499
500void FunctionSpecification::getParam(size_t index, int replacementIndexes[MAX_REPLACEABLES],
501                                     std::string* type, std::string* name, std::string* testOption,
502                                     int* lineNumber) const {
503    ParameterEntry* p = mParameters[index];
504    *type = expandString(p->type, replacementIndexes);
505    *name = p->name;
506    *testOption = expandString(p->testOption, replacementIndexes);
507    *lineNumber = p->lineNumber;
508}
509
510void FunctionSpecification::getInlines(int replacementIndexes[MAX_REPLACEABLES],
511                                       std::vector<std::string>* inlines) const {
512    expandStringVector(mInline, replacementIndexes, inlines);
513}
514
515void FunctionSpecification::parseTest(Scanner* scanner) {
516    const string value = scanner->getValue();
517    if (value == "scalar" || value == "vector" || value == "noverify" || value == "custom" ||
518        value == "none") {
519        mTest = value;
520    } else if (value.compare(0, 7, "limited") == 0) {
521        mTest = "limited";
522        if (value.compare(7, 1, "(") == 0) {
523            size_t pParen = value.find(')');
524            if (pParen == string::npos) {
525                scanner->error() << "Incorrect test: \"" << value << "\"\n";
526            } else {
527                mPrecisionLimit = value.substr(8, pParen - 8);
528            }
529        }
530    } else {
531        scanner->error() << "Unrecognized test option: \"" << value << "\"\n";
532    }
533}
534
535bool FunctionSpecification::hasTests(unsigned int versionOfTestFiles) const {
536    if (mVersionInfo.maxVersion != 0 && mVersionInfo.maxVersion < versionOfTestFiles) {
537        return false;
538    }
539    if (mTest == "none") {
540        return false;
541    }
542    return true;
543}
544
545void FunctionSpecification::scanFunctionSpecification(Scanner* scanner, SpecFile* specFile,
546                                                      unsigned int maxApiLevel) {
547    // Some functions like convert have # part of the name.  Truncate at that point.
548    const string& unexpandedName = scanner->getValue();
549    string name = unexpandedName;
550    size_t p = name.find('#');
551    if (p != string::npos) {
552        if (p > 0 && name[p - 1] == '_') {
553            p--;
554        }
555        name.erase(p);
556    }
557    VersionInfo info;
558    if (!info.scan(scanner, maxApiLevel)) {
559        cout << "Skipping some " << name << " definitions.\n";
560        scanner->skipUntilTag("end:");
561        return;
562    }
563
564    bool created = false;
565    Function* function = systemSpecification.findOrCreateFunction(name, &created);
566    FunctionSpecification* spec = new FunctionSpecification(function);
567    function->addSpecification(spec);
568    function->updateFinalVersion(info);
569    specFile->addFunctionSpecification(spec, created);
570
571    spec->mUnexpandedName = unexpandedName;
572    spec->mTest = "scalar";  // default
573    spec->mVersionInfo = info;
574
575    if (scanner->findOptionalTag("internal:")) {
576        spec->mInternal = (scanner->getValue() == "true");
577    }
578    if (scanner->findOptionalTag("intrinsic:")) {
579        spec->mIntrinsic = (scanner->getValue() == "true");
580    }
581    if (scanner->findOptionalTag("attrib:")) {
582        spec->mAttribute = scanner->getValue();
583    }
584    if (scanner->findOptionalTag("w:")) {
585        vector<string> t;
586        if (scanner->getValue().find("1") != string::npos) {
587            t.push_back("");
588        }
589        if (scanner->getValue().find("2") != string::npos) {
590            t.push_back("2");
591        }
592        if (scanner->getValue().find("3") != string::npos) {
593            t.push_back("3");
594        }
595        if (scanner->getValue().find("4") != string::npos) {
596            t.push_back("4");
597        }
598        spec->mReplaceables.push_back(t);
599    }
600
601    while (scanner->findOptionalTag("t:")) {
602        spec->mReplaceables.push_back(convertToTypeVector(scanner->getValue()));
603    }
604
605    if (scanner->findTag("ret:")) {
606        ParameterEntry* p = scanner->parseArgString(true);
607        function->addReturn(p, scanner);
608        spec->mReturn = p;
609    }
610    while (scanner->findOptionalTag("arg:")) {
611        ParameterEntry* p = scanner->parseArgString(false);
612        function->addParameter(p, scanner);
613        spec->mParameters.push_back(p);
614    }
615
616    function->scanDocumentationTags(scanner, created, specFile);
617
618    if (scanner->findOptionalTag("inline:")) {
619        scanner->checkNoValue();
620        while (scanner->findOptionalTag("")) {
621            spec->mInline.push_back(scanner->getValue());
622        }
623    }
624    if (scanner->findOptionalTag("test:")) {
625        spec->parseTest(scanner);
626    }
627
628    scanner->findTag("end:");
629
630    spec->createPermutations(function, scanner);
631}
632
633FunctionPermutation::FunctionPermutation(Function* func, FunctionSpecification* spec,
634                                         int replacementIndexes[MAX_REPLACEABLES], Scanner* scanner)
635    : mReturn(nullptr), mInputCount(0), mOutputCount(0) {
636    // We expand the strings now to make capitalization easier.  The previous code preserved
637    // the #n
638    // markers just before emitting, which made capitalization difficult.
639    mName = spec->getName(replacementIndexes);
640    mNameTrunk = func->getName();
641    mTest = spec->getTest();
642    mPrecisionLimit = spec->getPrecisionLimit();
643    spec->getInlines(replacementIndexes, &mInline);
644
645    mHasFloatAnswers = false;
646    for (size_t i = 0; i < spec->getNumberOfParams(); i++) {
647        string type, name, testOption;
648        int lineNumber = 0;
649        spec->getParam(i, replacementIndexes, &type, &name, &testOption, &lineNumber);
650        ParameterDefinition* def = new ParameterDefinition();
651        def->parseParameterDefinition(type, name, testOption, lineNumber, false, scanner);
652        if (def->isOutParameter) {
653            mOutputCount++;
654        } else {
655            mInputCount++;
656        }
657
658        if (def->typeIndex < 0 && mTest != "none") {
659            scanner->error(lineNumber)
660                        << "Could not find " << def->rsBaseType
661                        << " while generating automated tests.  Use test: none if not needed.\n";
662        }
663        if (def->isOutParameter && def->isFloatType) {
664            mHasFloatAnswers = true;
665        }
666        mParams.push_back(def);
667    }
668
669    string retType;
670    int lineNumber = 0;
671    spec->getReturn(replacementIndexes, &retType, &lineNumber);
672    if (!retType.empty()) {
673        mReturn = new ParameterDefinition();
674        mReturn->parseParameterDefinition(retType, "", "", lineNumber, true, scanner);
675        if (mReturn->isFloatType) {
676            mHasFloatAnswers = true;
677        }
678        mOutputCount++;
679    }
680}
681
682FunctionPermutation::~FunctionPermutation() {
683    for (auto i : mParams) {
684        delete i;
685    }
686    delete mReturn;
687}
688
689SpecFile::SpecFile(const string& specFileName) : mSpecFileName(specFileName) {
690    string core = mSpecFileName;
691    // Remove .spec
692    size_t l = core.length();
693    const char SPEC[] = ".spec";
694    const int SPEC_SIZE = sizeof(SPEC) - 1;
695    const int start = l - SPEC_SIZE;
696    if (start >= 0 && core.compare(start, SPEC_SIZE, SPEC) == 0) {
697        core.erase(start);
698    }
699
700    // The header file name should have the same base but with a ".rsh" extension.
701    mHeaderFileName = core + ".rsh";
702    mDetailedDocumentationUrl = core + ".html";
703}
704
705void SpecFile::addConstantSpecification(ConstantSpecification* spec, bool hasDocumentation) {
706    mConstantSpecificationsList.push_back(spec);
707    if (hasDocumentation) {
708        Constant* constant = spec->getConstant();
709        mDocumentedConstants.insert(pair<string, Constant*>(constant->getName(), constant));
710    }
711}
712
713void SpecFile::addTypeSpecification(TypeSpecification* spec, bool hasDocumentation) {
714    mTypeSpecificationsList.push_back(spec);
715    if (hasDocumentation) {
716        Type* type = spec->getType();
717        mDocumentedTypes.insert(pair<string, Type*>(type->getName(), type));
718    }
719}
720
721void SpecFile::addFunctionSpecification(FunctionSpecification* spec, bool hasDocumentation) {
722    mFunctionSpecificationsList.push_back(spec);
723    if (hasDocumentation) {
724        Function* function = spec->getFunction();
725        mDocumentedFunctions.insert(pair<string, Function*>(function->getName(), function));
726    }
727}
728
729// Read the specification, adding the definitions to the global functions map.
730bool SpecFile::readSpecFile(unsigned int maxApiLevel) {
731    FILE* specFile = fopen(mSpecFileName.c_str(), "rt");
732    if (!specFile) {
733        cerr << "Error opening input file: " << mSpecFileName << "\n";
734        return false;
735    }
736
737    Scanner scanner(mSpecFileName, specFile);
738
739    // Scan the header that should start the file.
740    scanner.skipBlankEntries();
741    if (scanner.findTag("header:")) {
742        if (scanner.findTag("summary:")) {
743            mBriefDescription = scanner.getValue();
744        }
745        if (scanner.findTag("description:")) {
746            scanner.checkNoValue();
747            while (scanner.findOptionalTag("")) {
748                mFullDescription.push_back(scanner.getValue());
749            }
750        }
751        if (scanner.findOptionalTag("include:")) {
752            scanner.checkNoValue();
753            while (scanner.findOptionalTag("")) {
754                mVerbatimInclude.push_back(scanner.getValue());
755            }
756        }
757        scanner.findTag("end:");
758    }
759
760    while (1) {
761        scanner.skipBlankEntries();
762        if (scanner.atEnd()) {
763            break;
764        }
765        const string tag = scanner.getNextTag();
766        if (tag == "function:") {
767            FunctionSpecification::scanFunctionSpecification(&scanner, this, maxApiLevel);
768        } else if (tag == "type:") {
769            TypeSpecification::scanTypeSpecification(&scanner, this, maxApiLevel);
770        } else if (tag == "constant:") {
771            ConstantSpecification::scanConstantSpecification(&scanner, this, maxApiLevel);
772        } else {
773            scanner.error() << "Expected function:, type:, or constant:.  Found: " << tag << "\n";
774            return false;
775        }
776    }
777
778    fclose(specFile);
779    return scanner.getErrorCount() == 0;
780}
781
782SystemSpecification::~SystemSpecification() {
783    for (auto i : mConstants) {
784        delete i.second;
785    }
786    for (auto i : mTypes) {
787        delete i.second;
788    }
789    for (auto i : mFunctions) {
790        delete i.second;
791    }
792    for (auto i : mSpecFiles) {
793        delete i;
794    }
795}
796
797// Returns the named entry in the map.  Creates it if it's not there.
798template <class T>
799T* findOrCreate(const string& name, map<string, T*>* map, bool* created) {
800    auto iter = map->find(name);
801    if (iter != map->end()) {
802        *created = false;
803        return iter->second;
804    }
805    *created = true;
806    T* f = new T(name);
807    map->insert(pair<string, T*>(name, f));
808    return f;
809}
810
811Constant* SystemSpecification::findOrCreateConstant(const string& name, bool* created) {
812    return findOrCreate<Constant>(name, &mConstants, created);
813}
814
815Type* SystemSpecification::findOrCreateType(const string& name, bool* created) {
816    return findOrCreate<Type>(name, &mTypes, created);
817}
818
819Function* SystemSpecification::findOrCreateFunction(const string& name, bool* created) {
820    return findOrCreate<Function>(name, &mFunctions, created);
821}
822
823bool SystemSpecification::readSpecFile(const string& fileName, unsigned int maxApiLevel) {
824    SpecFile* spec = new SpecFile(fileName);
825    if (!spec->readSpecFile(maxApiLevel)) {
826        cerr << fileName << ": Failed to parse.\n";
827        return false;
828    }
829    mSpecFiles.push_back(spec);
830    return true;
831}
832
833
834static void updateMaxApiLevel(const VersionInfo& info, unsigned int* maxApiLevel) {
835    if (info.minVersion == VersionInfo::kUnreleasedVersion) {
836        // Ignore development API level in consideration of max API level.
837        return;
838    }
839    *maxApiLevel = max(*maxApiLevel, max(info.minVersion, info.maxVersion));
840}
841
842unsigned int SystemSpecification::getMaximumApiLevel() {
843    unsigned int maxApiLevel = 0;
844    for (auto i : mConstants) {
845        for (auto j: i.second->getSpecifications()) {
846            updateMaxApiLevel(j->getVersionInfo(), &maxApiLevel);
847        }
848    }
849    for (auto i : mTypes) {
850        for (auto j: i.second->getSpecifications()) {
851            updateMaxApiLevel(j->getVersionInfo(), &maxApiLevel);
852        }
853    }
854    for (auto i : mFunctions) {
855        for (auto j: i.second->getSpecifications()) {
856            updateMaxApiLevel(j->getVersionInfo(), &maxApiLevel);
857        }
858    }
859    return maxApiLevel;
860}
861
862bool SystemSpecification::generateFiles(bool forVerification, unsigned int maxApiLevel) const {
863    bool success = generateHeaderFiles("scriptc") &&
864                   generateDocumentation("docs", forVerification) &&
865                   generateTestFiles("test", maxApiLevel) &&
866                   generateStubsWhiteList("slangtest", maxApiLevel);
867    if (success) {
868        cout << "Successfully processed " << mTypes.size() << " types, " << mConstants.size()
869             << " constants, and " << mFunctions.size() << " functions.\n";
870    }
871    return success;
872}
873
874string SystemSpecification::getHtmlAnchor(const string& name) const {
875    Definition* d = nullptr;
876    auto c = mConstants.find(name);
877    if (c != mConstants.end()) {
878        d = c->second;
879    } else {
880        auto t = mTypes.find(name);
881        if (t != mTypes.end()) {
882            d = t->second;
883        } else {
884            auto f = mFunctions.find(name);
885            if (f != mFunctions.end()) {
886                d = f->second;
887            } else {
888                return string();
889            }
890        }
891    }
892    ostringstream stream;
893    stream << "<a href='" << d->getUrl() << "'>" << name << "</a>";
894    return stream.str();
895}
896