blob: b36acaebe3fd934beca6a3c99996b7b3bef789d5 [file] [log] [blame]
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -07001/*
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 int MIN_API_LEVEL = 9;
36
37const NumericalType TYPES[] = {
38 {"f16", "FLOAT_16", "half", "half", 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
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -070053// The singleton of the collected information of all the spec files.
54SystemSpecification systemSpecification;
55
56// Returns the index in TYPES for the provided cType
57static int findCType(const string& cType) {
58 for (int i = 0; i < NUM_TYPES; i++) {
59 if (cType == TYPES[i].cType) {
60 return i;
61 }
62 }
63 return -1;
64}
65
66/* Converts a string like "u8, u16" to a vector of "ushort", "uint".
67 * For non-numerical types, we don't need to convert the abbreviation.
68 */
69static vector<string> convertToTypeVector(const string& input) {
70 // First convert the string to an array of strings.
71 vector<string> entries;
72 stringstream stream(input);
73 string entry;
74 while (getline(stream, entry, ',')) {
75 trimSpaces(&entry);
76 entries.push_back(entry);
77 }
78
79 /* Second, we look for present numerical types. We do it this way
80 * so the order of numerical types is always the same, no matter
81 * how specified in the spec file.
82 */
83 vector<string> result;
84 for (auto t : TYPES) {
85 for (auto i = entries.begin(); i != entries.end(); ++i) {
86 if (*i == t.specType) {
87 result.push_back(t.cType);
88 entries.erase(i);
89 break;
90 }
91 }
92 }
93
94 // Add the remaining; they are not numerical types.
95 for (auto s : entries) {
96 result.push_back(s);
97 }
98
99 return result;
100}
101
102void ParameterDefinition::parseParameterDefinition(const string& type, const string& name,
103 const string& testOption, int lineNumber,
104 bool isReturn, Scanner* scanner) {
105 rsType = type;
106 specName = name;
107
108 // Determine if this is an output.
109 isOutParameter = isReturn || charRemoved('*', &rsType);
110
Jean-Luc Brouillet66fea242015-04-09 16:47:59 -0700111 rsBaseType = rsType;
112 mVectorSize = "1";
113 /* If it's a vector type, we need to split the base type from the size.
114 * We know that's it's a vector type if the last character is a digit and
115 * the rest is an actual base type. We used to only verify the first part,
116 * which created a problem with rs_matrix2x2.
117 */
118 const int last = rsType.size() - 1;
119 const char lastChar = rsType[last];
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700120 if (lastChar >= '0' && lastChar <= '9') {
Jean-Luc Brouillet66fea242015-04-09 16:47:59 -0700121 const string trimmed = rsType.substr(0, last);
122 int i = findCType(trimmed);
123 if (i >= 0) {
124 rsBaseType = trimmed;
125 mVectorSize = lastChar;
126 }
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700127 }
Jean-Luc Brouillet66fea242015-04-09 16:47:59 -0700128 typeIndex = findCType(rsBaseType);
129
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700130 if (mVectorSize == "3") {
131 vectorWidth = "4";
132 } else {
133 vectorWidth = mVectorSize;
134 }
135
136 /* Create variable names to be used in the java and .rs files. Because x and
137 * y are reserved in .rs files, we prefix variable names with "in" or "out".
138 */
139 if (isOutParameter) {
140 variableName = "out";
141 if (!specName.empty()) {
142 variableName += capitalize(specName);
143 } else if (!isReturn) {
144 scanner->error(lineNumber) << "Should have a name.\n";
145 }
146 } else {
147 variableName = "in";
148 if (specName.empty()) {
149 scanner->error(lineNumber) << "Should have a name.\n";
150 }
151 variableName += capitalize(specName);
152 }
153 rsAllocName = "gAlloc" + capitalize(variableName);
154 javaAllocName = variableName;
155 javaArrayName = "array" + capitalize(javaAllocName);
156
157 // Process the option.
158 undefinedIfOutIsNan = false;
159 compatibleTypeIndex = -1;
160 if (!testOption.empty()) {
161 if (testOption.compare(0, 6, "range(") == 0) {
162 size_t pComma = testOption.find(',');
163 size_t pParen = testOption.find(')');
164 if (pComma == string::npos || pParen == string::npos) {
165 scanner->error(lineNumber) << "Incorrect range " << testOption << "\n";
166 } else {
167 minValue = testOption.substr(6, pComma - 6);
168 maxValue = testOption.substr(pComma + 1, pParen - pComma - 1);
169 }
170 } else if (testOption.compare(0, 6, "above(") == 0) {
171 size_t pParen = testOption.find(')');
172 if (pParen == string::npos) {
173 scanner->error(lineNumber) << "Incorrect testOption " << testOption << "\n";
174 } else {
175 smallerParameter = testOption.substr(6, pParen - 6);
176 }
177 } else if (testOption.compare(0, 11, "compatible(") == 0) {
178 size_t pParen = testOption.find(')');
179 if (pParen == string::npos) {
180 scanner->error(lineNumber) << "Incorrect testOption " << testOption << "\n";
181 } else {
182 compatibleTypeIndex = findCType(testOption.substr(11, pParen - 11));
183 }
184 } else if (testOption.compare(0, 11, "conditional") == 0) {
185 undefinedIfOutIsNan = true;
186 } else {
187 scanner->error(lineNumber) << "Unrecognized testOption " << testOption << "\n";
188 }
189 }
190
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700191 isFloatType = false;
192 if (typeIndex >= 0) {
193 javaBaseType = TYPES[typeIndex].javaType;
194 specType = TYPES[typeIndex].specType;
195 isFloatType = TYPES[typeIndex].exponentBits > 0;
196 }
197 if (!minValue.empty()) {
198 if (typeIndex < 0 || TYPES[typeIndex].kind != FLOATING_POINT) {
199 scanner->error(lineNumber) << "range(,) is only supported for floating point\n";
200 }
201 }
202}
203
Jean-Luc Brouillet2217eb72015-04-24 14:41:48 -0700204bool VersionInfo::scan(Scanner* scanner, int maxApiLevel) {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700205 if (scanner->findOptionalTag("version:")) {
206 const string s = scanner->getValue();
207 sscanf(s.c_str(), "%i %i", &minVersion, &maxVersion);
208 if (minVersion && minVersion < MIN_API_LEVEL) {
209 scanner->error() << "Minimum version must >= 9\n";
210 }
211 if (minVersion == MIN_API_LEVEL) {
212 minVersion = 0;
213 }
214 if (maxVersion && maxVersion < MIN_API_LEVEL) {
215 scanner->error() << "Maximum version must >= 9\n";
216 }
217 }
218 if (scanner->findOptionalTag("size:")) {
219 sscanf(scanner->getValue().c_str(), "%i", &intSize);
220 }
Jean-Luc Brouillet2217eb72015-04-24 14:41:48 -0700221 if (maxVersion > maxApiLevel) {
222 maxVersion = maxApiLevel;
223 }
224 return minVersion == 0 || minVersion <= maxApiLevel;
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700225}
226
Jean-Luc Brouillet4a730042015-04-02 16:15:25 -0700227Definition::Definition(const std::string& name) : mName(name), mDeprecated(false), mHidden(false) {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700228}
229
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700230void Definition::scanDocumentationTags(Scanner* scanner, bool firstOccurence,
231 const SpecFile* specFile) {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700232 if (scanner->findOptionalTag("hidden:")) {
233 scanner->checkNoValue();
234 mHidden = true;
235 }
Jean-Luc Brouillet4a730042015-04-02 16:15:25 -0700236 if (scanner->findOptionalTag("deprecated:")) {
237 mDeprecated = true;
238 mDeprecatedMessage = scanner->getValue();
239 }
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700240 if (firstOccurence) {
241 if (scanner->findTag("summary:")) {
242 mSummary = scanner->getValue();
243 }
244 if (scanner->findTag("description:")) {
245 scanner->checkNoValue();
246 while (scanner->findOptionalTag("")) {
247 mDescription.push_back(scanner->getValue());
248 }
249 }
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700250 mUrl = specFile->getDetailedDocumentationUrl() + "#android_rs:" + mName;
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700251 } else if (scanner->findOptionalTag("summary:")) {
252 scanner->error() << "Only the first specification should have a summary.\n";
253 }
254}
255
256Constant::~Constant() {
257 for (auto i : mSpecifications) {
258 delete i;
259 }
260}
261
262Type::~Type() {
263 for (auto i : mSpecifications) {
264 delete i;
265 }
266}
267
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700268Function::Function(const string& name) : Definition(name) {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700269 mCapitalizedName = capitalize(mName);
270}
271
272Function::~Function() {
273 for (auto i : mSpecifications) {
274 delete i;
275 }
276}
277
278bool Function::someParametersAreDocumented() const {
279 for (auto p : mParameters) {
280 if (!p->documentation.empty()) {
281 return true;
282 }
283 }
284 return false;
285}
286
287void Function::addParameter(ParameterEntry* entry, Scanner* scanner) {
288 for (auto i : mParameters) {
289 if (i->name == entry->name) {
290 // It's a duplicate.
291 if (!entry->documentation.empty()) {
292 scanner->error(entry->lineNumber)
293 << "Only the first occurence of an arg should have the "
294 "documentation.\n";
295 }
296 return;
297 }
298 }
299 mParameters.push_back(entry);
300}
301
302void Function::addReturn(ParameterEntry* entry, Scanner* scanner) {
303 if (entry->documentation.empty()) {
304 return;
305 }
306 if (!mReturnDocumentation.empty()) {
307 scanner->error() << "ret: should be documented only for the first variant\n";
308 }
309 mReturnDocumentation = entry->documentation;
310}
311
Jean-Luc Brouillet2217eb72015-04-24 14:41:48 -0700312void ConstantSpecification::scanConstantSpecification(Scanner* scanner, SpecFile* specFile,
313 int maxApiLevel) {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700314 string name = scanner->getValue();
Jean-Luc Brouillet2217eb72015-04-24 14:41:48 -0700315 VersionInfo info;
316 if (!info.scan(scanner, maxApiLevel)) {
317 cout << "Skipping some " << name << " definitions.\n";
318 scanner->skipUntilTag("end:");
319 return;
320 }
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700321
322 bool created = false;
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700323 Constant* constant = systemSpecification.findOrCreateConstant(name, &created);
324 ConstantSpecification* spec = new ConstantSpecification(constant);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700325 constant->addSpecification(spec);
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700326 specFile->addConstantSpecification(spec, created);
Jean-Luc Brouillet2217eb72015-04-24 14:41:48 -0700327 spec->mVersionInfo = info;
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700328
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700329 if (scanner->findTag("value:")) {
330 spec->mValue = scanner->getValue();
331 }
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700332 constant->scanDocumentationTags(scanner, created, specFile);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700333
334 scanner->findTag("end:");
335}
336
Jean-Luc Brouillet2217eb72015-04-24 14:41:48 -0700337void TypeSpecification::scanTypeSpecification(Scanner* scanner, SpecFile* specFile,
338 int maxApiLevel) {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700339 string name = scanner->getValue();
Jean-Luc Brouillet2217eb72015-04-24 14:41:48 -0700340 VersionInfo info;
341 if (!info.scan(scanner, maxApiLevel)) {
342 cout << "Skipping some " << name << " definitions.\n";
343 scanner->skipUntilTag("end:");
344 return;
345 }
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700346
347 bool created = false;
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700348 Type* type = systemSpecification.findOrCreateType(name, &created);
349 TypeSpecification* spec = new TypeSpecification(type);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700350 type->addSpecification(spec);
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700351 specFile->addTypeSpecification(spec, created);
Jean-Luc Brouillet2217eb72015-04-24 14:41:48 -0700352 spec->mVersionInfo = info;
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700353
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700354 if (scanner->findOptionalTag("simple:")) {
355 spec->mKind = SIMPLE;
356 spec->mSimpleType = scanner->getValue();
357 }
358 if (scanner->findOptionalTag("struct:")) {
359 spec->mKind = STRUCT;
360 spec->mStructName = scanner->getValue();
361 while (scanner->findOptionalTag("field:")) {
362 string s = scanner->getValue();
363 string comment;
364 scanner->parseDocumentation(&s, &comment);
365 spec->mFields.push_back(s);
366 spec->mFieldComments.push_back(comment);
367 }
368 if (scanner->findOptionalTag("attrib:")) {
369 spec->mAttrib = scanner->getValue();
370 }
371 }
372 if (scanner->findOptionalTag("enum:")) {
373 spec->mKind = ENUM;
374 spec->mEnumName = scanner->getValue();
375 while (scanner->findOptionalTag("value:")) {
376 string s = scanner->getValue();
377 string comment;
378 scanner->parseDocumentation(&s, &comment);
379 spec->mValues.push_back(s);
380 spec->mValueComments.push_back(comment);
381 }
382 }
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700383 type->scanDocumentationTags(scanner, created, specFile);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700384
385 scanner->findTag("end:");
386}
387
388FunctionSpecification::~FunctionSpecification() {
389 for (auto i : mParameters) {
390 delete i;
391 }
392 delete mReturn;
393 for (auto i : mPermutations) {
394 delete i;
395 }
396}
397
398string FunctionSpecification::expandString(string s,
399 int replacementIndexes[MAX_REPLACEABLES]) const {
400 if (mReplaceables.size() > 0) {
401 s = stringReplace(s, "#1", mReplaceables[0][replacementIndexes[0]]);
402 }
403 if (mReplaceables.size() > 1) {
404 s = stringReplace(s, "#2", mReplaceables[1][replacementIndexes[1]]);
405 }
406 if (mReplaceables.size() > 2) {
407 s = stringReplace(s, "#3", mReplaceables[2][replacementIndexes[2]]);
408 }
409 if (mReplaceables.size() > 3) {
410 s = stringReplace(s, "#4", mReplaceables[3][replacementIndexes[3]]);
411 }
412 return s;
413}
414
415void FunctionSpecification::expandStringVector(const vector<string>& in,
416 int replacementIndexes[MAX_REPLACEABLES],
417 vector<string>* out) const {
418 out->clear();
419 for (vector<string>::const_iterator iter = in.begin(); iter != in.end(); iter++) {
420 out->push_back(expandString(*iter, replacementIndexes));
421 }
422}
423
424void FunctionSpecification::createPermutations(Function* function, Scanner* scanner) {
425 int start[MAX_REPLACEABLES];
426 int end[MAX_REPLACEABLES];
427 for (int i = 0; i < MAX_REPLACEABLES; i++) {
428 if (i < (int)mReplaceables.size()) {
429 start[i] = 0;
430 end[i] = mReplaceables[i].size();
431 } else {
432 start[i] = -1;
433 end[i] = 0;
434 }
435 }
436 int replacementIndexes[MAX_REPLACEABLES];
437 // TODO: These loops assume that MAX_REPLACEABLES is 4.
438 for (replacementIndexes[3] = start[3]; replacementIndexes[3] < end[3];
439 replacementIndexes[3]++) {
440 for (replacementIndexes[2] = start[2]; replacementIndexes[2] < end[2];
441 replacementIndexes[2]++) {
442 for (replacementIndexes[1] = start[1]; replacementIndexes[1] < end[1];
443 replacementIndexes[1]++) {
444 for (replacementIndexes[0] = start[0]; replacementIndexes[0] < end[0];
445 replacementIndexes[0]++) {
446 auto p = new FunctionPermutation(function, this, replacementIndexes, scanner);
447 mPermutations.push_back(p);
448 }
449 }
450 }
451 }
452}
453
454string FunctionSpecification::getName(int replacementIndexes[MAX_REPLACEABLES]) const {
455 return expandString(mUnexpandedName, replacementIndexes);
456}
457
458void FunctionSpecification::getReturn(int replacementIndexes[MAX_REPLACEABLES],
459 std::string* retType, int* lineNumber) const {
460 *retType = expandString(mReturn->type, replacementIndexes);
461 *lineNumber = mReturn->lineNumber;
462}
463
464void FunctionSpecification::getParam(size_t index, int replacementIndexes[MAX_REPLACEABLES],
465 std::string* type, std::string* name, std::string* testOption,
466 int* lineNumber) const {
467 ParameterEntry* p = mParameters[index];
468 *type = expandString(p->type, replacementIndexes);
469 *name = p->name;
470 *testOption = expandString(p->testOption, replacementIndexes);
471 *lineNumber = p->lineNumber;
472}
473
474void FunctionSpecification::getInlines(int replacementIndexes[MAX_REPLACEABLES],
475 std::vector<std::string>* inlines) const {
476 expandStringVector(mInline, replacementIndexes, inlines);
477}
478
479void FunctionSpecification::parseTest(Scanner* scanner) {
480 const string value = scanner->getValue();
481 if (value == "scalar" || value == "vector" || value == "noverify" || value == "custom" ||
482 value == "none") {
483 mTest = value;
484 } else if (value.compare(0, 7, "limited") == 0) {
485 mTest = "limited";
486 if (value.compare(7, 1, "(") == 0) {
487 size_t pParen = value.find(')');
488 if (pParen == string::npos) {
489 scanner->error() << "Incorrect test: \"" << value << "\"\n";
490 } else {
491 mPrecisionLimit = value.substr(8, pParen - 8);
492 }
493 }
494 } else {
495 scanner->error() << "Unrecognized test option: \"" << value << "\"\n";
496 }
497}
498
499bool FunctionSpecification::hasTests(int versionOfTestFiles) const {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700500 if (mVersionInfo.maxVersion != 0 && mVersionInfo.maxVersion < versionOfTestFiles) {
501 return false;
502 }
503 if (mTest == "none") {
504 return false;
505 }
506 return true;
507}
508
Jean-Luc Brouillet2217eb72015-04-24 14:41:48 -0700509void FunctionSpecification::scanFunctionSpecification(Scanner* scanner, SpecFile* specFile,
510 int maxApiLevel) {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700511 // Some functions like convert have # part of the name. Truncate at that point.
Jean-Luc Brouillet2217eb72015-04-24 14:41:48 -0700512 const string& unexpandedName = scanner->getValue();
513 string name = unexpandedName;
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700514 size_t p = name.find('#');
515 if (p != string::npos) {
516 if (p > 0 && name[p - 1] == '_') {
517 p--;
518 }
519 name.erase(p);
520 }
Jean-Luc Brouillet2217eb72015-04-24 14:41:48 -0700521 VersionInfo info;
522 if (!info.scan(scanner, maxApiLevel)) {
523 cout << "Skipping some " << name << " definitions.\n";
524 scanner->skipUntilTag("end:");
525 return;
526 }
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700527
528 bool created = false;
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700529 Function* function = systemSpecification.findOrCreateFunction(name, &created);
530 FunctionSpecification* spec = new FunctionSpecification(function);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700531 function->addSpecification(spec);
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700532 specFile->addFunctionSpecification(spec, created);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700533
Jean-Luc Brouillet2217eb72015-04-24 14:41:48 -0700534 spec->mUnexpandedName = unexpandedName;
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700535 spec->mTest = "scalar"; // default
Jean-Luc Brouillet2217eb72015-04-24 14:41:48 -0700536 spec->mVersionInfo = info;
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700537
538 if (scanner->findOptionalTag("attrib:")) {
539 spec->mAttribute = scanner->getValue();
540 }
541 if (scanner->findOptionalTag("w:")) {
542 vector<string> t;
543 if (scanner->getValue().find("1") != string::npos) {
544 t.push_back("");
545 }
546 if (scanner->getValue().find("2") != string::npos) {
547 t.push_back("2");
548 }
549 if (scanner->getValue().find("3") != string::npos) {
550 t.push_back("3");
551 }
552 if (scanner->getValue().find("4") != string::npos) {
553 t.push_back("4");
554 }
555 spec->mReplaceables.push_back(t);
556 }
557
558 while (scanner->findOptionalTag("t:")) {
559 spec->mReplaceables.push_back(convertToTypeVector(scanner->getValue()));
560 }
561
562 if (scanner->findTag("ret:")) {
563 ParameterEntry* p = scanner->parseArgString(true);
564 function->addReturn(p, scanner);
565 spec->mReturn = p;
566 }
567 while (scanner->findOptionalTag("arg:")) {
568 ParameterEntry* p = scanner->parseArgString(false);
569 function->addParameter(p, scanner);
570 spec->mParameters.push_back(p);
571 }
572
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700573 function->scanDocumentationTags(scanner, created, specFile);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700574
575 if (scanner->findOptionalTag("inline:")) {
576 scanner->checkNoValue();
577 while (scanner->findOptionalTag("")) {
578 spec->mInline.push_back(scanner->getValue());
579 }
580 }
581 if (scanner->findOptionalTag("test:")) {
582 spec->parseTest(scanner);
583 }
584
585 scanner->findTag("end:");
586
587 spec->createPermutations(function, scanner);
588}
589
590FunctionPermutation::FunctionPermutation(Function* func, FunctionSpecification* spec,
591 int replacementIndexes[MAX_REPLACEABLES], Scanner* scanner)
Jean-Luc Brouillet4a730042015-04-02 16:15:25 -0700592 : mReturn(nullptr), mInputCount(0), mOutputCount(0) {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700593 // We expand the strings now to make capitalization easier. The previous code preserved
594 // the #n
595 // markers just before emitting, which made capitalization difficult.
596 mName = spec->getName(replacementIndexes);
597 mNameTrunk = func->getName();
598 mTest = spec->getTest();
599 mPrecisionLimit = spec->getPrecisionLimit();
600 spec->getInlines(replacementIndexes, &mInline);
601
602 mHasFloatAnswers = false;
603 for (size_t i = 0; i < spec->getNumberOfParams(); i++) {
604 string type, name, testOption;
605 int lineNumber = 0;
606 spec->getParam(i, replacementIndexes, &type, &name, &testOption, &lineNumber);
607 ParameterDefinition* def = new ParameterDefinition();
608 def->parseParameterDefinition(type, name, testOption, lineNumber, false, scanner);
609 if (def->isOutParameter) {
610 mOutputCount++;
611 } else {
612 mInputCount++;
613 }
614
615 if (def->typeIndex < 0 && mTest != "none") {
616 scanner->error(lineNumber)
617 << "Could not find " << def->rsBaseType
618 << " while generating automated tests. Use test: none if not needed.\n";
619 }
620 if (def->isOutParameter && def->isFloatType) {
621 mHasFloatAnswers = true;
622 }
623 mParams.push_back(def);
624 }
625
626 string retType;
627 int lineNumber = 0;
628 spec->getReturn(replacementIndexes, &retType, &lineNumber);
629 if (!retType.empty()) {
630 mReturn = new ParameterDefinition();
631 mReturn->parseParameterDefinition(retType, "", "", lineNumber, true, scanner);
632 if (mReturn->isFloatType) {
633 mHasFloatAnswers = true;
634 }
635 mOutputCount++;
636 }
637}
638
639FunctionPermutation::~FunctionPermutation() {
640 for (auto i : mParams) {
641 delete i;
642 }
643 delete mReturn;
644}
645
646SpecFile::SpecFile(const string& specFileName) : mSpecFileName(specFileName) {
647 string core = mSpecFileName;
648 // Remove .spec
649 size_t l = core.length();
650 const char SPEC[] = ".spec";
651 const int SPEC_SIZE = sizeof(SPEC) - 1;
652 const int start = l - SPEC_SIZE;
653 if (start >= 0 && core.compare(start, SPEC_SIZE, SPEC) == 0) {
654 core.erase(start);
655 }
656
657 // The header file name should have the same base but with a ".rsh" extension.
658 mHeaderFileName = core + ".rsh";
Jean-Luc Brouilletd9935ee2015-04-03 17:27:02 -0700659 mDetailedDocumentationUrl = core + ".html";
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700660}
661
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700662void SpecFile::addConstantSpecification(ConstantSpecification* spec, bool hasDocumentation) {
663 mConstantSpecificationsList.push_back(spec);
664 if (hasDocumentation) {
665 Constant* constant = spec->getConstant();
666 mDocumentedConstants.insert(pair<string, Constant*>(constant->getName(), constant));
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700667 }
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700668}
669
670void SpecFile::addTypeSpecification(TypeSpecification* spec, bool hasDocumentation) {
671 mTypeSpecificationsList.push_back(spec);
672 if (hasDocumentation) {
673 Type* type = spec->getType();
674 mDocumentedTypes.insert(pair<string, Type*>(type->getName(), type));
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700675 }
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700676}
677
678void SpecFile::addFunctionSpecification(FunctionSpecification* spec, bool hasDocumentation) {
679 mFunctionSpecificationsList.push_back(spec);
680 if (hasDocumentation) {
681 Function* function = spec->getFunction();
682 mDocumentedFunctions.insert(pair<string, Function*>(function->getName(), function));
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700683 }
684}
685
686// Read the specification, adding the definitions to the global functions map.
Jean-Luc Brouillet2217eb72015-04-24 14:41:48 -0700687bool SpecFile::readSpecFile(int maxApiLevel) {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700688 FILE* specFile = fopen(mSpecFileName.c_str(), "rt");
689 if (!specFile) {
690 cerr << "Error opening input file: " << mSpecFileName << "\n";
691 return false;
692 }
693
694 Scanner scanner(mSpecFileName, specFile);
695
696 // Scan the header that should start the file.
697 scanner.skipBlankEntries();
698 if (scanner.findTag("header:")) {
699 if (scanner.findTag("summary:")) {
700 mBriefDescription = scanner.getValue();
701 }
702 if (scanner.findTag("description:")) {
703 scanner.checkNoValue();
704 while (scanner.findOptionalTag("")) {
705 mFullDescription.push_back(scanner.getValue());
706 }
707 }
708 if (scanner.findOptionalTag("include:")) {
709 scanner.checkNoValue();
710 while (scanner.findOptionalTag("")) {
711 mVerbatimInclude.push_back(scanner.getValue());
712 }
713 }
714 scanner.findTag("end:");
715 }
716
717 while (1) {
718 scanner.skipBlankEntries();
719 if (scanner.atEnd()) {
720 break;
721 }
722 const string tag = scanner.getNextTag();
723 if (tag == "function:") {
Jean-Luc Brouillet2217eb72015-04-24 14:41:48 -0700724 FunctionSpecification::scanFunctionSpecification(&scanner, this, maxApiLevel);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700725 } else if (tag == "type:") {
Jean-Luc Brouillet2217eb72015-04-24 14:41:48 -0700726 TypeSpecification::scanTypeSpecification(&scanner, this, maxApiLevel);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700727 } else if (tag == "constant:") {
Jean-Luc Brouillet2217eb72015-04-24 14:41:48 -0700728 ConstantSpecification::scanConstantSpecification(&scanner, this, maxApiLevel);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700729 } else {
730 scanner.error() << "Expected function:, type:, or constant:. Found: " << tag << "\n";
731 return false;
732 }
733 }
734
735 fclose(specFile);
736 return scanner.getErrorCount() == 0;
737}
738
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700739SystemSpecification::~SystemSpecification() {
740 for (auto i : mConstants) {
741 delete i.second;
742 }
743 for (auto i : mTypes) {
744 delete i.second;
745 }
746 for (auto i : mFunctions) {
747 delete i.second;
748 }
749 for (auto i : mSpecFiles) {
750 delete i;
751 }
752}
753
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700754// Returns the named entry in the map. Creates it if it's not there.
755template <class T>
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700756T* findOrCreate(const string& name, map<string, T*>* map, bool* created) {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700757 auto iter = map->find(name);
758 if (iter != map->end()) {
759 *created = false;
760 return iter->second;
761 }
762 *created = true;
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700763 T* f = new T(name);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700764 map->insert(pair<string, T*>(name, f));
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700765 return f;
766}
767
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700768Constant* SystemSpecification::findOrCreateConstant(const string& name, bool* created) {
769 return findOrCreate<Constant>(name, &mConstants, created);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700770}
771
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700772Type* SystemSpecification::findOrCreateType(const string& name, bool* created) {
773 return findOrCreate<Type>(name, &mTypes, created);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700774}
775
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700776Function* SystemSpecification::findOrCreateFunction(const string& name, bool* created) {
777 return findOrCreate<Function>(name, &mFunctions, created);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700778}
779
Jean-Luc Brouillet2217eb72015-04-24 14:41:48 -0700780bool SystemSpecification::readSpecFile(const string& fileName, int maxApiLevel) {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700781 SpecFile* spec = new SpecFile(fileName);
Jean-Luc Brouillet2217eb72015-04-24 14:41:48 -0700782 if (!spec->readSpecFile(maxApiLevel)) {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700783 cerr << fileName << ": Failed to parse.\n";
784 return false;
785 }
786 mSpecFiles.push_back(spec);
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700787 return true;
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700788}
789
Jean-Luc Brouillet2217eb72015-04-24 14:41:48 -0700790bool SystemSpecification::generateFiles(bool forVerification, int maxApiLevel) const {
791 bool success = generateHeaderFiles("scriptc") &&
792 generateDocumentation("docs", forVerification) &&
793 generateTestFiles("test", maxApiLevel);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700794 if (success) {
795 cout << "Successfully processed " << mTypes.size() << " types, " << mConstants.size()
796 << " constants, and " << mFunctions.size() << " functions.\n";
797 }
798 return success;
799}
800
801string SystemSpecification::getHtmlAnchor(const string& name) const {
802 Definition* d = nullptr;
803 auto c = mConstants.find(name);
804 if (c != mConstants.end()) {
805 d = c->second;
806 } else {
807 auto t = mTypes.find(name);
808 if (t != mTypes.end()) {
809 d = t->second;
810 } else {
811 auto f = mFunctions.find(name);
812 if (f != mFunctions.end()) {
813 d = f->second;
814 } else {
815 return string();
816 }
817 }
818 }
819 ostringstream stream;
820 stream << "<a href='" << d->getUrl() << "'>" << name << "</a>";
821 return stream.str();
822}