blob: 6fd115c70cb88cfdf8cefa6c274f6f8bb94e420f [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
111 // Extract the vector size out of the type.
112 int last = rsType.size() - 1;
113 char lastChar = rsType[last];
114 if (lastChar >= '0' && lastChar <= '9') {
115 rsBaseType = rsType.substr(0, last);
116 mVectorSize = lastChar;
117 } else {
118 rsBaseType = rsType;
119 mVectorSize = "1";
120 }
121 if (mVectorSize == "3") {
122 vectorWidth = "4";
123 } else {
124 vectorWidth = mVectorSize;
125 }
126
127 /* Create variable names to be used in the java and .rs files. Because x and
128 * y are reserved in .rs files, we prefix variable names with "in" or "out".
129 */
130 if (isOutParameter) {
131 variableName = "out";
132 if (!specName.empty()) {
133 variableName += capitalize(specName);
134 } else if (!isReturn) {
135 scanner->error(lineNumber) << "Should have a name.\n";
136 }
137 } else {
138 variableName = "in";
139 if (specName.empty()) {
140 scanner->error(lineNumber) << "Should have a name.\n";
141 }
142 variableName += capitalize(specName);
143 }
144 rsAllocName = "gAlloc" + capitalize(variableName);
145 javaAllocName = variableName;
146 javaArrayName = "array" + capitalize(javaAllocName);
147
148 // Process the option.
149 undefinedIfOutIsNan = false;
150 compatibleTypeIndex = -1;
151 if (!testOption.empty()) {
152 if (testOption.compare(0, 6, "range(") == 0) {
153 size_t pComma = testOption.find(',');
154 size_t pParen = testOption.find(')');
155 if (pComma == string::npos || pParen == string::npos) {
156 scanner->error(lineNumber) << "Incorrect range " << testOption << "\n";
157 } else {
158 minValue = testOption.substr(6, pComma - 6);
159 maxValue = testOption.substr(pComma + 1, pParen - pComma - 1);
160 }
161 } else if (testOption.compare(0, 6, "above(") == 0) {
162 size_t pParen = testOption.find(')');
163 if (pParen == string::npos) {
164 scanner->error(lineNumber) << "Incorrect testOption " << testOption << "\n";
165 } else {
166 smallerParameter = testOption.substr(6, pParen - 6);
167 }
168 } else if (testOption.compare(0, 11, "compatible(") == 0) {
169 size_t pParen = testOption.find(')');
170 if (pParen == string::npos) {
171 scanner->error(lineNumber) << "Incorrect testOption " << testOption << "\n";
172 } else {
173 compatibleTypeIndex = findCType(testOption.substr(11, pParen - 11));
174 }
175 } else if (testOption.compare(0, 11, "conditional") == 0) {
176 undefinedIfOutIsNan = true;
177 } else {
178 scanner->error(lineNumber) << "Unrecognized testOption " << testOption << "\n";
179 }
180 }
181
182 typeIndex = findCType(rsBaseType);
183 isFloatType = false;
184 if (typeIndex >= 0) {
185 javaBaseType = TYPES[typeIndex].javaType;
186 specType = TYPES[typeIndex].specType;
187 isFloatType = TYPES[typeIndex].exponentBits > 0;
188 }
189 if (!minValue.empty()) {
190 if (typeIndex < 0 || TYPES[typeIndex].kind != FLOATING_POINT) {
191 scanner->error(lineNumber) << "range(,) is only supported for floating point\n";
192 }
193 }
194}
195
196void VersionInfo::scan(Scanner* scanner) {
197 if (scanner->findOptionalTag("version:")) {
198 const string s = scanner->getValue();
199 sscanf(s.c_str(), "%i %i", &minVersion, &maxVersion);
200 if (minVersion && minVersion < MIN_API_LEVEL) {
201 scanner->error() << "Minimum version must >= 9\n";
202 }
203 if (minVersion == MIN_API_LEVEL) {
204 minVersion = 0;
205 }
206 if (maxVersion && maxVersion < MIN_API_LEVEL) {
207 scanner->error() << "Maximum version must >= 9\n";
208 }
209 }
210 if (scanner->findOptionalTag("size:")) {
211 sscanf(scanner->getValue().c_str(), "%i", &intSize);
212 }
213}
214
Jean-Luc Brouillet4a730042015-04-02 16:15:25 -0700215Definition::Definition(const std::string& name) : mName(name), mDeprecated(false), mHidden(false) {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700216}
217
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700218void Definition::scanDocumentationTags(Scanner* scanner, bool firstOccurence,
219 const SpecFile* specFile) {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700220 if (scanner->findOptionalTag("hidden:")) {
221 scanner->checkNoValue();
222 mHidden = true;
223 }
Jean-Luc Brouillet4a730042015-04-02 16:15:25 -0700224 if (scanner->findOptionalTag("deprecated:")) {
225 mDeprecated = true;
226 mDeprecatedMessage = scanner->getValue();
227 }
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700228 if (firstOccurence) {
229 if (scanner->findTag("summary:")) {
230 mSummary = scanner->getValue();
231 }
232 if (scanner->findTag("description:")) {
233 scanner->checkNoValue();
234 while (scanner->findOptionalTag("")) {
235 mDescription.push_back(scanner->getValue());
236 }
237 }
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700238 mUrl = specFile->getDetailedDocumentationUrl() + "#android_rs:" + mName;
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700239 } else if (scanner->findOptionalTag("summary:")) {
240 scanner->error() << "Only the first specification should have a summary.\n";
241 }
242}
243
244Constant::~Constant() {
245 for (auto i : mSpecifications) {
246 delete i;
247 }
248}
249
250Type::~Type() {
251 for (auto i : mSpecifications) {
252 delete i;
253 }
254}
255
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700256Function::Function(const string& name) : Definition(name) {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700257 mCapitalizedName = capitalize(mName);
258}
259
260Function::~Function() {
261 for (auto i : mSpecifications) {
262 delete i;
263 }
264}
265
266bool Function::someParametersAreDocumented() const {
267 for (auto p : mParameters) {
268 if (!p->documentation.empty()) {
269 return true;
270 }
271 }
272 return false;
273}
274
275void Function::addParameter(ParameterEntry* entry, Scanner* scanner) {
276 for (auto i : mParameters) {
277 if (i->name == entry->name) {
278 // It's a duplicate.
279 if (!entry->documentation.empty()) {
280 scanner->error(entry->lineNumber)
281 << "Only the first occurence of an arg should have the "
282 "documentation.\n";
283 }
284 return;
285 }
286 }
287 mParameters.push_back(entry);
288}
289
290void Function::addReturn(ParameterEntry* entry, Scanner* scanner) {
291 if (entry->documentation.empty()) {
292 return;
293 }
294 if (!mReturnDocumentation.empty()) {
295 scanner->error() << "ret: should be documented only for the first variant\n";
296 }
297 mReturnDocumentation = entry->documentation;
298}
299
300void Specification::scanVersionInfo(Scanner* scanner) {
301 mVersionInfo.scan(scanner);
302}
303
304void ConstantSpecification::scanConstantSpecification(Scanner* scanner, SpecFile* specFile) {
305 string name = scanner->getValue();
306
307 bool created = false;
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700308 Constant* constant = systemSpecification.findOrCreateConstant(name, &created);
309 ConstantSpecification* spec = new ConstantSpecification(constant);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700310 constant->addSpecification(spec);
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700311 specFile->addConstantSpecification(spec, created);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700312
313 spec->scanVersionInfo(scanner);
314 if (scanner->findTag("value:")) {
315 spec->mValue = scanner->getValue();
316 }
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700317 constant->scanDocumentationTags(scanner, created, specFile);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700318
319 scanner->findTag("end:");
320}
321
322void TypeSpecification::scanTypeSpecification(Scanner* scanner, SpecFile* specFile) {
323 string name = scanner->getValue();
324
325 bool created = false;
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700326 Type* type = systemSpecification.findOrCreateType(name, &created);
327 TypeSpecification* spec = new TypeSpecification(type);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700328 type->addSpecification(spec);
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700329 specFile->addTypeSpecification(spec, created);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700330
331 spec->scanVersionInfo(scanner);
332 if (scanner->findOptionalTag("simple:")) {
333 spec->mKind = SIMPLE;
334 spec->mSimpleType = scanner->getValue();
335 }
336 if (scanner->findOptionalTag("struct:")) {
337 spec->mKind = STRUCT;
338 spec->mStructName = scanner->getValue();
339 while (scanner->findOptionalTag("field:")) {
340 string s = scanner->getValue();
341 string comment;
342 scanner->parseDocumentation(&s, &comment);
343 spec->mFields.push_back(s);
344 spec->mFieldComments.push_back(comment);
345 }
346 if (scanner->findOptionalTag("attrib:")) {
347 spec->mAttrib = scanner->getValue();
348 }
349 }
350 if (scanner->findOptionalTag("enum:")) {
351 spec->mKind = ENUM;
352 spec->mEnumName = scanner->getValue();
353 while (scanner->findOptionalTag("value:")) {
354 string s = scanner->getValue();
355 string comment;
356 scanner->parseDocumentation(&s, &comment);
357 spec->mValues.push_back(s);
358 spec->mValueComments.push_back(comment);
359 }
360 }
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700361 type->scanDocumentationTags(scanner, created, specFile);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700362
363 scanner->findTag("end:");
364}
365
366FunctionSpecification::~FunctionSpecification() {
367 for (auto i : mParameters) {
368 delete i;
369 }
370 delete mReturn;
371 for (auto i : mPermutations) {
372 delete i;
373 }
374}
375
376string FunctionSpecification::expandString(string s,
377 int replacementIndexes[MAX_REPLACEABLES]) const {
378 if (mReplaceables.size() > 0) {
379 s = stringReplace(s, "#1", mReplaceables[0][replacementIndexes[0]]);
380 }
381 if (mReplaceables.size() > 1) {
382 s = stringReplace(s, "#2", mReplaceables[1][replacementIndexes[1]]);
383 }
384 if (mReplaceables.size() > 2) {
385 s = stringReplace(s, "#3", mReplaceables[2][replacementIndexes[2]]);
386 }
387 if (mReplaceables.size() > 3) {
388 s = stringReplace(s, "#4", mReplaceables[3][replacementIndexes[3]]);
389 }
390 return s;
391}
392
393void FunctionSpecification::expandStringVector(const vector<string>& in,
394 int replacementIndexes[MAX_REPLACEABLES],
395 vector<string>* out) const {
396 out->clear();
397 for (vector<string>::const_iterator iter = in.begin(); iter != in.end(); iter++) {
398 out->push_back(expandString(*iter, replacementIndexes));
399 }
400}
401
402void FunctionSpecification::createPermutations(Function* function, Scanner* scanner) {
403 int start[MAX_REPLACEABLES];
404 int end[MAX_REPLACEABLES];
405 for (int i = 0; i < MAX_REPLACEABLES; i++) {
406 if (i < (int)mReplaceables.size()) {
407 start[i] = 0;
408 end[i] = mReplaceables[i].size();
409 } else {
410 start[i] = -1;
411 end[i] = 0;
412 }
413 }
414 int replacementIndexes[MAX_REPLACEABLES];
415 // TODO: These loops assume that MAX_REPLACEABLES is 4.
416 for (replacementIndexes[3] = start[3]; replacementIndexes[3] < end[3];
417 replacementIndexes[3]++) {
418 for (replacementIndexes[2] = start[2]; replacementIndexes[2] < end[2];
419 replacementIndexes[2]++) {
420 for (replacementIndexes[1] = start[1]; replacementIndexes[1] < end[1];
421 replacementIndexes[1]++) {
422 for (replacementIndexes[0] = start[0]; replacementIndexes[0] < end[0];
423 replacementIndexes[0]++) {
424 auto p = new FunctionPermutation(function, this, replacementIndexes, scanner);
425 mPermutations.push_back(p);
426 }
427 }
428 }
429 }
430}
431
432string FunctionSpecification::getName(int replacementIndexes[MAX_REPLACEABLES]) const {
433 return expandString(mUnexpandedName, replacementIndexes);
434}
435
436void FunctionSpecification::getReturn(int replacementIndexes[MAX_REPLACEABLES],
437 std::string* retType, int* lineNumber) const {
438 *retType = expandString(mReturn->type, replacementIndexes);
439 *lineNumber = mReturn->lineNumber;
440}
441
442void FunctionSpecification::getParam(size_t index, int replacementIndexes[MAX_REPLACEABLES],
443 std::string* type, std::string* name, std::string* testOption,
444 int* lineNumber) const {
445 ParameterEntry* p = mParameters[index];
446 *type = expandString(p->type, replacementIndexes);
447 *name = p->name;
448 *testOption = expandString(p->testOption, replacementIndexes);
449 *lineNumber = p->lineNumber;
450}
451
452void FunctionSpecification::getInlines(int replacementIndexes[MAX_REPLACEABLES],
453 std::vector<std::string>* inlines) const {
454 expandStringVector(mInline, replacementIndexes, inlines);
455}
456
457void FunctionSpecification::parseTest(Scanner* scanner) {
458 const string value = scanner->getValue();
459 if (value == "scalar" || value == "vector" || value == "noverify" || value == "custom" ||
460 value == "none") {
461 mTest = value;
462 } else if (value.compare(0, 7, "limited") == 0) {
463 mTest = "limited";
464 if (value.compare(7, 1, "(") == 0) {
465 size_t pParen = value.find(')');
466 if (pParen == string::npos) {
467 scanner->error() << "Incorrect test: \"" << value << "\"\n";
468 } else {
469 mPrecisionLimit = value.substr(8, pParen - 8);
470 }
471 }
472 } else {
473 scanner->error() << "Unrecognized test option: \"" << value << "\"\n";
474 }
475}
476
477bool FunctionSpecification::hasTests(int versionOfTestFiles) const {
478 if (mVersionInfo.minVersion != 0 && mVersionInfo.minVersion > versionOfTestFiles) {
479 return false;
480 }
481 if (mVersionInfo.maxVersion != 0 && mVersionInfo.maxVersion < versionOfTestFiles) {
482 return false;
483 }
484 if (mTest == "none") {
485 return false;
486 }
487 return true;
488}
489
490void FunctionSpecification::scanFunctionSpecification(Scanner* scanner, SpecFile* specFile) {
491 // Some functions like convert have # part of the name. Truncate at that point.
492 string name = scanner->getValue();
493 size_t p = name.find('#');
494 if (p != string::npos) {
495 if (p > 0 && name[p - 1] == '_') {
496 p--;
497 }
498 name.erase(p);
499 }
500
501 bool created = false;
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700502 Function* function = systemSpecification.findOrCreateFunction(name, &created);
503 FunctionSpecification* spec = new FunctionSpecification(function);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700504 function->addSpecification(spec);
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700505 specFile->addFunctionSpecification(spec, created);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700506
507 spec->mUnexpandedName = scanner->getValue();
508 spec->mTest = "scalar"; // default
509
510 spec->scanVersionInfo(scanner);
511
512 if (scanner->findOptionalTag("attrib:")) {
513 spec->mAttribute = scanner->getValue();
514 }
515 if (scanner->findOptionalTag("w:")) {
516 vector<string> t;
517 if (scanner->getValue().find("1") != string::npos) {
518 t.push_back("");
519 }
520 if (scanner->getValue().find("2") != string::npos) {
521 t.push_back("2");
522 }
523 if (scanner->getValue().find("3") != string::npos) {
524 t.push_back("3");
525 }
526 if (scanner->getValue().find("4") != string::npos) {
527 t.push_back("4");
528 }
529 spec->mReplaceables.push_back(t);
530 }
531
532 while (scanner->findOptionalTag("t:")) {
533 spec->mReplaceables.push_back(convertToTypeVector(scanner->getValue()));
534 }
535
536 if (scanner->findTag("ret:")) {
537 ParameterEntry* p = scanner->parseArgString(true);
538 function->addReturn(p, scanner);
539 spec->mReturn = p;
540 }
541 while (scanner->findOptionalTag("arg:")) {
542 ParameterEntry* p = scanner->parseArgString(false);
543 function->addParameter(p, scanner);
544 spec->mParameters.push_back(p);
545 }
546
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700547 function->scanDocumentationTags(scanner, created, specFile);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700548
549 if (scanner->findOptionalTag("inline:")) {
550 scanner->checkNoValue();
551 while (scanner->findOptionalTag("")) {
552 spec->mInline.push_back(scanner->getValue());
553 }
554 }
555 if (scanner->findOptionalTag("test:")) {
556 spec->parseTest(scanner);
557 }
558
559 scanner->findTag("end:");
560
561 spec->createPermutations(function, scanner);
562}
563
564FunctionPermutation::FunctionPermutation(Function* func, FunctionSpecification* spec,
565 int replacementIndexes[MAX_REPLACEABLES], Scanner* scanner)
Jean-Luc Brouillet4a730042015-04-02 16:15:25 -0700566 : mReturn(nullptr), mInputCount(0), mOutputCount(0) {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700567 // We expand the strings now to make capitalization easier. The previous code preserved
568 // the #n
569 // markers just before emitting, which made capitalization difficult.
570 mName = spec->getName(replacementIndexes);
571 mNameTrunk = func->getName();
572 mTest = spec->getTest();
573 mPrecisionLimit = spec->getPrecisionLimit();
574 spec->getInlines(replacementIndexes, &mInline);
575
576 mHasFloatAnswers = false;
577 for (size_t i = 0; i < spec->getNumberOfParams(); i++) {
578 string type, name, testOption;
579 int lineNumber = 0;
580 spec->getParam(i, replacementIndexes, &type, &name, &testOption, &lineNumber);
581 ParameterDefinition* def = new ParameterDefinition();
582 def->parseParameterDefinition(type, name, testOption, lineNumber, false, scanner);
583 if (def->isOutParameter) {
584 mOutputCount++;
585 } else {
586 mInputCount++;
587 }
588
589 if (def->typeIndex < 0 && mTest != "none") {
590 scanner->error(lineNumber)
591 << "Could not find " << def->rsBaseType
592 << " while generating automated tests. Use test: none if not needed.\n";
593 }
594 if (def->isOutParameter && def->isFloatType) {
595 mHasFloatAnswers = true;
596 }
597 mParams.push_back(def);
598 }
599
600 string retType;
601 int lineNumber = 0;
602 spec->getReturn(replacementIndexes, &retType, &lineNumber);
603 if (!retType.empty()) {
604 mReturn = new ParameterDefinition();
605 mReturn->parseParameterDefinition(retType, "", "", lineNumber, true, scanner);
606 if (mReturn->isFloatType) {
607 mHasFloatAnswers = true;
608 }
609 mOutputCount++;
610 }
611}
612
613FunctionPermutation::~FunctionPermutation() {
614 for (auto i : mParams) {
615 delete i;
616 }
617 delete mReturn;
618}
619
620SpecFile::SpecFile(const string& specFileName) : mSpecFileName(specFileName) {
621 string core = mSpecFileName;
622 // Remove .spec
623 size_t l = core.length();
624 const char SPEC[] = ".spec";
625 const int SPEC_SIZE = sizeof(SPEC) - 1;
626 const int start = l - SPEC_SIZE;
627 if (start >= 0 && core.compare(start, SPEC_SIZE, SPEC) == 0) {
628 core.erase(start);
629 }
630
631 // The header file name should have the same base but with a ".rsh" extension.
632 mHeaderFileName = core + ".rsh";
Jean-Luc Brouilletd9935ee2015-04-03 17:27:02 -0700633 mDetailedDocumentationUrl = core + ".html";
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700634}
635
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700636void SpecFile::addConstantSpecification(ConstantSpecification* spec, bool hasDocumentation) {
637 mConstantSpecificationsList.push_back(spec);
638 if (hasDocumentation) {
639 Constant* constant = spec->getConstant();
640 mDocumentedConstants.insert(pair<string, Constant*>(constant->getName(), constant));
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700641 }
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700642}
643
644void SpecFile::addTypeSpecification(TypeSpecification* spec, bool hasDocumentation) {
645 mTypeSpecificationsList.push_back(spec);
646 if (hasDocumentation) {
647 Type* type = spec->getType();
648 mDocumentedTypes.insert(pair<string, Type*>(type->getName(), type));
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700649 }
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700650}
651
652void SpecFile::addFunctionSpecification(FunctionSpecification* spec, bool hasDocumentation) {
653 mFunctionSpecificationsList.push_back(spec);
654 if (hasDocumentation) {
655 Function* function = spec->getFunction();
656 mDocumentedFunctions.insert(pair<string, Function*>(function->getName(), function));
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700657 }
658}
659
660// Read the specification, adding the definitions to the global functions map.
661bool SpecFile::readSpecFile() {
662 FILE* specFile = fopen(mSpecFileName.c_str(), "rt");
663 if (!specFile) {
664 cerr << "Error opening input file: " << mSpecFileName << "\n";
665 return false;
666 }
667
668 Scanner scanner(mSpecFileName, specFile);
669
670 // Scan the header that should start the file.
671 scanner.skipBlankEntries();
672 if (scanner.findTag("header:")) {
673 if (scanner.findTag("summary:")) {
674 mBriefDescription = scanner.getValue();
675 }
676 if (scanner.findTag("description:")) {
677 scanner.checkNoValue();
678 while (scanner.findOptionalTag("")) {
679 mFullDescription.push_back(scanner.getValue());
680 }
681 }
682 if (scanner.findOptionalTag("include:")) {
683 scanner.checkNoValue();
684 while (scanner.findOptionalTag("")) {
685 mVerbatimInclude.push_back(scanner.getValue());
686 }
687 }
688 scanner.findTag("end:");
689 }
690
691 while (1) {
692 scanner.skipBlankEntries();
693 if (scanner.atEnd()) {
694 break;
695 }
696 const string tag = scanner.getNextTag();
697 if (tag == "function:") {
698 FunctionSpecification::scanFunctionSpecification(&scanner, this);
699 } else if (tag == "type:") {
700 TypeSpecification::scanTypeSpecification(&scanner, this);
701 } else if (tag == "constant:") {
702 ConstantSpecification::scanConstantSpecification(&scanner, this);
703 } else {
704 scanner.error() << "Expected function:, type:, or constant:. Found: " << tag << "\n";
705 return false;
706 }
707 }
708
709 fclose(specFile);
710 return scanner.getErrorCount() == 0;
711}
712
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700713SystemSpecification::~SystemSpecification() {
714 for (auto i : mConstants) {
715 delete i.second;
716 }
717 for (auto i : mTypes) {
718 delete i.second;
719 }
720 for (auto i : mFunctions) {
721 delete i.second;
722 }
723 for (auto i : mSpecFiles) {
724 delete i;
725 }
726}
727
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700728// Returns the named entry in the map. Creates it if it's not there.
729template <class T>
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700730T* findOrCreate(const string& name, map<string, T*>* map, bool* created) {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700731 auto iter = map->find(name);
732 if (iter != map->end()) {
733 *created = false;
734 return iter->second;
735 }
736 *created = true;
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700737 T* f = new T(name);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700738 map->insert(pair<string, T*>(name, f));
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700739 return f;
740}
741
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700742Constant* SystemSpecification::findOrCreateConstant(const string& name, bool* created) {
743 return findOrCreate<Constant>(name, &mConstants, created);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700744}
745
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700746Type* SystemSpecification::findOrCreateType(const string& name, bool* created) {
747 return findOrCreate<Type>(name, &mTypes, created);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700748}
749
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700750Function* SystemSpecification::findOrCreateFunction(const string& name, bool* created) {
751 return findOrCreate<Function>(name, &mFunctions, created);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700752}
753
754bool SystemSpecification::readSpecFile(const string& fileName) {
755 SpecFile* spec = new SpecFile(fileName);
756 if (!spec->readSpecFile()) {
757 cerr << fileName << ": Failed to parse.\n";
758 return false;
759 }
760 mSpecFiles.push_back(spec);
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700761 return true;
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700762}
763
764bool SystemSpecification::generateFiles(int versionOfTestFiles) const {
Jean-Luc Brouillet62e09932015-03-22 11:14:07 -0700765 bool success = GenerateHeaderFiles("scriptc") && generateHtmlDocumentation("html") &&
766 GenerateTestFiles("test", versionOfTestFiles);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700767 if (success) {
768 cout << "Successfully processed " << mTypes.size() << " types, " << mConstants.size()
769 << " constants, and " << mFunctions.size() << " functions.\n";
770 }
771 return success;
772}
773
774string SystemSpecification::getHtmlAnchor(const string& name) const {
775 Definition* d = nullptr;
776 auto c = mConstants.find(name);
777 if (c != mConstants.end()) {
778 d = c->second;
779 } else {
780 auto t = mTypes.find(name);
781 if (t != mTypes.end()) {
782 d = t->second;
783 } else {
784 auto f = mFunctions.find(name);
785 if (f != mFunctions.end()) {
786 d = f->second;
787 } else {
788 return string();
789 }
790 }
791 }
792 ostringstream stream;
793 stream << "<a href='" << d->getUrl() << "'>" << name << "</a>";
794 return stream.str();
795}