flatc.js 48 KB

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