flatc.js 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291
  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.type.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 {
  881. if (fieldType instanceof flatc.PrimitiveType) {
  882. this._builder.add('$.' + field.name + ' = reader.' + fieldType.name + '_(position, ' + field.offset + ', ' + field.defaultValue + ');');
  883. }
  884. else if (fieldType instanceof flatc.Union) {
  885. const unionType = '$root.' + field.type.parent.name + '.' + field.type.name;
  886. this._builder.add('$.' + field.name + ' = reader.union(position, ' + field.offset + ', ' + unionType + '.decode);');
  887. }
  888. else if (fieldType instanceof flatc.Struct) {
  889. const fieldType = '$root.' + field.type.parent.name + '.' + field.type.name;
  890. this._builder.add('$.' + field.name + ' = reader.struct(position, ' + field.offset + ', ' + fieldType + '.decode);');
  891. }
  892. else {
  893. const fieldType = '$root.' + field.type.parent.name + '.' + field.type.name;
  894. this._builder.add('$.' + field.name + ' = reader.table(position, ' + field.offset + ', ' + fieldType + '.decode);');
  895. }
  896. }
  897. }
  898. this._builder.add('return $;');
  899. this._builder.outdent();
  900. this._builder.add('}');
  901. if (this._text) {
  902. this._builder.add('');
  903. this._builder.add(type.fields.size !== 0 ? 'static decodeText(reader, json) {' : 'static decodeText(/* reader, json */) {');
  904. this._builder.indent();
  905. this._builder.add('const $ = new ' + typeReference + '();');
  906. for (const field of type.fields.values()) {
  907. if (field.repeated) {
  908. if (field.type instanceof flatc.PrimitiveType) {
  909. switch (field.type.name) {
  910. case 'int64':
  911. case 'uint64':
  912. case 'string':
  913. case 'bool': {
  914. this._builder.add('$.' + field.name + ' = reader.array(json.' + field.name + ');');
  915. break;
  916. }
  917. default: {
  918. const arrayType = field.type.name[0].toUpperCase() + field.type.name.substring(1) + 'Array';
  919. this._builder.add('$.' + field.name + ' = reader.typedArray(json.' + field.name + ', ' + arrayType + ');');
  920. break;
  921. }
  922. }
  923. }
  924. else if (field.type instanceof flatc.Union) {
  925. throw new flatc.Error('Not implemented.');
  926. }
  927. else if (field.type instanceof flatc.Struct) {
  928. const fieldType = '$root.' + field.type.parent.name + '.' + field.type.name;
  929. this._builder.add('$.' + field.name + ' = ' + fieldType + '.decode(reader, position + ' + field.offset + ');');
  930. }
  931. else {
  932. const fieldType = '$root.' + field.type.parent.name + '.' + field.type.name;
  933. this._builder.add('$.' + field.name + ' = reader.objectArray(json.' + field.name + ', ' + fieldType + '.decodeText);');
  934. }
  935. }
  936. else {
  937. if (field.type instanceof flatc.PrimitiveType) {
  938. this._builder.add('$.' + field.name + ' = reader.value(json.' + field.name + ', ' + field.defaultValue + ');');
  939. }
  940. else if (field.type instanceof flatc.Enum) {
  941. const enumName = '$root.' + field.type.parent.name + '.' + field.type.name;
  942. this._builder.add('$.' + field.name + ' = ' + enumName + '[json.' + field.name + '];');
  943. }
  944. else if (field.type instanceof flatc.Union) {
  945. const unionType = '$root.' + field.type.parent.name + '.' + field.type.name;
  946. this._builder.add('$.' + field.name + ' = ' + unionType + '.decodeText(reader, json.' + field.name + ', json.' + field.name + '_type' + ');');
  947. }
  948. else { // struct | table
  949. const fieldType = '$root.' + field.type.parent.name + '.' + field.type.name;
  950. this._builder.add('$.' + field.name + ' = reader.object(json.' + field.name + ', ' + fieldType + '.decodeText);');
  951. }
  952. }
  953. }
  954. this._builder.add('return $;');
  955. this._builder.outdent();
  956. this._builder.add('}');
  957. }
  958. this._builder.outdent();
  959. this._builder.add('};');
  960. /* eslint-enable indent */
  961. }
  962. _buildStruct(type) {
  963. const typeName = type.parent.name + '.' + type.name;
  964. const typeReference = '$root.' + typeName;
  965. /* eslint-disable indent */
  966. this._builder.add('');
  967. this._builder.add(typeReference + ' = class ' + type.name + ' {');
  968. this._builder.indent();
  969. this._builder.add('');
  970. this._builder.add(type.fields.size !== 0 ? 'static decode(reader, position) {' : 'static decode(/* reader, position */) {');
  971. this._builder.indent();
  972. this._builder.add('const $ = new ' + typeReference + '();');
  973. for (const field of type.fields.values()) {
  974. if (field.repeated) {
  975. throw new flatc.Error("Struct '" + typeName + "' may contain only scalar or struct fields.");
  976. }
  977. else {
  978. if (field.type instanceof flatc.PrimitiveType) {
  979. this._builder.add('$.' + field.name + ' = reader.' + field.type.name + '(position + ' + field.offset + ');');
  980. }
  981. else if (field.type instanceof flatc.Enum) {
  982. throw new flatc.Error('Not implemented.');
  983. }
  984. else if (field.type instanceof flatc.Struct) {
  985. const fieldType = '$root.' + field.type.parent.name + '.' + field.type.name;
  986. this._builder.add('$.' + field.name + ' = ' + fieldType + '.decode(reader, position + ' + field.offset + ');');
  987. }
  988. else {
  989. throw new flatc.Error("Struct '" + typeName + "' may contain only scalar or struct fields.");
  990. }
  991. }
  992. }
  993. this._builder.add('return $;');
  994. this._builder.outdent();
  995. this._builder.add('}');
  996. if (this._text) {
  997. this._builder.add('');
  998. this._builder.add(type.fields.size !== 0 ? 'static decodeText(reader, json) {' : 'static decodeText(/* reader, json */) {');
  999. this._builder.indent();
  1000. this._builder.add('const $ = new ' + typeReference + '();');
  1001. for (const field of type.fields.values()) {
  1002. if (field.repeated) {
  1003. throw new flatc.Error("Struct '" + typeName + "' may contain only scalar or struct fields.");
  1004. }
  1005. else {
  1006. if (field.type instanceof flatc.PrimitiveType) {
  1007. this._builder.add('$.' + field.name + ' = json.' + field.name + ';');
  1008. }
  1009. else if (field.type instanceof flatc.Enum) {
  1010. throw new flatc.Error('Not implemented.');
  1011. }
  1012. else if (field.type instanceof flatc.Struct) {
  1013. const fieldType = '$root.' + field.type.parent.name + '.' + field.type.name;
  1014. this._builder.add('$.' + field.name + ' = ' + fieldType + '.decodeText(reader, json.' + field.name + ');');
  1015. }
  1016. else {
  1017. throw new flatc.Error("Struct '" + typeName + "' may contain only scalar or struct fields.");
  1018. }
  1019. }
  1020. }
  1021. this._builder.add('return $;');
  1022. this._builder.outdent();
  1023. this._builder.add('}');
  1024. }
  1025. this._builder.outdent();
  1026. this._builder.add('};');
  1027. /* eslint-enable indent */
  1028. }
  1029. _buildUnion(type) {
  1030. /* eslint-disable indent */
  1031. this._builder.add('');
  1032. this._builder.add('$root.' + type.parent.name + '.' + type.name + ' = class ' + '{');
  1033. this._builder.indent();
  1034. this._builder.add('');
  1035. this._builder.add(type.values.size !== 0 ? 'static decode(reader, position, type) {' : 'static decode(/* reader, position, type */) {');
  1036. this._builder.indent();
  1037. this._builder.add('switch (type) {');
  1038. this._builder.indent();
  1039. for (const pair of type.values) {
  1040. const valueType = '$root.' + pair[1].parent.name + '.' + pair[1].name;
  1041. this._builder.add('case ' + pair[0] + ': return ' + valueType + '.decode(reader, position);');
  1042. }
  1043. this._builder.add('default: return undefined;');
  1044. this._builder.outdent();
  1045. this._builder.add('}');
  1046. this._builder.outdent();
  1047. this._builder.add('}');
  1048. this._builder.add('');
  1049. this._builder.add(type.values.size !== 0 ? 'static decodeText(reader, json, type) {' : 'static decodeText(/* reader, json, type */) {');
  1050. this._builder.indent();
  1051. this._builder.add('switch (type) {');
  1052. this._builder.indent();
  1053. for (const pair of type.values) {
  1054. const valueType = '$root.' + pair[1].parent.name + '.' + pair[1].name;
  1055. this._builder.add('case \'' + pair[1].name + '\': return ' + valueType + '.decodeText(reader, json);');
  1056. }
  1057. this._builder.add('default: return undefined;');
  1058. this._builder.outdent();
  1059. this._builder.add('}');
  1060. this._builder.outdent();
  1061. this._builder.add('}');
  1062. this._builder.outdent();
  1063. this._builder.add('};');
  1064. /* eslint-enable indent */
  1065. }
  1066. _buildEnum(type) {
  1067. /* eslint-disable indent */
  1068. this._builder.add('');
  1069. this._builder.add('$root.' + type.parent.name + '.' + type.name + ' = {');
  1070. this._builder.indent();
  1071. const keys = Array.from(type.values.keys());
  1072. for (let i = 0; i < keys.length; i++) {
  1073. const key = keys[i];
  1074. this._builder.add(key + ': ' + type.values.get(key) + (i === keys.length - 1 ? '' : ','));
  1075. }
  1076. this._builder.outdent();
  1077. this._builder.add('};');
  1078. /* eslint-enable indent */
  1079. }
  1080. };
  1081. flatc.Generator.StringBuilder = class {
  1082. constructor() {
  1083. this._indentation = '';
  1084. this._lines = [];
  1085. this._newline = true;
  1086. }
  1087. indent() {
  1088. this._indentation += ' ';
  1089. }
  1090. outdent() {
  1091. if (this._indentation.length === 0) {
  1092. throw new flatc.Error('Invalid indentation.');
  1093. }
  1094. this._indentation = this._indentation.substring(0, this._indentation.length - 4);
  1095. }
  1096. add(text, newline) {
  1097. if (this._newline) {
  1098. if (text !== '') {
  1099. this._lines.push(this._indentation);
  1100. }
  1101. }
  1102. this._lines[this._lines.length - 1] = this._lines[this._lines.length - 1] + text + (newline === false ? '' : '\n');
  1103. this._newline = newline === false ? false : true;
  1104. }
  1105. toString() {
  1106. return this._lines.join('');
  1107. }
  1108. };
  1109. flatc.Error = class extends Error {
  1110. constructor(message) {
  1111. super(message);
  1112. this.name = 'FlatBuffers Compiler Error';
  1113. }
  1114. };
  1115. const main = (args) => {
  1116. const options = { verbose: false, root: 'default', out: '', text: false, paths: [], files: [] };
  1117. while (args.length > 0) {
  1118. const arg = args.shift();
  1119. switch (arg) {
  1120. case '--verbose':
  1121. options.verbose = true;
  1122. break;
  1123. case '--out':
  1124. options.out = args.shift();
  1125. break;
  1126. case '--root':
  1127. options.root = args.shift();
  1128. break;
  1129. case '--text':
  1130. options.text = true;
  1131. break;
  1132. case '--path':
  1133. options.paths.push(args.shift());
  1134. break;
  1135. default:
  1136. if (arg.startsWith('-')) {
  1137. throw new flatc.Error("Invalid command line argument '" + arg + "'.");
  1138. }
  1139. options.files.push(arg);
  1140. break;
  1141. }
  1142. }
  1143. try {
  1144. const root = new flatc.Root(options.root, options.paths, options.files);
  1145. const generator = new flatc.Generator(root, options.text);
  1146. if (options.out) {
  1147. fs.writeFileSync(options.out, generator.content, 'utf-8');
  1148. }
  1149. }
  1150. catch (err) {
  1151. if (err instanceof flatc.Error && !options.verbose) {
  1152. process.stderr.write(err.message + '\n');
  1153. }
  1154. else {
  1155. process.stderr.write(err.stack + '\n');
  1156. }
  1157. return 1;
  1158. }
  1159. return 0;
  1160. };
  1161. if (typeof process === 'object' && Array.isArray(process.argv) &&
  1162. process.argv.length > 1 && process.argv[1] === __filename) {
  1163. const args = process.argv.slice(2);
  1164. const code = main(args);
  1165. process.exit(code);
  1166. }
  1167. if (typeof module !== 'undefined' && typeof module.exports === 'object') {
  1168. module.exports.Root = flatc.Root;
  1169. module.exports.Namespace = flatc.Namespace;
  1170. module.exports.Type = flatc.Type;
  1171. module.exports.Enum = flatc.Enum;
  1172. }