flatc.js 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283
  1. const flatc = {};
  2. const fs = require('fs');
  3. const path = require('path');
  4. flatc.Object = class {
  5. constructor(parent, name) {
  6. this.parent = parent;
  7. this.name = name;
  8. this.metadata = new Map();
  9. }
  10. get root() {
  11. return this.parent.root;
  12. }
  13. resolve() {
  14. if (!this.resolved) {
  15. for (const key of this.metadata.keys()) {
  16. switch (key) {
  17. case 'force_align':
  18. case 'deprecated':
  19. case 'key':
  20. case 'required':
  21. break;
  22. default:
  23. throw new flatc.Error("Unsupported attribute '" + key + "'.");
  24. }
  25. }
  26. this.resolved = true;
  27. }
  28. }
  29. find(name, type) {
  30. return this.parent ? this.parent.find(name, type) : undefined;
  31. }
  32. };
  33. flatc.Namespace = class extends flatc.Object {
  34. constructor(parent, name) {
  35. super(parent, name);
  36. this.children = new Map();
  37. this.root_type = new Set();
  38. }
  39. resolve() {
  40. if (!this.resolved) {
  41. for (const child of this.children.values()) {
  42. child.resolve();
  43. }
  44. if (this.root_type.size > 0) {
  45. for (const root_type of this.root_type) {
  46. const type = this.find(root_type, flatc.Type);
  47. if (!type) {
  48. throw new flatc.Error("Failed to resolve root type '" + root_type + "'.");
  49. }
  50. this.root.root_type.add(type);
  51. }
  52. this.root_type.clear();
  53. }
  54. super.resolve();
  55. }
  56. }
  57. find(name, type) {
  58. if (type === flatc.Type) {
  59. const parts = name.split('.');
  60. const typeName = parts.pop();
  61. const namespaceName = parts.join('.');
  62. if (namespaceName === '') {
  63. if (this.children.has(typeName)) {
  64. return this.children.get(typeName);
  65. }
  66. }
  67. const namespace = this.parent.find(namespaceName, flatc.Namespace);
  68. if (namespace) {
  69. if (namespace.children.has(typeName)) {
  70. return namespace.children.get(typeName);
  71. }
  72. }
  73. const parents = this.name.split('.');
  74. while (parents.length > 1) {
  75. parents.pop();
  76. const namespace = this.parent.find(parents.join('.') + '.' + namespaceName, flatc.Namespace);
  77. if (namespace) {
  78. if (namespace.children.has(typeName)) {
  79. return namespace.children.get(typeName);
  80. }
  81. }
  82. }
  83. }
  84. return super.find(name, type);
  85. }
  86. };
  87. flatc.Type = class extends flatc.Object {
  88. constructor(parent, name) {
  89. super(parent, name);
  90. if (parent instanceof flatc.Namespace) {
  91. if (parent.children.has(name)) {
  92. throw new flatc.Error("Duplicate identifier '" + name + "'.");
  93. }
  94. parent.children.set(name, this);
  95. }
  96. }
  97. };
  98. flatc.Enum = class extends flatc.Type {
  99. constructor(parent, name, base) {
  100. super(parent, name);
  101. this.base = base;
  102. this.values = new Map();
  103. }
  104. resolve() {
  105. if (!this.resolved) {
  106. if (this.base instanceof flatc.TypeReference) {
  107. this.base = this.base.resolve(this);
  108. this.defaultValue = this.base.defaultValue;
  109. }
  110. let index = 0;
  111. for (const key of this.values.keys()) {
  112. if (this.values.get(key) === undefined) {
  113. this.values.set(key, index);
  114. }
  115. index = this.values.get(key) + 1;
  116. }
  117. this.keys = new Map(Array.from(this.values).map((pair) => [ pair[1], pair[0] ]));
  118. super.resolve();
  119. }
  120. }
  121. };
  122. flatc.Union = class extends flatc.Type {
  123. constructor(parent, name) {
  124. super(parent, name);
  125. this.values = new Map();
  126. }
  127. resolve() {
  128. if (!this.resolved) {
  129. let index = 1;
  130. for (const key of this.values.keys()) {
  131. if (this.values.get(key) === undefined) {
  132. this.values.set(key, index);
  133. }
  134. index = this.values.get(key) + 1;
  135. }
  136. const map = new Map();
  137. for (const pair of this.values) {
  138. const type = this.parent.find(pair[0], flatc.Type);
  139. map.set(pair[1], type);
  140. }
  141. this.values = map;
  142. super.resolve();
  143. }
  144. }
  145. };
  146. flatc.Table = class extends flatc.Type {
  147. constructor(parent, name) {
  148. super(parent, name);
  149. this.fields = new Map();
  150. }
  151. resolve() {
  152. if (!this.resolved) {
  153. let offset = 4;
  154. for (const field of this.fields.values()) {
  155. field.resolve();
  156. field.offset = offset;
  157. offset += (field.type instanceof flatc.Union) ? 4 : 2;
  158. }
  159. super.resolve();
  160. }
  161. }
  162. };
  163. flatc.Struct = class extends flatc.Type {
  164. constructor(parent, name) {
  165. super(parent, name);
  166. this.fields = new Map();
  167. this.size = -1;
  168. }
  169. resolve() {
  170. if (!this.resolved) {
  171. let offset = 0;
  172. for (const field of this.fields.values()) {
  173. field.resolve();
  174. if (field.type instanceof flatc.PrimitiveType && field.type !== 'string') {
  175. const size = field.type.size;
  176. field.offset = (offset % size != 0) ? (Math.floor(offset / size) + 1) * size : offset;
  177. offset = field.offset + field.type.size;
  178. }
  179. else if (field.type instanceof flatc.Struct) {
  180. field.type.resolve();
  181. const align = 8;
  182. field.offset = (offset % align != 0) ? (Math.floor(offset / align) + 1) * align : offset;
  183. offset += field.type.size;
  184. }
  185. else {
  186. throw flatc.Error('Structs may contain only scalar or struct fields.');
  187. }
  188. }
  189. this.size = offset;
  190. super.resolve();
  191. }
  192. }
  193. };
  194. flatc.Field = class extends flatc.Object {
  195. constructor(parent, name, type, defaultValue) {
  196. super(parent, name);
  197. this.type = type;
  198. this.defaultValue = defaultValue;
  199. }
  200. resolve() {
  201. if (!this.resolved) {
  202. if (this.type instanceof flatc.TypeReference) {
  203. if (this.type.repeated) {
  204. this.repeated = true;
  205. }
  206. this.type = this.type.resolve(this);
  207. if (this.defaultValue === undefined) {
  208. let type = this.type instanceof flatc.Enum ? this.type.base : this.type;
  209. if (type instanceof flatc.TypeReference) {
  210. type = type.resolve(this);
  211. }
  212. if (type instanceof flatc.PrimitiveType) {
  213. this.defaultValue = type.defaultValue;
  214. }
  215. }
  216. else if (this.type instanceof flatc.Enum) {
  217. this.type.resolve();
  218. if (this.type.values.has(this.defaultValue)) {
  219. this.defaultValue = this.type.values.get(this.defaultValue);
  220. }
  221. else if (!new Set(this.type.values.values()).has(this.defaultValue)) {
  222. throw new flatc.Error("Unsupported enum value '" + this.defaultValue + "'.");
  223. }
  224. }
  225. }
  226. super.resolve();
  227. }
  228. }
  229. };
  230. flatc.PrimitiveType = class extends flatc.Type {
  231. constructor(name, defaultValue, size) {
  232. super(null, name);
  233. this.defaultValue = defaultValue;
  234. this.size = size;
  235. }
  236. static get(name) {
  237. if (!this._map) {
  238. this._map = new Map();
  239. const register = (names, defaultValue, size) => {
  240. const type = new flatc.PrimitiveType(names[0], defaultValue, size);
  241. for (const name of names) {
  242. this._map.set(name, type);
  243. }
  244. };
  245. register([ 'bool' ], false, 1);
  246. register([ 'int8', 'byte' ], 0, 1);
  247. register([ 'uint8', 'ubyte' ], 0, 1);
  248. register([ 'int16', 'short' ], 0, 2);
  249. register([ 'uint16', 'ushort' ], 0, 2);
  250. register([ 'int32', 'int' ], 0, 4);
  251. register([ 'uint32', 'uint' ], 0, 4);
  252. register([ 'int64', 'long' ], 0, 8);
  253. register([ 'uint64', 'ulong' ], 0, 8);
  254. register([ 'float32', 'float' ], 0.0, 4);
  255. register([ 'float64', 'double' ], 0, 4);
  256. register([ 'string' ], null, undefined);
  257. }
  258. return this._map.get(name);
  259. }
  260. };
  261. flatc.TypeReference = class {
  262. constructor(name, repeated) {
  263. this.name = name;
  264. this.repeated = repeated;
  265. }
  266. resolve(context) {
  267. const primitiveType = flatc.PrimitiveType.get(this.name);
  268. if (primitiveType) {
  269. return primitiveType;
  270. }
  271. const type = context.parent.find(this.name, flatc.Type);
  272. if (type) {
  273. return type;
  274. }
  275. throw new flatc.Error("Falied to resolve type '" + this.name + "'.");
  276. }
  277. };
  278. flatc.Parser = class {
  279. constructor(text, file, root) {
  280. // https://google.github.io/flatbuffers/flatbuffers_grammar.html
  281. this._tokenizer = new flatc.Parser.Tokenizer(text, file);
  282. this._root = root;
  283. this._context = root.defineNamespace('');
  284. }
  285. include() {
  286. const includes = [];
  287. while (!this._tokenizer.match('eof') && this._tokenizer.eat('id', 'include')) {
  288. includes.push(this._tokenizer.string());
  289. this._tokenizer.expect(';');
  290. }
  291. return includes;
  292. }
  293. parse() {
  294. const attributes = [];
  295. while (!this._tokenizer.match('eof')) {
  296. if (this._tokenizer.eat('id', 'namespace')) {
  297. let name = this._tokenizer.identifier();
  298. while (this._tokenizer.eat('.')) {
  299. name += '.' + this._tokenizer.identifier();
  300. }
  301. this._tokenizer.expect(';');
  302. this._context = this._root.defineNamespace(name);
  303. continue;
  304. }
  305. if (this._tokenizer.eat('id', 'table')) {
  306. const name = this._tokenizer.identifier();
  307. const table = new flatc.Table(this._context, name);
  308. this._parseMetadata(table.metadata);
  309. this._tokenizer.expect('{');
  310. while (!this._tokenizer.eat('}')) {
  311. const field = this._parseField(table);
  312. table.fields.set(field.name, field);
  313. this._tokenizer.expect(';');
  314. }
  315. continue;
  316. }
  317. if (this._tokenizer.eat('id', 'struct')) {
  318. const name = this._tokenizer.identifier();
  319. const struct = new flatc.Struct(this._context, name);
  320. this._parseMetadata(struct.metadata);
  321. this._tokenizer.expect('{');
  322. while (!this._tokenizer.eat('}')) {
  323. const field = this._parseField(struct);
  324. struct.fields.set(field.name, field);
  325. this._tokenizer.expect(';');
  326. }
  327. continue;
  328. }
  329. if (this._tokenizer.eat('id', 'enum')) {
  330. const name = this._tokenizer.identifier();
  331. this._tokenizer.expect(':');
  332. const base = this._parseTypeReference();
  333. if (base.repeated) {
  334. throw new flatc.Error('Underlying enum type must be integral' + this._tokenizer.location());
  335. }
  336. const type = new flatc.Enum(this._context, name, base);
  337. this._parseMetadata(type.metadata);
  338. this._tokenizer.expect('{');
  339. while (!this._tokenizer.eat('}')) {
  340. const key = this._tokenizer.identifier();
  341. const value = this._tokenizer.eat('=') ? this._tokenizer.integer() : undefined;
  342. type.values.set(key, value);
  343. if (this._tokenizer.eat(',')) {
  344. continue;
  345. }
  346. }
  347. continue;
  348. }
  349. if (this._tokenizer.eat('id', 'union')) {
  350. const name = this._tokenizer.identifier();
  351. const union = new flatc.Union(this._context, name);
  352. this._parseMetadata(union.metadata);
  353. this._tokenizer.expect('{');
  354. while (!this._tokenizer.eat('}')) {
  355. const name = this._tokenizer.identifier();
  356. const key = this._tokenizer.eat(':') ? this._tokenizer.identifier() : name;
  357. const value = this._tokenizer.eat('=') ? this._tokenizer.integer() : undefined;
  358. union.values.set(key, value);
  359. if (this._tokenizer.eat(',')) {
  360. continue;
  361. }
  362. }
  363. continue;
  364. }
  365. if (this._tokenizer.eat('id', 'rpc_service')) {
  366. throw new flatc.Error("Unsupported keyword 'rpc_service'." + this._tokenizer.location());
  367. }
  368. if (this._tokenizer.eat('id', 'root_type')) {
  369. this._context.root_type.add(this._tokenizer.identifier());
  370. this._tokenizer.eat(';');
  371. continue;
  372. }
  373. if (this._tokenizer.eat('id', 'file_extension')) {
  374. const value = this._tokenizer.string();
  375. this._root.file_extension = value;
  376. this._tokenizer.eat(';');
  377. continue;
  378. }
  379. if (this._tokenizer.eat('id', 'file_identifier')) {
  380. const value = this._tokenizer.string();
  381. if (value.length !== 4) {
  382. throw new flatc.Error("'file_identifier' must be exactly 4 characters " + this._tokenizer.location());
  383. }
  384. this._root.file_identifier = value;
  385. this._tokenizer.eat(';');
  386. continue;
  387. }
  388. if (this._tokenizer.eat('id', 'attribute')) {
  389. const token = this._tokenizer.read();
  390. switch (token.type) {
  391. case 'string':
  392. attributes.push(token.value);
  393. break;
  394. case 'id':
  395. attributes.push(token.token);
  396. break;
  397. default:
  398. throw new flatc.Error("Unexpected attribute token '" + token.token + "'" + this._tokenizer.location());
  399. }
  400. this._tokenizer.expect(';');
  401. continue;
  402. }
  403. if (this._tokenizer.eat('{')) {
  404. throw new flatc.Error('Unsupported object.' + this._tokenizer.location());
  405. }
  406. throw new flatc.Error("Unexpected token '" + this._tokenizer.peek().token + "'" + this._tokenizer.location());
  407. }
  408. }
  409. _parseTypeReference() {
  410. const token = this._tokenizer.read();
  411. if (token.type === 'id') {
  412. return new flatc.TypeReference(token.token, false);
  413. }
  414. if (token.type === '[') {
  415. const identifier = this._tokenizer.read();
  416. if (identifier.type === 'id') {
  417. this._tokenizer.expect(']');
  418. return new flatc.TypeReference(identifier.token, true);
  419. }
  420. }
  421. throw new flatc.Error("Expected type instead of '" + token.token + "'" + this._tokenizer.location());
  422. }
  423. _parseField(parent) {
  424. const name = this._tokenizer.identifier();
  425. this._tokenizer.expect(':');
  426. const type = this._parseTypeReference();
  427. const defaultValue = this._tokenizer.eat('=') ? this._parseScalar() : undefined;
  428. const field = new flatc.Field(parent, name, type, defaultValue);
  429. this._parseMetadata(field.metadata);
  430. return field;
  431. }
  432. _parseMetadata(metadata) {
  433. if (this._tokenizer.eat('(')) {
  434. while (!this._tokenizer.eat(')')) {
  435. const key = this._tokenizer.identifier();
  436. const value = this._tokenizer.eat(':') ? this._parseSingleValue() : undefined;
  437. metadata.set(key, value);
  438. if (this._tokenizer.eat(',')) {
  439. continue;
  440. }
  441. }
  442. }
  443. }
  444. _parseScalar() {
  445. const token = this._tokenizer.read();
  446. switch (token.type) {
  447. case 'boolean':
  448. case 'integer':
  449. case 'float':
  450. return token.value;
  451. case 'id':
  452. return token.token;
  453. default:
  454. throw new flatc.Error("Expected scalar instead of '" + token.token + "'" + this._tokenizer.location());
  455. }
  456. }
  457. _parseSingleValue() {
  458. const token = this._tokenizer.read();
  459. switch (token.type) {
  460. case 'string':
  461. case 'boolean':
  462. case 'integer':
  463. case 'float':
  464. return token.value;
  465. default:
  466. throw new flatc.Error("Expected single value instead of '" + token.token + "'" + this._token.location());
  467. }
  468. }
  469. };
  470. flatc.Parser.Tokenizer = class {
  471. constructor(text, file) {
  472. this._text = text;
  473. this._file = file;
  474. this._position = 0;
  475. this._lineStart = 0;
  476. this._line = 0;
  477. this._token = { type: '', value: '' };
  478. }
  479. peek() {
  480. if (!this._cache) {
  481. this._token = this._tokenize();
  482. this._cache = true;
  483. }
  484. return this._token;
  485. }
  486. read() {
  487. if (!this._cache) {
  488. this._token = this._tokenize();
  489. }
  490. const next = this._position + this._token.token.length;
  491. while (this._position < next) {
  492. if (flatc.Parser.Tokenizer._isNewline(this._get(this._position))) {
  493. this._position = this._newLine(this._position);
  494. this._lineStart = this._position;
  495. this._line++;
  496. }
  497. else {
  498. this._position++;
  499. }
  500. }
  501. this._cache = false;
  502. return this._token;
  503. }
  504. match(type, value) {
  505. const token = this.peek();
  506. if (token.type === type && (!value || token.token === value)) {
  507. return true;
  508. }
  509. return false;
  510. }
  511. eat(type, value) {
  512. const token = this.peek();
  513. if (token.type === type && (!value || token.token === value)) {
  514. this.read();
  515. return true;
  516. }
  517. return false;
  518. }
  519. expect(type, value) {
  520. const token = this.peek();
  521. if (token.type !== type) {
  522. throw new flatc.Error("Unexpected '" + token.token + "' instead of '" + type + "'" + this.location());
  523. }
  524. if (value && token.token !== value) {
  525. throw new flatc.Error("Unexpected '" + token.token + "' instead of '" + value + "'" + this.location());
  526. }
  527. this.read();
  528. }
  529. string() {
  530. const token = this.read();
  531. if (token.type === 'string') {
  532. return token.value;
  533. }
  534. throw new flatc.Error("Expected string instead of '" + token.token + "'" + this.location());
  535. }
  536. identifier() {
  537. const token = this.read();
  538. if (token.type === 'id') {
  539. return token.token;
  540. }
  541. throw new flatc.Error("Expected identifier instead of '" + token.token + "'" + this.location());
  542. }
  543. integer() {
  544. const token = this.read();
  545. if (token.type === 'integer') {
  546. return token.value;
  547. }
  548. throw new flatc.Error("Expected integer instead of '" + token.token + "'" + this.location());
  549. }
  550. location() {
  551. return ' at ' + this._file + ':' + (this._line + 1).toString() + ':' + (this._position - this._lineStart + 1).toString();
  552. }
  553. _tokenize() {
  554. if (this._token.type !== '\n') {
  555. this._skipWhitespace();
  556. }
  557. if (this._position >= this._text.length) {
  558. return { type: 'eof', value: '' };
  559. }
  560. const content = this._text.slice(this._position);
  561. const boolean_constant = content.match(/^(true|false)/);
  562. if (boolean_constant) {
  563. const content = boolean_constant[0];
  564. return { type: 'boolean', token: content, value: content === 'true' };
  565. }
  566. const identifier = content.match(/^[a-zA-Z_][a-zA-Z0-9_.]*/);
  567. if (identifier) {
  568. return { type: 'id', token: identifier[0] };
  569. }
  570. const string_constant = content.match(/^".*?"/) || content.match(/^'.*?'/);
  571. if (string_constant) {
  572. const content = string_constant[0];
  573. return { type: 'string', token: content, value: content.substring(1, content.length - 1) };
  574. }
  575. const dec_float_constant = content.match(/^[-+]?(([.][0-9]+)|([0-9]+[.][0-9]*)|([0-9]+))([eE][-+]?[0-9]+)?/);
  576. if (dec_float_constant) {
  577. const content = dec_float_constant[0];
  578. if (content.indexOf('.') !== -1 || content.indexOf('e') !== -1) {
  579. return { type: 'float', token: content, value: parseFloat(content) };
  580. }
  581. }
  582. const hex_float_constant = content.match(/^[-+]?0[xX](([.][0-9a-fA-F]+)|([0-9a-fA-F]+[.][0-9a-fA-F]*)|([0-9a-fA-F]+))([pP][-+]?[0-9]+)/);
  583. if (hex_float_constant) {
  584. throw new flatc.Error('Unsupported hexadecimal constant.');
  585. }
  586. const dec_integer_constant = content.match(/^[-+]?[0-9]+/);
  587. if (dec_integer_constant) {
  588. const content = dec_integer_constant[0];
  589. return { type: 'integer', token: content, value: parseInt(content, 10) };
  590. }
  591. const hex_integer_constant = content.match(/^[-+]?0[xX][0-9a-fA-F]+/);
  592. if (hex_integer_constant) {
  593. throw new flatc.Error('Unsupported hexadecimal constant.');
  594. }
  595. const c = this._get(this._position);
  596. switch (c) {
  597. case ';':
  598. case ':':
  599. case '{':
  600. case '}':
  601. case '[':
  602. case ']':
  603. case '(':
  604. case ')':
  605. case '=':
  606. case ',':
  607. return { type: c, token: c };
  608. default:
  609. throw new flatc.Error("Unsupported character '" + c + "' " + this.location());
  610. }
  611. }
  612. _get(position) {
  613. return position >= this._text.length ? '\0' : this._text[position];
  614. }
  615. _skipLine() {
  616. while (this._position < this._text.length) {
  617. if (flatc.Parser.Tokenizer._isNewline(this._get(this._position))) {
  618. break;
  619. }
  620. this._position++;
  621. }
  622. }
  623. _skipWhitespace() {
  624. while (this._position < this._text.length) {
  625. const c = this._get(this._position);
  626. if (flatc.Parser.Tokenizer._isSpace(c)) {
  627. this._position++;
  628. continue;
  629. }
  630. if (flatc.Parser.Tokenizer._isNewline(c)) {
  631. // Implicit Line Continuation
  632. this._position = this._newLine(this._position);
  633. this._lineStart = this._position;
  634. this._line++;
  635. continue;
  636. }
  637. if (c === '/') {
  638. const c1 = this._get(this._position + 1);
  639. if (c1 === '/') {
  640. this._skipLine();
  641. continue;
  642. }
  643. if (c1 === '*') {
  644. this._position += 2;
  645. while (this._get(this._position) !== '*' || this._get(this._position + 1) !== '/') {
  646. this._position++;
  647. if ((this._position + 2) > this._text.length) {
  648. throw new flatc.Error('Unexpected end of file in comment.');
  649. }
  650. }
  651. this._position += 2;
  652. continue;
  653. }
  654. }
  655. break;
  656. }
  657. }
  658. static _isSpace(c) {
  659. switch (c) {
  660. case ' ':
  661. case '\t':
  662. case '\v': // 11
  663. case '\f': // 12
  664. case '\xA0': // 160
  665. return true;
  666. default:
  667. return false;
  668. }
  669. }
  670. static _isNewline(c) {
  671. switch(c) {
  672. case '\n':
  673. case '\r':
  674. case '\u2028': // 8232
  675. case '\u2029': // 8233
  676. return true;
  677. default:
  678. return false;
  679. }
  680. }
  681. _newLine(position) {
  682. if ((this._get(position) === '\n' && this._get(position + 1) === '\r') ||
  683. (this._get(position) === '\r' && this._get(position + 1) === '\n')) {
  684. return position + 2;
  685. }
  686. return position + 1;
  687. }
  688. };
  689. flatc.Root = class extends flatc.Object {
  690. constructor(root, paths, files) {
  691. super(null, root);
  692. this._namespaces = new Map();
  693. this._files = new Set();
  694. this.root_type = new Set();
  695. for (const file of files) {
  696. this._parseFile(paths, file);
  697. }
  698. this.resolve();
  699. }
  700. resolve() {
  701. if (!this.resolved) {
  702. for (const namespace of this._namespaces.values()) {
  703. namespace.resolve();
  704. }
  705. super.resolve();
  706. }
  707. }
  708. get root() {
  709. return this;
  710. }
  711. get namespaces() {
  712. return this._namespaces;
  713. }
  714. set(name, value) {
  715. this.metadata.set(name, value);
  716. }
  717. get(name) {
  718. return this.metadata.get(name);
  719. }
  720. defineNamespace(name) {
  721. if (!this._namespaces.has(name)) {
  722. this._namespaces.set(name, new flatc.Namespace(this, name));
  723. }
  724. return this._namespaces.get(name);
  725. }
  726. find(name, type) {
  727. if (type === flatc.Namespace) {
  728. if (this._namespaces.has(name)) {
  729. return this._namespaces.get(name);
  730. }
  731. }
  732. return super.find(name, type);
  733. }
  734. _parseFile(paths, file) {
  735. if (!this._files.has(file)) {
  736. this._files.add(file);
  737. const content = fs.readFileSync(file, 'utf-8');
  738. const parser = new flatc.Parser(content, file, this);
  739. const includes = parser.include();
  740. for (const include of includes) {
  741. const includeFile = this._resolve(paths, file, include);
  742. if (includeFile) {
  743. this._parseFile(paths, includeFile);
  744. continue;
  745. }
  746. throw new flatc.Error("Include '" + include + "' not found.");
  747. }
  748. parser.parse();
  749. }
  750. }
  751. _resolve(paths, origin, target) {
  752. const file = path.join(path.dirname(origin), target);
  753. if (fs.existsSync(file)) {
  754. return file;
  755. }
  756. for (const current of paths) {
  757. const file = path.join(current, target);
  758. if (fs.existsSync(file)) {
  759. return file;
  760. }
  761. }
  762. return null;
  763. }
  764. };
  765. flatc.Generator = class {
  766. constructor(root, text) {
  767. this._root = root;
  768. this._text = text;
  769. this._builder = new flatc.Generator.StringBuilder();
  770. this._builder.add("var $root = flatbuffers.get('" + this._root.name + "');");
  771. for (const namespace of this._root.namespaces.values()) {
  772. this._buildNamespace(namespace);
  773. }
  774. this._content = this._builder.toString();
  775. }
  776. get content() {
  777. return this._content;
  778. }
  779. _buildNamespace(namespace) {
  780. if (namespace.name !== '') {
  781. const parts = namespace.name.split('.');
  782. for (let i = 1; i <= parts.length; i++) {
  783. const name = '$root.' + parts.slice(0, i).join('.');
  784. this._builder.add('');
  785. this._builder.add(name + ' = ' + name + ' || {};');
  786. }
  787. }
  788. for (const child of namespace.children.values()) {
  789. if (child instanceof flatc.Table) {
  790. this._buildTable(child);
  791. }
  792. else if (child instanceof flatc.Struct) {
  793. this._buildStruct(child);
  794. }
  795. else if (child instanceof flatc.Union) {
  796. this._buildUnion(child);
  797. }
  798. else if (child instanceof flatc.Enum) {
  799. this._buildEnum(child);
  800. }
  801. }
  802. }
  803. _buildTable(type) {
  804. const typeName = type.parent.name + '.' + type.name;
  805. const typeReference = '$root.' + typeName;
  806. /* eslint-disable indent */
  807. this._builder.add('');
  808. this._builder.add(typeReference + ' = class ' + type.name + ' {');
  809. this._builder.indent();
  810. if (this._root.root_type.has(type)) {
  811. const file_identifier = this._root.file_identifier;
  812. if (file_identifier) {
  813. this._builder.add('');
  814. this._builder.add('static identifier(reader) {');
  815. this._builder.indent();
  816. this._builder.add("return reader.identifier === '" + file_identifier + "';");
  817. this._builder.outdent();
  818. this._builder.add('}');
  819. }
  820. this._builder.add('');
  821. this._builder.add('static create(reader) {');
  822. this._builder.indent();
  823. this._builder.add('return ' + typeReference + '.decode(reader, reader.root);');
  824. this._builder.outdent();
  825. this._builder.add('}');
  826. if (this._text) {
  827. this._builder.add('');
  828. this._builder.add('static createText(reader) {');
  829. this._builder.indent();
  830. this._builder.add('return ' + typeReference + '.decodeText(reader, reader.root);');
  831. this._builder.outdent();
  832. this._builder.add('}');
  833. }
  834. }
  835. this._builder.add('');
  836. this._builder.add(type.fields.size !== 0 ? 'static decode(reader, position) {' : 'static decode(/* reader, position */) {');
  837. this._builder.indent();
  838. this._builder.add('const $ = new ' + typeReference + '();');
  839. for (const field of type.fields.values()) {
  840. const fieldType = field.type instanceof flatc.Enum ? field.type.base : field.type;
  841. if (field.repeated) {
  842. if (fieldType instanceof flatc.PrimitiveType) {
  843. switch (field.type.name) {
  844. case 'int64': {
  845. this._builder.add('$.' + field.name + ' = reader.int64s_(position, ' + field.offset + ');');
  846. break;
  847. }
  848. case 'uint64': {
  849. this._builder.add('$.' + field.name + ' = reader.uint64s_(position, ' + field.offset + ');');
  850. break;
  851. }
  852. case 'string': {
  853. this._builder.add('$.' + field.name + ' = reader.strings_(position, ' + field.offset + ');');
  854. break;
  855. }
  856. case 'bool': {
  857. this._builder.add('$.' + field.name + ' = reader.bools_(position, ' + field.offset + ');');
  858. break;
  859. }
  860. default: {
  861. const arrayType = fieldType.name[0].toUpperCase() + fieldType.name.substring(1) + 'Array';
  862. this._builder.add('$.' + field.name + ' = reader.typedArray(position, ' + field.offset + ', ' + arrayType + ');');
  863. break;
  864. }
  865. }
  866. }
  867. else if (fieldType instanceof flatc.Union) {
  868. const unionType = '$root.' + field.type.parent.name + '.' + field.type.name;
  869. this._builder.add('$.' + field.name + ' = reader.unionArray(position, ' + field.offset + ', ' + ',' + unionType + '.decode);');
  870. }
  871. else if (fieldType instanceof flatc.Struct) {
  872. const fieldType = '$root.' + field.type.parent.name + '.' + field.type.name;
  873. this._builder.add('$.' + field.name + ' = reader.structArray(position, ' + field.offset + ', ' + field.size + ',' + fieldType + '.decode);');
  874. }
  875. else {
  876. const fieldType = '$root.' + field.type.parent.name + '.' + field.type.name;
  877. this._builder.add('$.' + field.name + ' = reader.tableArray(position, ' + field.offset + ', ' + fieldType + '.decode);');
  878. }
  879. }
  880. else if (fieldType instanceof flatc.PrimitiveType) {
  881. this._builder.add('$.' + field.name + ' = reader.' + fieldType.name + '_(position, ' + field.offset + ', ' + field.defaultValue + ');');
  882. }
  883. else if (fieldType instanceof flatc.Union) {
  884. const unionType = '$root.' + field.type.parent.name + '.' + field.type.name;
  885. this._builder.add('$.' + field.name + ' = reader.union(position, ' + field.offset + ', ' + unionType + '.decode);');
  886. }
  887. else if (fieldType instanceof flatc.Struct) {
  888. const fieldType = '$root.' + field.type.parent.name + '.' + field.type.name;
  889. this._builder.add('$.' + field.name + ' = reader.struct(position, ' + field.offset + ', ' + fieldType + '.decode);');
  890. }
  891. else {
  892. const fieldType = '$root.' + field.type.parent.name + '.' + field.type.name;
  893. this._builder.add('$.' + field.name + ' = reader.table(position, ' + field.offset + ', ' + fieldType + '.decode);');
  894. }
  895. }
  896. this._builder.add('return $;');
  897. this._builder.outdent();
  898. this._builder.add('}');
  899. if (this._text) {
  900. this._builder.add('');
  901. this._builder.add(type.fields.size !== 0 ? 'static decodeText(reader, json) {' : 'static decodeText(/* reader, json */) {');
  902. this._builder.indent();
  903. this._builder.add('const $ = new ' + typeReference + '();');
  904. for (const field of type.fields.values()) {
  905. if (field.repeated) {
  906. if (field.type instanceof flatc.PrimitiveType) {
  907. switch (field.type.name) {
  908. case 'int64':
  909. case 'uint64':
  910. case 'string':
  911. case 'bool': {
  912. this._builder.add('$.' + field.name + ' = reader.array(json.' + field.name + ');');
  913. break;
  914. }
  915. default: {
  916. const arrayType = field.type.name[0].toUpperCase() + field.type.name.substring(1) + 'Array';
  917. this._builder.add('$.' + field.name + ' = reader.typedArray(json.' + field.name + ', ' + arrayType + ');');
  918. break;
  919. }
  920. }
  921. }
  922. else if (field.type instanceof flatc.Union) {
  923. throw new flatc.Error('Not implemented.');
  924. }
  925. else if (field.type instanceof flatc.Struct) {
  926. const fieldType = '$root.' + field.type.parent.name + '.' + field.type.name;
  927. this._builder.add('$.' + field.name + ' = ' + fieldType + '.decode(reader, position + ' + field.offset + ');');
  928. }
  929. else {
  930. const fieldType = '$root.' + field.type.parent.name + '.' + field.type.name;
  931. this._builder.add('$.' + field.name + ' = reader.objectArray(json.' + field.name + ', ' + fieldType + '.decodeText);');
  932. }
  933. }
  934. else if (field.type instanceof flatc.PrimitiveType) {
  935. this._builder.add('$.' + field.name + ' = reader.value(json.' + field.name + ', ' + field.defaultValue + ');');
  936. }
  937. else if (field.type instanceof flatc.Enum) {
  938. const enumName = '$root.' + field.type.parent.name + '.' + field.type.name;
  939. this._builder.add('$.' + field.name + ' = ' + enumName + '[json.' + field.name + '];');
  940. }
  941. else if (field.type instanceof flatc.Union) {
  942. const unionType = '$root.' + field.type.parent.name + '.' + field.type.name;
  943. this._builder.add('$.' + field.name + ' = ' + unionType + '.decodeText(reader, json.' + field.name + ', json.' + field.name + '_type' + ');');
  944. }
  945. else { // struct | table
  946. const fieldType = '$root.' + field.type.parent.name + '.' + field.type.name;
  947. this._builder.add('$.' + field.name + ' = reader.object(json.' + field.name + ', ' + fieldType + '.decodeText);');
  948. }
  949. }
  950. this._builder.add('return $;');
  951. this._builder.outdent();
  952. this._builder.add('}');
  953. }
  954. this._builder.outdent();
  955. this._builder.add('};');
  956. /* eslint-enable indent */
  957. }
  958. _buildStruct(type) {
  959. const typeName = type.parent.name + '.' + type.name;
  960. const typeReference = '$root.' + typeName;
  961. /* eslint-disable indent */
  962. this._builder.add('');
  963. this._builder.add(typeReference + ' = class ' + type.name + ' {');
  964. this._builder.indent();
  965. this._builder.add('');
  966. this._builder.add(type.fields.size !== 0 ? 'static decode(reader, position) {' : 'static decode(/* reader, position */) {');
  967. this._builder.indent();
  968. this._builder.add('const $ = new ' + typeReference + '();');
  969. for (const field of type.fields.values()) {
  970. if (field.repeated) {
  971. throw new flatc.Error("Struct '" + typeName + "' may contain only scalar or struct fields.");
  972. }
  973. else if (field.type instanceof flatc.PrimitiveType) {
  974. this._builder.add('$.' + field.name + ' = reader.' + field.type.name + '(position + ' + field.offset + ');');
  975. }
  976. else if (field.type instanceof flatc.Enum) {
  977. throw new flatc.Error('Not implemented.');
  978. }
  979. else if (field.type instanceof flatc.Struct) {
  980. const fieldType = '$root.' + field.type.parent.name + '.' + field.type.name;
  981. this._builder.add('$.' + field.name + ' = ' + fieldType + '.decode(reader, position + ' + field.offset + ');');
  982. }
  983. else {
  984. throw new flatc.Error("Struct '" + typeName + "' may contain only scalar or struct fields.");
  985. }
  986. }
  987. this._builder.add('return $;');
  988. this._builder.outdent();
  989. this._builder.add('}');
  990. if (this._text) {
  991. this._builder.add('');
  992. this._builder.add(type.fields.size !== 0 ? 'static decodeText(reader, json) {' : 'static decodeText(/* reader, json */) {');
  993. this._builder.indent();
  994. this._builder.add('const $ = new ' + typeReference + '();');
  995. for (const field of type.fields.values()) {
  996. if (field.repeated) {
  997. throw new flatc.Error("Struct '" + typeName + "' may contain only scalar or struct fields.");
  998. }
  999. else if (field.type instanceof flatc.PrimitiveType) {
  1000. this._builder.add('$.' + field.name + ' = json.' + field.name + ';');
  1001. }
  1002. else if (field.type instanceof flatc.Enum) {
  1003. throw new flatc.Error('Not implemented.');
  1004. }
  1005. else if (field.type instanceof flatc.Struct) {
  1006. const fieldType = '$root.' + field.type.parent.name + '.' + field.type.name;
  1007. this._builder.add('$.' + field.name + ' = ' + fieldType + '.decodeText(reader, json.' + field.name + ');');
  1008. }
  1009. else {
  1010. throw new flatc.Error("Struct '" + typeName + "' may contain only scalar or struct fields.");
  1011. }
  1012. }
  1013. this._builder.add('return $;');
  1014. this._builder.outdent();
  1015. this._builder.add('}');
  1016. }
  1017. this._builder.outdent();
  1018. this._builder.add('};');
  1019. /* eslint-enable indent */
  1020. }
  1021. _buildUnion(type) {
  1022. /* eslint-disable indent */
  1023. this._builder.add('');
  1024. this._builder.add('$root.' + type.parent.name + '.' + type.name + ' = class ' + '{');
  1025. this._builder.indent();
  1026. this._builder.add('');
  1027. this._builder.add(type.values.size !== 0 ? 'static decode(reader, position, type) {' : 'static decode(/* reader, position, type */) {');
  1028. this._builder.indent();
  1029. this._builder.add('switch (type) {');
  1030. this._builder.indent();
  1031. for (const pair of type.values) {
  1032. const valueType = '$root.' + pair[1].parent.name + '.' + pair[1].name;
  1033. this._builder.add('case ' + pair[0] + ': return ' + valueType + '.decode(reader, position);');
  1034. }
  1035. this._builder.add('default: return undefined;');
  1036. this._builder.outdent();
  1037. this._builder.add('}');
  1038. this._builder.outdent();
  1039. this._builder.add('}');
  1040. this._builder.add('');
  1041. this._builder.add(type.values.size !== 0 ? 'static decodeText(reader, json, type) {' : 'static decodeText(/* reader, json, type */) {');
  1042. this._builder.indent();
  1043. this._builder.add('switch (type) {');
  1044. this._builder.indent();
  1045. for (const pair of type.values) {
  1046. const valueType = '$root.' + pair[1].parent.name + '.' + pair[1].name;
  1047. this._builder.add('case \'' + pair[1].name + '\': return ' + valueType + '.decodeText(reader, json);');
  1048. }
  1049. this._builder.add('default: return undefined;');
  1050. this._builder.outdent();
  1051. this._builder.add('}');
  1052. this._builder.outdent();
  1053. this._builder.add('}');
  1054. this._builder.outdent();
  1055. this._builder.add('};');
  1056. /* eslint-enable indent */
  1057. }
  1058. _buildEnum(type) {
  1059. /* eslint-disable indent */
  1060. this._builder.add('');
  1061. this._builder.add('$root.' + type.parent.name + '.' + type.name + ' = {');
  1062. this._builder.indent();
  1063. const keys = Array.from(type.values.keys());
  1064. for (let i = 0; i < keys.length; i++) {
  1065. const key = keys[i];
  1066. this._builder.add(key + ': ' + type.values.get(key) + (i === keys.length - 1 ? '' : ','));
  1067. }
  1068. this._builder.outdent();
  1069. this._builder.add('};');
  1070. /* eslint-enable indent */
  1071. }
  1072. };
  1073. flatc.Generator.StringBuilder = class {
  1074. constructor() {
  1075. this._indentation = '';
  1076. this._lines = [];
  1077. this._newline = true;
  1078. }
  1079. indent() {
  1080. this._indentation += ' ';
  1081. }
  1082. outdent() {
  1083. if (this._indentation.length === 0) {
  1084. throw new flatc.Error('Invalid indentation.');
  1085. }
  1086. this._indentation = this._indentation.substring(0, this._indentation.length - 4);
  1087. }
  1088. add(text, newline) {
  1089. if (this._newline) {
  1090. if (text !== '') {
  1091. this._lines.push(this._indentation);
  1092. }
  1093. }
  1094. this._lines[this._lines.length - 1] = this._lines[this._lines.length - 1] + text + (newline === false ? '' : '\n');
  1095. this._newline = newline === false ? false : true;
  1096. }
  1097. toString() {
  1098. return this._lines.join('');
  1099. }
  1100. };
  1101. flatc.Error = class extends Error {
  1102. constructor(message) {
  1103. super(message);
  1104. this.name = 'FlatBuffers Compiler Error';
  1105. }
  1106. };
  1107. const main = (args) => {
  1108. const options = { verbose: false, root: 'default', out: '', text: false, paths: [], files: [] };
  1109. while (args.length > 0) {
  1110. const arg = args.shift();
  1111. switch (arg) {
  1112. case '--verbose':
  1113. options.verbose = true;
  1114. break;
  1115. case '--out':
  1116. options.out = args.shift();
  1117. break;
  1118. case '--root':
  1119. options.root = args.shift();
  1120. break;
  1121. case '--text':
  1122. options.text = true;
  1123. break;
  1124. case '--path':
  1125. options.paths.push(args.shift());
  1126. break;
  1127. default:
  1128. if (arg.startsWith('-')) {
  1129. throw new flatc.Error("Invalid command line argument '" + arg + "'.");
  1130. }
  1131. options.files.push(arg);
  1132. break;
  1133. }
  1134. }
  1135. try {
  1136. const root = new flatc.Root(options.root, options.paths, options.files);
  1137. const generator = new flatc.Generator(root, options.text);
  1138. if (options.out) {
  1139. fs.writeFileSync(options.out, generator.content, 'utf-8');
  1140. }
  1141. }
  1142. catch (err) {
  1143. if (err instanceof flatc.Error && !options.verbose) {
  1144. process.stderr.write(err.message + '\n');
  1145. }
  1146. else {
  1147. process.stderr.write(err.stack + '\n');
  1148. }
  1149. return 1;
  1150. }
  1151. return 0;
  1152. };
  1153. if (typeof process === 'object' && Array.isArray(process.argv) &&
  1154. process.argv.length > 1 && process.argv[1] === __filename) {
  1155. const args = process.argv.slice(2);
  1156. const code = main(args);
  1157. process.exit(code);
  1158. }
  1159. if (typeof module !== 'undefined' && typeof module.exports === 'object') {
  1160. module.exports.Root = flatc.Root;
  1161. module.exports.Namespace = flatc.Namespace;
  1162. module.exports.Type = flatc.Type;
  1163. module.exports.Enum = flatc.Enum;
  1164. }