| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391 |
- import * as fs from 'fs/promises';
- import * as path from 'path';
- const flatc = {};
- flatc.Object = class {
- constructor(parent, name) {
- this.parent = parent;
- this.name = name;
- this.metadata = new Map();
- }
- get root() {
- return this.parent.root;
- }
- resolve() {
- if (!this.resolved) {
- for (const key of this.metadata.keys()) {
- switch (key) {
- case 'bit_flags':
- case 'deprecated':
- case 'force_align':
- case 'key':
- case 'required':
- break;
- default:
- throw new flatc.Error(`Unsupported attribute '${key}'.`);
- }
- }
- this.resolved = true;
- }
- }
- find(name, type) {
- return this.parent ? this.parent.find(name, type) : undefined;
- }
- };
- flatc.Namespace = class extends flatc.Object {
- constructor(parent, name) {
- super(parent, name);
- this.children = new Map();
- this.root_type = new Map();
- }
- resolve() {
- if (!this.resolved) {
- for (const child of this.children.values()) {
- child.resolve();
- }
- if (this.root_type.size > 0) {
- for (const [name, value] of this.root_type) {
- const type = this.find(name, flatc.Type);
- if (!type) {
- throw new flatc.Error(`Failed to resolve root type '${name}'.`);
- }
- if (value) {
- type.file_identifier = value;
- }
- this.root.root_type.add(type);
- }
- this.root_type.clear();
- }
- super.resolve();
- }
- }
- find(name, type) {
- if (type === flatc.Type) {
- const parts = name.split('.');
- const typeName = parts.pop();
- const namespaceName = parts.join('.');
- if (namespaceName === '') {
- if (this.children.has(typeName)) {
- return this.children.get(typeName);
- }
- }
- const namespace = this.parent.find(namespaceName, flatc.Namespace);
- if (namespace) {
- if (namespace.children.has(typeName)) {
- return namespace.children.get(typeName);
- }
- }
- const parents = this.name.split('.');
- while (parents.length > 1) {
- parents.pop();
- const parentNamespaceName = parents.join('.') + (namespaceName ? `.${namespaceName}` : '');
- const namespace = this.parent.find(parentNamespaceName, flatc.Namespace);
- if (namespace) {
- if (namespace.children.has(typeName)) {
- return namespace.children.get(typeName);
- }
- }
- }
- }
- return super.find(name, type);
- }
- };
- flatc.Type = class extends flatc.Object {
- constructor(parent, name) {
- super(parent, name);
- if (parent instanceof flatc.Namespace) {
- if (parent.children.has(name)) {
- throw new flatc.Error(`Duplicate identifier '${name}'.`);
- }
- parent.children.set(name, this);
- }
- }
- };
- flatc.Enum = class extends flatc.Type {
- constructor(parent, name, base) {
- super(parent, name);
- this.base = base;
- this.values = new Map();
- }
- resolve() {
- if (!this.resolved) {
- if (this.base instanceof flatc.TypeReference) {
- this.base = this.base.resolve(this);
- this.defaultValue = this.base.defaultValue;
- }
- let index = 0;
- for (const key of this.values.keys()) {
- if (this.values.get(key) === undefined) {
- this.values.set(key, index);
- }
- index = this.values.get(key) + 1;
- }
- this.keys = new Map(Array.from(this.values).map(([key, value]) => [value, key]));
- super.resolve();
- }
- }
- };
- flatc.Alias = class extends flatc.Type {
- constructor(parent, name, type) {
- super(parent, name);
- this.type = type;
- }
- };
- flatc.Union = class extends flatc.Type {
- constructor(parent, name) {
- super(parent, name);
- this.values = [];
- }
- resolve() {
- if (!this.resolved) {
- let index = 1;
- for (const value of this.values) {
- value.index = value.index === undefined ? index : value.index;
- index = value.index + 1;
- const name = this.parent.find(value.name, flatc.Type);
- const type = value.type ? this.parent.find(value.type, flatc.Type) : null;
- value.type = name;
- if (!name && type) {
- value.type = new flatc.Alias(this.parent, value.name, type);
- type.aliases.add(value.type);
- this.root.aliases.push(value);
- }
- }
- super.resolve();
- }
- }
- };
- flatc.Table = class extends flatc.Type {
- constructor(parent, name) {
- super(parent, name);
- this.fields = new Map();
- this.aliases = new Set();
- }
- resolve() {
- if (!this.resolved) {
- let offset = 4;
- for (const field of this.fields.values()) {
- field.resolve();
- field.offset = offset;
- offset += (field.type instanceof flatc.Union) ? 4 : 2;
- }
- super.resolve();
- }
- }
- };
- flatc.Struct = class extends flatc.Type {
- constructor(parent, name) {
- super(parent, name);
- this.fields = new Map();
- this.size = -1;
- }
- resolve() {
- if (!this.resolved) {
- let offset = 0;
- for (const field of this.fields.values()) {
- field.resolve();
- const fieldType = field.type instanceof flatc.Enum ? field.type.base : field.type;
- if (field.repeated) {
- if (field.length === undefined) {
- const name = `${this.parent.name}.${this.name}`;
- throw new flatc.Error(`Struct '${name}' may contain only scalar or struct fields.`);
- }
- const size = fieldType.size;
- field.offset = (offset % size) === 0 ? offset : (Math.floor(offset / size) + 1) * size;
- offset = field.offset + (field.length * size);
- } else if (fieldType instanceof flatc.PrimitiveType && field.type !== 'string') {
- const size = fieldType.size;
- field.offset = (offset % size) === 0 ? offset : (Math.floor(offset / size) + 1) * size;
- offset = field.offset + size;
- } else if (field.type instanceof flatc.Struct) {
- field.type.resolve();
- const align = 8;
- field.offset = (offset % align) === 0 ? offset : (Math.floor(offset / align) + 1) * align;
- offset += field.type.size;
- } else {
- throw new flatc.Error('Structs may contain only scalar or struct fields.');
- }
- }
- this.size = offset;
- super.resolve();
- }
- }
- };
- flatc.Field = class extends flatc.Object {
- constructor(parent, name, type, defaultValue) {
- super(parent, name);
- this.type = type;
- this.defaultValue = defaultValue;
- }
- resolve() {
- if (!this.resolved) {
- if (this.type instanceof flatc.TypeReference) {
- if (this.type.repeated) {
- this.repeated = true;
- this.length = this.type.length;
- }
- this.type = this.type.resolve(this);
- if (this.defaultValue === undefined) {
- let type = this.type instanceof flatc.Enum ? this.type.base : this.type;
- if (type instanceof flatc.TypeReference) {
- type = type.resolve(this);
- }
- if (type instanceof flatc.PrimitiveType) {
- this.defaultValue = type.defaultValue;
- }
- } else if (this.type instanceof flatc.Enum) {
- this.type.resolve();
- if (this.type.values.has(this.defaultValue)) {
- this.defaultValue = this.type.values.get(this.defaultValue);
- } else if (!new Set(this.type.values.values()).has(this.defaultValue)) {
- throw new flatc.Error(`Unsupported enum value '${this.defaultValue}'.`);
- }
- }
- }
- super.resolve();
- }
- }
- };
- flatc.PrimitiveType = class extends flatc.Type {
- constructor(name, defaultValue, size) {
- super(null, name);
- this.defaultValue = defaultValue;
- this.size = size;
- }
- static get(name) {
- if (!this._map) {
- this._map = new Map();
- const register = (names, defaultValue, size) => {
- const type = new flatc.PrimitiveType(names[0], defaultValue, size);
- for (const name of names) {
- this._map.set(name, type);
- }
- };
- register(['bool'], false, 1);
- register(['int8', 'byte'], 0, 1);
- register(['uint8', 'ubyte'], 0, 1);
- register(['int16', 'short'], 0, 2);
- register(['uint16', 'ushort'], 0, 2);
- register(['int32', 'int'], 0, 4);
- register(['uint32', 'uint'], 0, 4);
- register(['int64', 'long'], 0n, 8);
- register(['uint64', 'ulong'], 0n, 8);
- register(['float32', 'float'], 0.0, 4);
- register(['float64', 'double'], 0, 4);
- register(['string'], null, undefined);
- }
- return this._map.get(name);
- }
- };
- flatc.TypeReference = class {
- constructor(name, repeated, length) {
- this.name = name;
- this.repeated = repeated;
- this.length = length;
- }
- resolve(context) {
- const primitiveType = flatc.PrimitiveType.get(this.name);
- if (primitiveType) {
- return primitiveType;
- }
- const type = context.parent.find(this.name, flatc.Type);
- if (type) {
- return type;
- }
- throw new flatc.Error(`Falied to resolve type '${this.name}'.`);
- }
- };
- flatc.Parser = class {
- constructor(text, file, root) {
- // https://google.github.io/flatbuffers/flatbuffers_grammar.html
- this._tokenizer = new flatc.Tokenizer(text, file);
- this._root = root;
- this._context = root.defineNamespace('');
- }
- include() {
- const includes = [];
- while (!this._tokenizer.match('eof') && this._tokenizer.accept('id', 'include')) {
- includes.push(this._tokenizer.string());
- this._tokenizer.expect(';');
- }
- return includes;
- }
- parse() {
- const attributes = [];
- while (!this._tokenizer.match('eof')) {
- if (this._tokenizer.accept('id', 'namespace')) {
- let name = this._tokenizer.identifier();
- while (this._tokenizer.accept('.')) {
- name += `.${this._tokenizer.identifier()}`;
- }
- this._tokenizer.expect(';');
- this._context = this._root.defineNamespace(name);
- continue;
- }
- if (this._tokenizer.accept('id', 'table')) {
- const name = this._tokenizer.identifier();
- const table = new flatc.Table(this._context, name);
- this._parseMetadata(table.metadata);
- this._tokenizer.expect('{');
- while (!this._tokenizer.accept('}')) {
- const field = this._parseField(table);
- table.fields.set(field.name, field);
- this._tokenizer.expect(';');
- }
- continue;
- }
- if (this._tokenizer.accept('id', 'struct')) {
- const name = this._tokenizer.identifier();
- const struct = new flatc.Struct(this._context, name);
- this._parseMetadata(struct.metadata);
- this._tokenizer.expect('{');
- while (!this._tokenizer.accept('}')) {
- const field = this._parseField(struct);
- struct.fields.set(field.name, field);
- this._tokenizer.expect(';');
- }
- continue;
- }
- if (this._tokenizer.accept('id', 'enum')) {
- const name = this._tokenizer.identifier();
- this._tokenizer.expect(':');
- const base = this._parseTypeReference();
- if (base.repeated) {
- throw new flatc.Error(`Underlying enum type must be integral ${this._tokenizer.location()}`);
- }
- const type = new flatc.Enum(this._context, name, base);
- this._parseMetadata(type.metadata);
- this._tokenizer.expect('{');
- while (!this._tokenizer.accept('}')) {
- const key = this._tokenizer.identifier();
- const value = this._tokenizer.accept('=') ? this._tokenizer.integer() : undefined;
- type.values.set(key, value);
- this._parseMetadata(new Map());
- if (this._tokenizer.accept(',')) {
- continue;
- }
- }
- continue;
- }
- if (this._tokenizer.accept('id', 'union')) {
- const name = this._tokenizer.identifier();
- const union = new flatc.Union(this._context, name);
- this._parseMetadata(union.metadata);
- this._tokenizer.expect('{');
- while (!this._tokenizer.accept('}')) {
- const name = this._tokenizer.identifier();
- const type = this._tokenizer.accept(':') ? this._tokenizer.identifier() : null;
- const index = this._tokenizer.accept('=') ? this._tokenizer.integer() : undefined;
- union.values.push({ name, type, index });
- this._parseMetadata(new Map());
- if (this._tokenizer.accept(',')) {
- continue;
- }
- }
- continue;
- }
- if (this._tokenizer.accept('id', 'rpc_service')) {
- throw new flatc.Error(`Unsupported keyword 'rpc_service' ${this._tokenizer.location()}`);
- }
- if (this._tokenizer.accept('id', 'root_type')) {
- const root_type = this._tokenizer.identifier();
- this._root_type = this._root_type || root_type;
- this._tokenizer.accept(';');
- continue;
- }
- if (this._tokenizer.accept('id', 'file_extension')) {
- const value = this._tokenizer.string();
- this._file_extension = value;
- this._tokenizer.accept(';');
- continue;
- }
- if (this._tokenizer.accept('id', 'file_identifier')) {
- const value = this._tokenizer.string();
- if (value.length !== 4) {
- throw new flatc.Error(`'file_identifier' must be exactly 4 characters ${this._tokenizer.location()}`);
- }
- this._file_identifier = value;
- this._tokenizer.accept(';');
- continue;
- }
- if (this._tokenizer.accept('id', 'attribute')) {
- const token = this._tokenizer.read();
- switch (token.type) {
- case 'string':
- attributes.push(token.value);
- break;
- case 'id':
- attributes.push(token.token);
- break;
- default:
- throw new flatc.Error(`Unexpected attribute token '${token.token}' ${this._tokenizer.location()}`);
- }
- this._tokenizer.expect(';');
- continue;
- }
- if (this._tokenizer.accept('{')) {
- throw new flatc.Error(`Unsupported object ${this._tokenizer.location()}`);
- }
- throw new flatc.Error(`Unexpected token '${this._tokenizer.peek().token}' ${this._tokenizer.location()}`);
- }
- if (this._root_type) {
- this._context.root_type.set(this._root_type, this._file_identifier);
- }
- }
- _parseTypeReference() {
- const token = this._tokenizer.read();
- if (token.type === 'id') {
- return new flatc.TypeReference(token.token, false);
- }
- if (token.type === '[') {
- const identifier = this._tokenizer.read();
- if (identifier.type === 'id') {
- if (this._tokenizer.accept(':')) {
- const length = this._parseScalar(); // array length
- this._tokenizer.expect(']');
- return new flatc.TypeReference(identifier.token, true, length);
- }
- this._tokenizer.expect(']');
- return new flatc.TypeReference(identifier.token, true);
- }
- }
- throw new flatc.Error(`Expected type instead of '${token.token}' ${this._tokenizer.location()}`);
- }
- _parseField(parent) {
- const name = this._tokenizer.identifier();
- this._tokenizer.expect(':');
- const type = this._parseTypeReference();
- const defaultValue = this._tokenizer.accept('=') ? this._parseScalar() : undefined;
- const field = new flatc.Field(parent, name, type, defaultValue);
- this._parseMetadata(field.metadata);
- return field;
- }
- _parseMetadata(metadata) {
- if (this._tokenizer.accept('(')) {
- while (!this._tokenizer.accept(')')) {
- const key = this._tokenizer.identifier();
- const value = this._tokenizer.accept(':') ? this._parseSingleValue() : undefined;
- metadata.set(key, value);
- if (this._tokenizer.accept(',')) {
- continue;
- }
- }
- }
- }
- _parseScalar() {
- const token = this._tokenizer.read();
- switch (token.type) {
- case 'boolean':
- case 'integer':
- case 'float':
- return token.value;
- case 'id':
- return token.token;
- default:
- throw new flatc.Error(`Expected scalar instead of '${token.token}'${this._tokenizer.location()}`);
- }
- }
- _parseSingleValue() {
- const token = this._tokenizer.read();
- switch (token.type) {
- case 'string':
- case 'boolean':
- case 'integer':
- case 'float':
- return token.value;
- default:
- throw new flatc.Error(`Expected single value instead of '${token.token}'${this._token.location()}`);
- }
- }
- };
- flatc.Tokenizer = class {
- constructor(text, file) {
- this._text = text;
- this._file = file;
- this._position = 0;
- this._lineStart = 0;
- this._line = 0;
- this._token = { type: '', value: '' };
- }
- peek() {
- if (!this._cache) {
- this._token = this._tokenize();
- this._cache = true;
- }
- return this._token;
- }
- read() {
- if (!this._cache) {
- this._token = this._tokenize();
- }
- const next = this._position + this._token.token.length;
- while (this._position < next) {
- if (flatc.Tokenizer._isNewline(this._get(this._position))) {
- this._position = this._newLine(this._position);
- this._lineStart = this._position;
- this._line++;
- } else {
- this._position++;
- }
- }
- this._cache = false;
- return this._token;
- }
- match(type, value) {
- const token = this.peek();
- if (token.type === type && (!value || token.token === value)) {
- return true;
- }
- return false;
- }
- accept(type, value) {
- const token = this.peek();
- if (token.type === type && (!value || token.token === value)) {
- this.read();
- return true;
- }
- return false;
- }
- expect(type, value) {
- const token = this.peek();
- if (token.type !== type) {
- throw new flatc.Error(`Unexpected '${token.token}' instead of '${type}'${this.location()}`);
- }
- if (value && token.token !== value) {
- throw new flatc.Error(`Unexpected '${token.token}' instead of '${value}'${this.location()}`);
- }
- this.read();
- }
- string() {
- const token = this.read();
- if (token.type === 'string') {
- return token.value;
- }
- throw new flatc.Error(`Expected string instead of '${token.token}' ${this.location()}`);
- }
- identifier() {
- const token = this.read();
- if (token.type === 'id') {
- return token.token;
- }
- throw new flatc.Error(`Expected identifier instead of '${token.token}' ${this.location()}`);
- }
- integer() {
- const token = this.read();
- if (token.type === 'integer') {
- return token.value;
- }
- throw new flatc.Error(`Expected integer instead of '${token.token}' ${this.location()}`);
- }
- location() {
- const line = this._line + 1;
- const column = this._position - this._lineStart + 1;
- return `at ${this._file}:${line}:${column}`;
- }
- _tokenize() {
- if (this._token.type !== '\n') {
- this._skipWhitespace();
- }
- if (this._position >= this._text.length) {
- return { type: 'eof', value: '' };
- }
- const content = this._text.slice(this._position);
- const boolean_constant = content.match(/^(true|false)/);
- if (boolean_constant) {
- const [content] = boolean_constant;
- return { type: 'boolean', token: content, value: content === 'true' };
- }
- const identifier = content.match(/^[a-zA-Z_][a-zA-Z0-9_.]*/);
- if (identifier) {
- return { type: 'id', token: identifier[0] };
- }
- const string_constant = content.match(/^".*?"/) || content.match(/^'.*?'/);
- if (string_constant) {
- const [content] = string_constant;
- return { type: 'string', token: content, value: content.substring(1, content.length - 1) };
- }
- const dec_float_constant = content.match(/^[-+]?(([.][0-9]+)|([0-9]+[.][0-9]*)|([0-9]+))([eE][-+]?[0-9]+)?/);
- if (dec_float_constant) {
- const [content] = dec_float_constant;
- if (content.indexOf('.') !== -1 || content.indexOf('e') !== -1) {
- return { type: 'float', token: content, value: parseFloat(content) };
- }
- }
- 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]+)/);
- if (hex_float_constant) {
- throw new flatc.Error('Unsupported hexadecimal constant.');
- }
- const dec_integer_constant = content.match(/^[-+]?[0-9]+/);
- if (dec_integer_constant) {
- const [content] = dec_integer_constant;
- return { type: 'integer', token: content, value: parseInt(content, 10) };
- }
- const hex_integer_constant = content.match(/^[-+]?0[xX][0-9a-fA-F]+/);
- if (hex_integer_constant) {
- throw new flatc.Error('Unsupported hexadecimal constant.');
- }
- const c = this._get(this._position);
- switch (c) {
- case ';':
- case ':':
- case '{':
- case '}':
- case '[':
- case ']':
- case '(':
- case ')':
- case '=':
- case ',':
- return { type: c, token: c };
- default:
- throw new flatc.Error(`Unsupported character '${c}' ${this.location()}`);
- }
- }
- _get(position) {
- return position >= this._text.length ? '\0' : this._text[position];
- }
- _skipLine() {
- while (this._position < this._text.length) {
- if (flatc.Tokenizer._isNewline(this._get(this._position))) {
- break;
- }
- this._position++;
- }
- }
- _skipWhitespace() {
- while (this._position < this._text.length) {
- const c = this._get(this._position);
- if (flatc.Tokenizer._isSpace(c)) {
- this._position++;
- continue;
- }
- if (flatc.Tokenizer._isNewline(c)) {
- // Implicit Line Continuation
- this._position = this._newLine(this._position);
- this._lineStart = this._position;
- this._line++;
- continue;
- }
- if (c === '/') {
- const c1 = this._get(this._position + 1);
- if (c1 === '/') {
- this._skipLine();
- continue;
- }
- if (c1 === '*') {
- this._position += 2;
- while (this._get(this._position) !== '*' || this._get(this._position + 1) !== '/') {
- this._position++;
- if ((this._position + 2) > this._text.length) {
- throw new flatc.Error('Unexpected end of file in comment.');
- }
- }
- this._position += 2;
- continue;
- }
- }
- break;
- }
- }
- static _isSpace(c) {
- switch (c) {
- case ' ':
- case '\t':
- case '\v': // 11
- case '\f': // 12
- case '\xA0': // 160
- return true;
- default:
- return false;
- }
- }
- static _isNewline(c) {
- switch (c) {
- case '\n':
- case '\r':
- case '\u2028': // 8232
- case '\u2029': // 8233
- return true;
- default:
- return false;
- }
- }
- _newLine(position) {
- if ((this._get(position) === '\n' && this._get(position + 1) === '\r') ||
- (this._get(position) === '\r' && this._get(position + 1) === '\n')) {
- return position + 2;
- }
- return position + 1;
- }
- };
- flatc.Root = class extends flatc.Object {
- constructor(root) {
- super(null, root);
- this._namespaces = new Map();
- this._files = new Set();
- this.root_type = new Set();
- }
- async load(paths, files) {
- for (const file of files) {
- /* eslint-disable no-await-in-loop */
- await this._parseFile(paths, file);
- /* eslint-enable no-await-in-loop */
- }
- this.resolve();
- }
- resolve() {
- if (!this.resolved) {
- this.aliases = [];
- for (const namespace of this._namespaces.values()) {
- namespace.resolve();
- }
- for (const value of this.aliases) {
- if (value.type instanceof flatc.Alias && value.type.type.aliases.size <= 1) {
- value.type.type.aliases.delete(value.type);
- value.type.parent.children.delete(value.type.name);
- value.type = value.type.type;
- }
- }
- delete this.aliases;
- super.resolve();
- }
- }
- get root() {
- return this;
- }
- get namespaces() {
- return this._namespaces;
- }
- set(name, value) {
- this.metadata.set(name, value);
- }
- get(name) {
- return this.metadata.get(name);
- }
- defineNamespace(name) {
- if (!this._namespaces.has(name)) {
- this._namespaces.set(name, new flatc.Namespace(this, name));
- }
- return this._namespaces.get(name);
- }
- find(name, type) {
- if (type === flatc.Namespace) {
- if (this._namespaces.has(name)) {
- return this._namespaces.get(name);
- }
- }
- return super.find(name, type);
- }
- async _parseFile(paths, file) {
- if (!this._files.has(file)) {
- this._files.add(file);
- const content = await fs.readFile(file, 'utf-8');
- const parser = new flatc.Parser(content, file, this);
- const includes = parser.include();
- for (const include of includes) {
- /* eslint-disable no-await-in-loop */
- const includeFile = await this._resolve(paths, file, include);
- /* eslint-enable no-await-in-loop */
- if (includeFile) {
- /* eslint-disable no-await-in-loop */
- await this._parseFile(paths, includeFile);
- /* eslint-enable no-await-in-loop */
- continue;
- }
- throw new flatc.Error(`Include '${include}' not found.`);
- }
- parser.parse();
- }
- }
- async _resolve(paths, origin, target) {
- const access = async (path) => {
- try {
- await fs.access(path);
- return true;
- } catch {
- return false;
- }
- };
- const file = path.join(path.dirname(origin), target);
- const exists = await access(file);
- if (exists) {
- return file;
- }
- for (const current of paths) {
- const file = path.join(current, target);
- /* eslint-disable no-await-in-loop */
- const exists = await access(file);
- /* eslint-enable no-await-in-loop */
- if (exists) {
- return file;
- }
- }
- return null;
- }
- };
- flatc.Generator = class {
- constructor(root, text) {
- this._root = root;
- this._text = text;
- this._builder = new flatc.Generator.StringBuilder();
- const namespaces = Array.from(this._root.namespaces.values()).filter((namespace) => namespace.name !== '').map((namespace) => namespace.name);
- const exports = new Set (namespaces.map((namespace) => namespace.split('.')[0]));
- for (const value of exports) {
- this._builder.add('');
- this._builder.add(`export const ${value} = {};`);
- }
- for (const namespace of this._root.namespaces.values()) {
- this._buildNamespace(namespace);
- }
- this._content = this._builder.toString();
- }
- get content() {
- return this._content;
- }
- _buildNamespace(namespace) {
- if (namespace.name !== '') {
- const parts = namespace.name.split('.');
- for (let i = 2; i <= parts.length; i++) {
- const name = `${parts.slice(0, i).join('.')}`;
- this._builder.add('');
- this._builder.add(`${name} = ${name} || {};`);
- }
- }
- for (const child of namespace.children.values()) {
- if (child instanceof flatc.Table) {
- this._buildTable(child);
- } else if (child instanceof flatc.Struct) {
- this._buildStruct(child);
- } else if (child instanceof flatc.Union) {
- this._buildUnion(child);
- } else if (child instanceof flatc.Alias) {
- this._buildAlias(child);
- } else if (child instanceof flatc.Enum) {
- this._buildEnum(child);
- } else {
- throw new flatc.Error(`Unsupported type '${child.name}'.`);
- }
- }
- }
- _buildTable(type) {
- const typeName = `${type.parent.name}.${type.name}`;
- const typeReference = `${typeName}`;
- /* eslint-disable indent */
- this._builder.add('');
- this._builder.add(`${typeReference} = class ${type.name} {`);
- this._builder.indent();
- if (this._root.root_type.has(type)) {
- const file_identifier = type.file_identifier;
- if (file_identifier) {
- this._builder.add('');
- this._builder.add('static identifier(reader) {');
- this._builder.indent();
- this._builder.add(`return reader.identifier === '${file_identifier}';`);
- this._builder.outdent();
- this._builder.add('}');
- }
- this._builder.add('');
- this._builder.add('static create(reader) {');
- this._builder.indent();
- this._builder.add(`return ${typeReference}.decode(reader, reader.root);`);
- this._builder.outdent();
- this._builder.add('}');
- if (this._text) {
- this._builder.add('');
- this._builder.add('static createText(reader) {');
- this._builder.indent();
- this._builder.add(`return ${typeReference}.decodeText(reader, reader.root);`);
- this._builder.outdent();
- this._builder.add('}');
- }
- }
- this._builder.add('');
- if (type.aliases.size > 0) {
- this._builder.add('static decode(reader, position, $) {');
- } else if (type.fields.size === 0) {
- this._builder.add('static decode(/* reader, position */) {');
- } else {
- this._builder.add('static decode(reader, position) {');
- }
- this._builder.indent();
- this._builder.add(type.aliases.size > 0 ? `$ = $ || new ${typeReference}();` : `const $ = new ${typeReference}();`);
- for (const field of type.fields.values()) {
- const fieldType = field.type instanceof flatc.Enum ? field.type.base : field.type;
- if (field.repeated) {
- if (fieldType instanceof flatc.PrimitiveType) {
- switch (field.type.name) {
- case 'int64': {
- this._builder.add(`$.${field.name} = reader.int64s_(position, ${field.offset});`);
- break;
- }
- case 'uint64': {
- this._builder.add(`$.${field.name} = reader.uint64s_(position, ${field.offset});`);
- break;
- }
- case 'string': {
- this._builder.add(`$.${field.name} = reader.strings_(position, ${field.offset});`);
- break;
- }
- case 'bool': {
- this._builder.add(`$.${field.name} = reader.bools_(position, ${field.offset});`);
- break;
- }
- default: {
- const arrayType = `${fieldType.name[0].toUpperCase() + fieldType.name.substring(1)}Array`;
- this._builder.add(`$.${field.name} = reader.array(position, ${field.offset}, ${arrayType});`);
- break;
- }
- }
- } else if (fieldType instanceof flatc.Union) {
- const unionType = `${field.type.parent.name}.${field.type.name}`;
- this._builder.add(`$.${field.name} = reader.unions(position, ${field.offset}, ${unionType});`);
- } else if (fieldType instanceof flatc.Struct) {
- const fieldType = `${field.type.parent.name}.${field.type.name}`;
- this._builder.add(`$.${field.name} = reader.structs(position, ${field.offset}, ${fieldType});`);
- } else {
- const fieldType = `${field.type.parent.name}.${field.type.name}`;
- this._builder.add(`$.${field.name} = reader.tables(position, ${field.offset}, ${fieldType});`);
- }
- } else if (fieldType instanceof flatc.PrimitiveType) {
- const n = fieldType.name === 'uint64' || fieldType.name === 'int64' ? 'n' : '';
- this._builder.add(`$.${field.name} = reader.${fieldType.name}_(position, ${field.offset}, ${field.defaultValue}${n});`);
- } else if (fieldType instanceof flatc.Union) {
- const unionType = `${field.type.parent.name}.${field.type.name}`;
- this._builder.add(`$.${field.name} = reader.union(position, ${field.offset}, ${unionType});`);
- } else if (fieldType instanceof flatc.Struct) {
- const fieldType = `${field.type.parent.name}.${field.type.name}`;
- this._builder.add(`$.${field.name} = reader.struct(position, ${field.offset}, ${fieldType});`);
- } else {
- const fieldType = `${field.type.parent.name}.${field.type.name}`;
- this._builder.add(`$.${field.name} = reader.table(position, ${field.offset}, ${fieldType});`);
- }
- }
- this._builder.add('return $;');
- this._builder.outdent();
- this._builder.add('}');
- if (this._text) {
- this._builder.add('');
- if (type.aliases.size > 0) {
- this._builder.add('static decodeText(reader, json, $) {');
- } else if (type.fields.size === 0) {
- this._builder.add('static decodeText(/* reader, json */) {');
- } else {
- this._builder.add('static decodeText(reader, json) {');
- }
- this._builder.indent();
- this._builder.add(type.aliases.size > 0 ? `$ = $ || new ${typeReference}();` : `const $ = new ${typeReference}();`);
- for (const field of type.fields.values()) {
- if (field.repeated) {
- if (field.type instanceof flatc.PrimitiveType) {
- switch (field.type.name) {
- case 'int64':
- case 'uint64':
- case 'string':
- case 'bool': {
- this._builder.add(`$.${field.name} = reader.array(json.${field.name});`);
- break;
- }
- default: {
- const type = `${field.type.name[0].toUpperCase() + field.type.name.substring(1)}Array`;
- this._builder.add(`$.${field.name} = reader.array(json.${field.name}, ${type});`);
- break;
- }
- }
- } else if (field.type instanceof flatc.Union) {
- throw new flatc.Error('Not implemented.');
- } else if (field.type instanceof flatc.Struct) {
- const fieldType = `${field.type.parent.name}.${field.type.name}`;
- this._builder.add(`$.${field.name} = ${fieldType}.decode(reader, position + ${field.offset});`);
- } else {
- const fieldType = `${field.type.parent.name}.${field.type.name}`;
- this._builder.add(`$.${field.name} = reader.objects(json.${field.name}, ${fieldType});`);
- }
- } else if (field.type instanceof flatc.PrimitiveType) {
- switch (field.type.name) {
- case 'int64': {
- this._builder.add(`$.${field.name} = reader.int64(json.${field.name}, ${field.defaultValue}n);`);
- break;
- }
- case 'uint64': {
- this._builder.add(`$.${field.name} = reader.uint64(json.${field.name}, ${field.defaultValue}n);`);
- break;
- }
- default: {
- this._builder.add(`$.${field.name} = reader.value(json.${field.name}, ${field.defaultValue});`);
- break;
- }
- }
- } else if (field.type instanceof flatc.Enum) {
- const enumName = `${field.type.parent.name}.${field.type.name}`;
- this._builder.add(`$.${field.name} = ${enumName}[json.${field.name}];`);
- } else if (field.type instanceof flatc.Union) {
- const unionType = `${field.type.parent.name}.${field.type.name}`;
- this._builder.add(`$.${field.name} = ${unionType}.decodeText(reader, json.${field.name}, json.${field.name}_type);`);
- } else { // struct | table
- const fieldType = `${field.type.parent.name}.${field.type.name}`;
- this._builder.add(`$.${field.name} = reader.object(json.${field.name}, ${fieldType});`);
- }
- }
- this._builder.add('return $;');
- this._builder.outdent();
- this._builder.add('}');
- }
- this._builder.outdent();
- this._builder.add('};');
- /* eslint-enable indent */
- }
- _buildStruct(type) {
- const typeName = `${type.parent.name}.${type.name}`;
- const typeReference = `${typeName}`;
- /* eslint-disable indent */
- this._builder.add('');
- this._builder.add(`${typeReference} = class ${type.name} {`);
- this._builder.indent();
- this._builder.add('');
- this._builder.add(type.fields.size === 0 ? 'static decode(/* reader, position */) {' : 'static decode(reader, position) {');
- this._builder.indent();
- this._builder.add(`const $ = new ${typeReference}();`);
- for (const field of type.fields.values()) {
- if (field.repeated) {
- if (field.length === undefined) {
- throw new flatc.Error(`Struct '${typeName}' may contain only scalar or struct fields.`);
- }
- this._builder.add(`$.${field.name} = undefined; // not implemented`);
- } else if (field.type instanceof flatc.PrimitiveType) {
- this._builder.add(`$.${field.name} = reader.${field.type.name}(position + ${field.offset});`);
- } else if (field.type instanceof flatc.Enum) {
- this._builder.add(`$.${field.name} = reader.${field.type.base.name}(position + ${field.offset});`);
- } else if (field.type instanceof flatc.Struct) {
- const fieldType = `${field.type.parent.name}.${field.type.name}`;
- this._builder.add(`$.${field.name} = ${fieldType}.decode(reader, position + ${field.offset});`);
- } else {
- throw new flatc.Error(`Struct '${typeName}' may contain only scalar or struct fields.`);
- }
- }
- this._builder.add('return $;');
- this._builder.outdent();
- this._builder.add('}');
- if (this._text) {
- this._builder.add('');
- this._builder.add(type.fields.size === 0 ? 'static decodeText(/* reader, json */) {' : 'static decodeText(reader, json) {');
- this._builder.indent();
- this._builder.add(`const $ = new ${typeReference}();`);
- for (const field of type.fields.values()) {
- if (field.repeated) {
- throw new flatc.Error(`Struct '${typeName}' may contain only scalar or struct fields.`);
- } else if (field.type instanceof flatc.PrimitiveType) {
- this._builder.add(`$.${field.name} = json.${field.name};`);
- } else if (field.type instanceof flatc.Enum) {
- throw new flatc.Error('Not implemented.');
- } else if (field.type instanceof flatc.Struct) {
- const fieldType = `${field.type.parent.name}.${field.type.name}`;
- this._builder.add(`$.${field.name} = ${fieldType}.decodeText(reader, json.${field.name});`);
- } else {
- throw new flatc.Error(`Struct '${typeName}' may contain only scalar or struct fields.`);
- }
- }
- this._builder.add('return $;');
- this._builder.outdent();
- this._builder.add('}');
- }
- this._builder.outdent();
- this._builder.add('};');
- /* eslint-enable indent */
- }
- _buildAlias(type) {
- const typeName = `${type.parent.name}.${type.name}`;
- const typeReference = `${typeName}`;
- /* eslint-disable indent */
- this._builder.add('');
- this._builder.add(`${typeReference} = class ${type.name} {`);
- this._builder.indent();
- this._builder.add('');
- this._builder.add('static decode(reader, position) {');
- this._builder.indent();
- this._builder.add(`const $ = new ${typeReference}();`);
- this._builder.add(`${type.type.parent.name}.${type.type.name}.decode(reader, position, $);`);
- this._builder.add('return $;');
- this._builder.outdent();
- this._builder.add('}');
- if (this._text) {
- this._builder.add('');
- this._builder.add('static decodeText(reader, json) {');
- this._builder.indent();
- this._builder.add(`const $ = new ${typeReference}();`);
- this._builder.add(`${type.type.parent.name}.${type.type.name}.decode(reader, json, $);`);
- this._builder.add('return $;');
- this._builder.outdent();
- this._builder.add('}');
- }
- this._builder.outdent();
- this._builder.add('};');
- /* eslint-enable indent */
- }
- _buildUnion(type) {
- /* eslint-disable indent */
- this._builder.add('');
- this._builder.add(`${type.parent.name}.${type.name} = class {`);
- this._builder.indent();
- this._builder.add('');
- this._builder.add(type.values.length === 0 ? 'static decode(/* reader, position, type */) {' : 'static decode(reader, position, type) {');
- this._builder.indent();
- this._builder.add('switch (type) {');
- this._builder.indent();
- for (const value of type.values) {
- const valueType = `${value.type.parent.name}.${value.type.name}`;
- this._builder.add(`case ${value.index}: return ${valueType}.decode(reader, position);`);
- }
- this._builder.add('default: return undefined;');
- this._builder.outdent();
- this._builder.add('}');
- this._builder.outdent();
- this._builder.add('}');
- if (this._text) {
- this._builder.add('');
- this._builder.add(type.values.length === 0 ? 'static decodeText(/* reader, json, type */) {' : 'static decodeText(reader, json, type) {');
- this._builder.indent();
- this._builder.add('switch (type) {');
- this._builder.indent();
- for (const value of type.values) {
- const valueType = `${value.type.parent.name}.${value.type.name}`;
- this._builder.add(`case '${value.name}': return ${valueType}.decodeText(reader, json);`);
- }
- this._builder.add('default: return undefined;');
- this._builder.outdent();
- this._builder.add('}');
- this._builder.outdent();
- this._builder.add('}');
- }
- this._builder.outdent();
- this._builder.add('};');
- /* eslint-enable indent */
- }
- _buildEnum(type) {
- /* eslint-disable indent */
- this._builder.add('');
- this._builder.add(`${type.parent.name}.${type.name} = {`);
- this._builder.indent();
- const keys = Array.from(type.values.keys());
- for (let i = 0; i < keys.length; i++) {
- const key = keys[i];
- this._builder.add(`${key}: ${type.values.get(key)}${i === keys.length - 1 ? '' : ','}`);
- }
- this._builder.outdent();
- this._builder.add('};');
- /* eslint-enable indent */
- }
- };
- flatc.Generator.StringBuilder = class {
- constructor() {
- this._indentation = '';
- this._lines = [''];
- this._newline = true;
- }
- indent() {
- this._indentation += ' ';
- }
- outdent() {
- if (this._indentation.length === 0) {
- throw new flatc.Error('Invalid indentation.');
- }
- this._indentation = this._indentation.substring(0, this._indentation.length - 4);
- }
- add(text, newline) {
- if (this._newline) {
- if (text !== '') {
- this._lines.push(this._indentation);
- }
- }
- this._lines[this._lines.length - 1] = this._lines[this._lines.length - 1] + text + (newline === false ? '' : '\n');
- this._newline = newline === false ? false : true;
- }
- toString() {
- return this._lines.join('');
- }
- };
- flatc.Error = class extends Error {
- constructor(message) {
- super(message);
- this.name = 'FlatBuffers Compiler Error';
- }
- };
- const main = async (args) => {
- const options = { verbose: false, root: 'default', out: '', text: false, paths: [], files: [] };
- while (args.length > 0) {
- const arg = args.shift();
- switch (arg) {
- case '--verbose':
- options.verbose = true;
- break;
- case '--out':
- options.out = args.shift();
- break;
- case '--root':
- options.root = args.shift();
- break;
- case '--text':
- options.text = true;
- break;
- case '--path':
- options.paths.push(args.shift());
- break;
- default:
- if (arg.startsWith('-')) {
- throw new flatc.Error(`Invalid command line argument '${arg}'.`);
- }
- options.files.push(arg);
- break;
- }
- }
- try {
- const root = new flatc.Root(options.root);
- await root.load(options.paths, options.files);
- const generator = new flatc.Generator(root, options.text);
- if (options.out) {
- await fs.writeFile(options.out, generator.content, 'utf-8');
- }
- } catch (error) {
- if (error instanceof flatc.Error && !options.verbose) {
- process.stderr.write(`${error.message}\n`);
- } else {
- process.stderr.write(`${error.stack}\n`);
- }
- process.exit(1);
- }
- process.exit(0);
- };
- if (typeof process === 'object' &&
- Array.isArray(process.argv) && process.argv.length > 1 &&
- path.basename(process.argv[1]) === 'flatc.js') {
- const args = process.argv.slice(2);
- await main(args);
- }
- export const Root = flatc.Root;
- export const Namespace = flatc.Namespace;
- export const Type = flatc.Type;
- export const Enum = flatc.Enum;
|