Buckets:
| ; | |
| module.exports = parse; | |
| parse.filename = null; | |
| parse.defaults = { keepCase: false }; | |
| var tokenize = require("./tokenize"), | |
| Root = require("./root"), | |
| Type = require("./type"), | |
| Field = require("./field"), | |
| MapField = require("./mapfield"), | |
| OneOf = require("./oneof"), | |
| Enum = require("./enum"), | |
| Service = require("./service"), | |
| Method = require("./method"), | |
| ReflectionObject = require("./object"), | |
| types = require("./types"), | |
| util = require("./util"); | |
| var base10Re = /^[1-9][0-9]*$/, | |
| base10NegRe = /^-?[1-9][0-9]*$/, | |
| base16Re = /^0[x][0-9a-fA-F]+$/, | |
| base16NegRe = /^-?0[x][0-9a-fA-F]+$/, | |
| base8Re = /^0[0-7]+$/, | |
| base8NegRe = /^-?0[0-7]+$/, | |
| numberRe = /^(?![eE])[0-9]*(?:\.[0-9]*)?(?:[eE][+-]?[0-9]+)?$/, | |
| nameRe = /^[a-zA-Z_][a-zA-Z_0-9]*$/, | |
| typeRefRe = /^(?:\.?[a-zA-Z_][a-zA-Z_0-9]*)(?:\.[a-zA-Z_][a-zA-Z_0-9]*)*$/; | |
| /** | |
| * Result object returned from {@link parse}. | |
| * @interface IParserResult | |
| * @property {string|undefined} package Package name, if declared | |
| * @property {string[]|undefined} imports Imports, if any | |
| * @property {string[]|undefined} weakImports Weak imports, if any | |
| * @property {Root} root Populated root instance | |
| */ | |
| /** | |
| * Options modifying the behavior of {@link parse}. | |
| * @interface IParseOptions | |
| * @property {boolean} [keepCase=false] Keeps field casing instead of converting to camel case | |
| * @property {boolean} [alternateCommentMode=false] Recognize double-slash comments in addition to doc-block comments. | |
| * @property {boolean} [preferTrailingComment=false] Use trailing comment when both leading comment and trailing comment exist. | |
| */ | |
| /** | |
| * Options modifying the behavior of JSON serialization. | |
| * @interface IToJSONOptions | |
| * @property {boolean} [keepComments=false] Serializes comments. | |
| */ | |
| /** | |
| * Parses the given .proto source and returns an object with the parsed contents. | |
| * @param {string} source Source contents | |
| * @param {Root} root Root to populate | |
| * @param {IParseOptions} [options] Parse options. Defaults to {@link parse.defaults} when omitted. | |
| * @returns {IParserResult} Parser result | |
| * @property {string} filename=null Currently processing file name for error reporting, if known | |
| * @property {IParseOptions} defaults Default {@link IParseOptions} | |
| */ | |
| function parse(source, root, options) { | |
| /* eslint-disable callback-return */ | |
| if (!(root instanceof Root)) { | |
| options = root; | |
| root = new Root(); | |
| } | |
| if (!options) | |
| options = parse.defaults; | |
| var preferTrailingComment = options.preferTrailingComment || false; | |
| var tn = tokenize(source, options.alternateCommentMode || false), | |
| next = tn.next, | |
| push = tn.push, | |
| peek = tn.peek, | |
| skip = tn.skip, | |
| cmnt = tn.cmnt; | |
| var head = true, | |
| pkg, | |
| imports, | |
| weakImports, | |
| edition = "proto2"; | |
| var ptr = root; | |
| var topLevelObjects = []; | |
| var topLevelOptions = {}; | |
| var applyCase = options.keepCase ? function(name) { return name; } : util.camelCase; | |
| function resolveFileFeatures() { | |
| topLevelObjects.forEach(obj => { | |
| obj._edition = edition; | |
| Object.keys(topLevelOptions).forEach(opt => { | |
| if (obj.getOption(opt) !== undefined) return; | |
| obj.setOption(opt, topLevelOptions[opt], true); | |
| }); | |
| }); | |
| } | |
| /* istanbul ignore next */ | |
| function illegal(token, name, insideTryCatch) { | |
| var filename = parse.filename; | |
| if (!insideTryCatch) | |
| parse.filename = null; | |
| return Error("illegal " + (name || "token") + " '" + token + "' (" + (filename ? filename + ", " : "") + "line " + tn.line + ")"); | |
| } | |
| function readString() { | |
| var values = [], | |
| token; | |
| do { | |
| /* istanbul ignore if */ | |
| if ((token = next()) !== "\"" && token !== "'") | |
| throw illegal(token); | |
| values.push(next()); | |
| skip(token); | |
| token = peek(); | |
| } while (token === "\"" || token === "'"); | |
| return values.join(""); | |
| } | |
| function readValue(acceptTypeRef) { | |
| var token = next(); | |
| switch (token) { | |
| case "'": | |
| case "\"": | |
| push(token); | |
| return readString(); | |
| case "true": case "TRUE": | |
| return true; | |
| case "false": case "FALSE": | |
| return false; | |
| } | |
| try { | |
| return parseNumber(token, /* insideTryCatch */ true); | |
| } catch (e) { | |
| /* istanbul ignore else */ | |
| if (acceptTypeRef && typeRefRe.test(token)) | |
| return token; | |
| /* istanbul ignore next */ | |
| throw illegal(token, "value"); | |
| } | |
| } | |
| function readRanges(target, acceptStrings) { | |
| var token, start; | |
| do { | |
| if (acceptStrings && ((token = peek()) === "\"" || token === "'")) { | |
| var str = readString(); | |
| target.push(str); | |
| if (edition >= 2023) { | |
| throw illegal(str, "id"); | |
| } | |
| } else { | |
| try { | |
| target.push([ start = parseId(next()), skip("to", true) ? parseId(next()) : start ]); | |
| } catch (err) { | |
| if (acceptStrings && typeRefRe.test(token) && edition >= 2023) { | |
| target.push(token); | |
| } else { | |
| throw err; | |
| } | |
| } | |
| } | |
| } while (skip(",", true)); | |
| var dummy = {options: undefined}; | |
| dummy.setOption = function(name, value) { | |
| if (this.options === undefined) this.options = {}; | |
| this.options[name] = value; | |
| }; | |
| ifBlock( | |
| dummy, | |
| function parseRange_block(token) { | |
| /* istanbul ignore else */ | |
| if (token === "option") { | |
| parseOption(dummy, token); // skip | |
| skip(";"); | |
| } else | |
| throw illegal(token); | |
| }, | |
| function parseRange_line() { | |
| parseInlineOptions(dummy); // skip | |
| }); | |
| } | |
| function parseNumber(token, insideTryCatch) { | |
| var sign = 1; | |
| if (token.charAt(0) === "-") { | |
| sign = -1; | |
| token = token.substring(1); | |
| } | |
| switch (token) { | |
| case "inf": case "INF": case "Inf": | |
| return sign * Infinity; | |
| case "nan": case "NAN": case "Nan": case "NaN": | |
| return NaN; | |
| case "0": | |
| return 0; | |
| } | |
| if (base10Re.test(token)) | |
| return sign * parseInt(token, 10); | |
| if (base16Re.test(token)) | |
| return sign * parseInt(token, 16); | |
| if (base8Re.test(token)) | |
| return sign * parseInt(token, 8); | |
| /* istanbul ignore else */ | |
| if (numberRe.test(token)) | |
| return sign * parseFloat(token); | |
| /* istanbul ignore next */ | |
| throw illegal(token, "number", insideTryCatch); | |
| } | |
| function parseId(token, acceptNegative) { | |
| switch (token) { | |
| case "max": case "MAX": case "Max": | |
| return 536870911; | |
| case "0": | |
| return 0; | |
| } | |
| /* istanbul ignore if */ | |
| if (!acceptNegative && token.charAt(0) === "-") | |
| throw illegal(token, "id"); | |
| if (base10NegRe.test(token)) | |
| return parseInt(token, 10); | |
| if (base16NegRe.test(token)) | |
| return parseInt(token, 16); | |
| /* istanbul ignore else */ | |
| if (base8NegRe.test(token)) | |
| return parseInt(token, 8); | |
| /* istanbul ignore next */ | |
| throw illegal(token, "id"); | |
| } | |
| function parsePackage() { | |
| /* istanbul ignore if */ | |
| if (pkg !== undefined) | |
| throw illegal("package"); | |
| pkg = next(); | |
| /* istanbul ignore if */ | |
| if (!typeRefRe.test(pkg)) | |
| throw illegal(pkg, "name"); | |
| ptr = ptr.define(pkg); | |
| skip(";"); | |
| } | |
| function parseImport() { | |
| var token = peek(); | |
| var whichImports; | |
| switch (token) { | |
| case "weak": | |
| whichImports = weakImports || (weakImports = []); | |
| next(); | |
| break; | |
| case "public": | |
| next(); | |
| // eslint-disable-next-line no-fallthrough | |
| default: | |
| whichImports = imports || (imports = []); | |
| break; | |
| } | |
| token = readString(); | |
| skip(";"); | |
| whichImports.push(token); | |
| } | |
| function parseSyntax() { | |
| skip("="); | |
| edition = readString(); | |
| /* istanbul ignore if */ | |
| if (edition < 2023) | |
| throw illegal(edition, "syntax"); | |
| skip(";"); | |
| } | |
| function parseEdition() { | |
| skip("="); | |
| edition = readString(); | |
| const supportedEditions = ["2023"]; | |
| /* istanbul ignore if */ | |
| if (!supportedEditions.includes(edition)) | |
| throw illegal(edition, "edition"); | |
| skip(";"); | |
| } | |
| function parseCommon(parent, token) { | |
| switch (token) { | |
| case "option": | |
| parseOption(parent, token); | |
| skip(";"); | |
| return true; | |
| case "message": | |
| parseType(parent, token); | |
| return true; | |
| case "enum": | |
| parseEnum(parent, token); | |
| return true; | |
| case "service": | |
| parseService(parent, token); | |
| return true; | |
| case "extend": | |
| parseExtension(parent, token); | |
| return true; | |
| } | |
| return false; | |
| } | |
| function ifBlock(obj, fnIf, fnElse) { | |
| var trailingLine = tn.line; | |
| if (obj) { | |
| if(typeof obj.comment !== "string") { | |
| obj.comment = cmnt(); // try block-type comment | |
| } | |
| obj.filename = parse.filename; | |
| } | |
| if (skip("{", true)) { | |
| var token; | |
| while ((token = next()) !== "}") | |
| fnIf(token); | |
| skip(";", true); | |
| } else { | |
| if (fnElse) | |
| fnElse(); | |
| skip(";"); | |
| if (obj && (typeof obj.comment !== "string" || preferTrailingComment)) | |
| obj.comment = cmnt(trailingLine) || obj.comment; // try line-type comment | |
| } | |
| } | |
| function parseType(parent, token) { | |
| /* istanbul ignore if */ | |
| if (!nameRe.test(token = next())) | |
| throw illegal(token, "type name"); | |
| var type = new Type(token); | |
| ifBlock(type, function parseType_block(token) { | |
| if (parseCommon(type, token)) | |
| return; | |
| switch (token) { | |
| case "map": | |
| parseMapField(type, token); | |
| break; | |
| case "required": | |
| if (edition !== "proto2") | |
| throw illegal(token); | |
| /* eslint-disable no-fallthrough */ | |
| case "repeated": | |
| parseField(type, token); | |
| break; | |
| case "optional": | |
| /* istanbul ignore if */ | |
| if (edition === "proto3") { | |
| parseField(type, "proto3_optional"); | |
| } else if (edition !== "proto2") { | |
| throw illegal(token); | |
| } else { | |
| parseField(type, "optional"); | |
| } | |
| break; | |
| case "oneof": | |
| parseOneOf(type, token); | |
| break; | |
| case "extensions": | |
| readRanges(type.extensions || (type.extensions = [])); | |
| break; | |
| case "reserved": | |
| readRanges(type.reserved || (type.reserved = []), true); | |
| break; | |
| default: | |
| /* istanbul ignore if */ | |
| if (edition === "proto2" || !typeRefRe.test(token)) { | |
| throw illegal(token); | |
| } | |
| push(token); | |
| parseField(type, "optional"); | |
| break; | |
| } | |
| }); | |
| parent.add(type); | |
| if (parent === ptr) { | |
| topLevelObjects.push(type); | |
| } | |
| } | |
| function parseField(parent, rule, extend) { | |
| var type = next(); | |
| if (type === "group") { | |
| parseGroup(parent, rule); | |
| return; | |
| } | |
| // Type names can consume multiple tokens, in multiple variants: | |
| // package.subpackage field tokens: "package.subpackage" [TYPE NAME ENDS HERE] "field" | |
| // package . subpackage field tokens: "package" "." "subpackage" [TYPE NAME ENDS HERE] "field" | |
| // package. subpackage field tokens: "package." "subpackage" [TYPE NAME ENDS HERE] "field" | |
| // package .subpackage field tokens: "package" ".subpackage" [TYPE NAME ENDS HERE] "field" | |
| // Keep reading tokens until we get a type name with no period at the end, | |
| // and the next token does not start with a period. | |
| while (type.endsWith(".") || peek().startsWith(".")) { | |
| type += next(); | |
| } | |
| /* istanbul ignore if */ | |
| if (!typeRefRe.test(type)) | |
| throw illegal(type, "type"); | |
| var name = next(); | |
| /* istanbul ignore if */ | |
| if (!nameRe.test(name)) | |
| throw illegal(name, "name"); | |
| name = applyCase(name); | |
| skip("="); | |
| var field = new Field(name, parseId(next()), type, rule, extend); | |
| ifBlock(field, function parseField_block(token) { | |
| /* istanbul ignore else */ | |
| if (token === "option") { | |
| parseOption(field, token); | |
| skip(";"); | |
| } else | |
| throw illegal(token); | |
| }, function parseField_line() { | |
| parseInlineOptions(field); | |
| }); | |
| if (rule === "proto3_optional") { | |
| // for proto3 optional fields, we create a single-member Oneof to mimic "optional" behavior | |
| var oneof = new OneOf("_" + name); | |
| field.setOption("proto3_optional", true); | |
| oneof.add(field); | |
| parent.add(oneof); | |
| } else { | |
| parent.add(field); | |
| } | |
| if (parent === ptr) { | |
| topLevelObjects.push(field); | |
| } | |
| } | |
| function parseGroup(parent, rule) { | |
| if (edition >= 2023) { | |
| throw illegal("group"); | |
| } | |
| var name = next(); | |
| /* istanbul ignore if */ | |
| if (!nameRe.test(name)) | |
| throw illegal(name, "name"); | |
| var fieldName = util.lcFirst(name); | |
| if (name === fieldName) | |
| name = util.ucFirst(name); | |
| skip("="); | |
| var id = parseId(next()); | |
| var type = new Type(name); | |
| type.group = true; | |
| var field = new Field(fieldName, id, name, rule); | |
| field.filename = parse.filename; | |
| ifBlock(type, function parseGroup_block(token) { | |
| switch (token) { | |
| case "option": | |
| parseOption(type, token); | |
| skip(";"); | |
| break; | |
| case "required": | |
| case "repeated": | |
| parseField(type, token); | |
| break; | |
| case "optional": | |
| /* istanbul ignore if */ | |
| if (edition === "proto3") { | |
| parseField(type, "proto3_optional"); | |
| } else { | |
| parseField(type, "optional"); | |
| } | |
| break; | |
| case "message": | |
| parseType(type, token); | |
| break; | |
| case "enum": | |
| parseEnum(type, token); | |
| break; | |
| case "reserved": | |
| readRanges(type.reserved || (type.reserved = []), true); | |
| break; | |
| /* istanbul ignore next */ | |
| default: | |
| throw illegal(token); // there are no groups with proto3 semantics | |
| } | |
| }); | |
| parent.add(type) | |
| .add(field); | |
| } | |
| function parseMapField(parent) { | |
| skip("<"); | |
| var keyType = next(); | |
| /* istanbul ignore if */ | |
| if (types.mapKey[keyType] === undefined) | |
| throw illegal(keyType, "type"); | |
| skip(","); | |
| var valueType = next(); | |
| /* istanbul ignore if */ | |
| if (!typeRefRe.test(valueType)) | |
| throw illegal(valueType, "type"); | |
| skip(">"); | |
| var name = next(); | |
| /* istanbul ignore if */ | |
| if (!nameRe.test(name)) | |
| throw illegal(name, "name"); | |
| skip("="); | |
| var field = new MapField(applyCase(name), parseId(next()), keyType, valueType); | |
| ifBlock(field, function parseMapField_block(token) { | |
| /* istanbul ignore else */ | |
| if (token === "option") { | |
| parseOption(field, token); | |
| skip(";"); | |
| } else | |
| throw illegal(token); | |
| }, function parseMapField_line() { | |
| parseInlineOptions(field); | |
| }); | |
| parent.add(field); | |
| } | |
| function parseOneOf(parent, token) { | |
| /* istanbul ignore if */ | |
| if (!nameRe.test(token = next())) | |
| throw illegal(token, "name"); | |
| var oneof = new OneOf(applyCase(token)); | |
| ifBlock(oneof, function parseOneOf_block(token) { | |
| if (token === "option") { | |
| parseOption(oneof, token); | |
| skip(";"); | |
| } else { | |
| push(token); | |
| parseField(oneof, "optional"); | |
| } | |
| }); | |
| parent.add(oneof); | |
| } | |
| function parseEnum(parent, token) { | |
| /* istanbul ignore if */ | |
| if (!nameRe.test(token = next())) | |
| throw illegal(token, "name"); | |
| var enm = new Enum(token); | |
| ifBlock(enm, function parseEnum_block(token) { | |
| switch(token) { | |
| case "option": | |
| parseOption(enm, token); | |
| skip(";"); | |
| break; | |
| case "reserved": | |
| readRanges(enm.reserved || (enm.reserved = []), true); | |
| if(enm.reserved === undefined) enm.reserved = []; | |
| break; | |
| default: | |
| parseEnumValue(enm, token); | |
| } | |
| }); | |
| parent.add(enm); | |
| if (parent === ptr) { | |
| topLevelObjects.push(enm); | |
| } | |
| } | |
| function parseEnumValue(parent, token) { | |
| /* istanbul ignore if */ | |
| if (!nameRe.test(token)) | |
| throw illegal(token, "name"); | |
| skip("="); | |
| var value = parseId(next(), true), | |
| dummy = { | |
| options: undefined | |
| }; | |
| dummy.getOption = function(name) { | |
| return this.options[name]; | |
| }; | |
| dummy.setOption = function(name, value) { | |
| ReflectionObject.prototype.setOption.call(dummy, name, value); | |
| }; | |
| dummy.setParsedOption = function() { | |
| return undefined; | |
| }; | |
| ifBlock(dummy, function parseEnumValue_block(token) { | |
| /* istanbul ignore else */ | |
| if (token === "option") { | |
| parseOption(dummy, token); // skip | |
| skip(";"); | |
| } else | |
| throw illegal(token); | |
| }, function parseEnumValue_line() { | |
| parseInlineOptions(dummy); // skip | |
| }); | |
| parent.add(token, value, dummy.comment, dummy.parsedOptions || dummy.options); | |
| } | |
| function parseOption(parent, token) { | |
| var option; | |
| var propName; | |
| var isOption = true; | |
| if (token === "option") { | |
| token = next(); | |
| } | |
| while (token !== "=") { | |
| if (token === "(") { | |
| var parensValue = next(); | |
| skip(")"); | |
| token = "(" + parensValue + ")"; | |
| } | |
| if (isOption) { | |
| isOption = false; | |
| if (token.includes(".") && !token.includes("(")) { | |
| var tokens = token.split("."); | |
| option = tokens[0] + "."; | |
| token = tokens[1]; | |
| continue; | |
| } | |
| option = token; | |
| } else { | |
| propName = propName ? propName += token : token; | |
| } | |
| token = next(); | |
| } | |
| var name = propName ? option.concat(propName) : option; | |
| var optionValue = parseOptionValue(parent, name); | |
| propName = propName && propName[0] === "." ? propName.slice(1) : propName; | |
| option = option && option[option.length - 1] === "." ? option.slice(0, -1) : option; | |
| setParsedOption(parent, option, optionValue, propName); | |
| } | |
| function parseOptionValue(parent, name) { | |
| // { a: "foo" b { c: "bar" } } | |
| if (skip("{", true)) { | |
| var objectResult = {}; | |
| while (!skip("}", true)) { | |
| /* istanbul ignore if */ | |
| if (!nameRe.test(token = next())) { | |
| throw illegal(token, "name"); | |
| } | |
| if (token === null) { | |
| throw illegal(token, "end of input"); | |
| } | |
| var value; | |
| var propName = token; | |
| skip(":", true); | |
| if (peek() === "{") { | |
| // option (my_option) = { | |
| // repeated_value: [ "foo", "bar" ] | |
| // }; | |
| value = parseOptionValue(parent, name + "." + token); | |
| } else if (peek() === "[") { | |
| value = []; | |
| var lastValue; | |
| if (skip("[", true)) { | |
| do { | |
| lastValue = readValue(true); | |
| value.push(lastValue); | |
| } while (skip(",", true)); | |
| skip("]"); | |
| if (typeof lastValue !== "undefined") { | |
| setOption(parent, name + "." + token, lastValue); | |
| } | |
| } | |
| } else { | |
| value = readValue(true); | |
| setOption(parent, name + "." + token, value); | |
| } | |
| var prevValue = objectResult[propName]; | |
| if (prevValue) | |
| value = [].concat(prevValue).concat(value); | |
| objectResult[propName] = value; | |
| // Semicolons and commas can be optional | |
| skip(",", true); | |
| skip(";", true); | |
| } | |
| return objectResult; | |
| } | |
| var simpleValue = readValue(true); | |
| setOption(parent, name, simpleValue); | |
| return simpleValue; | |
| // Does not enforce a delimiter to be universal | |
| } | |
| function setOption(parent, name, value) { | |
| if (ptr === parent && /^features\./.test(name)) { | |
| topLevelOptions[name] = value; | |
| return; | |
| } | |
| if (parent.setOption) | |
| parent.setOption(name, value); | |
| } | |
| function setParsedOption(parent, name, value, propName) { | |
| if (parent.setParsedOption) | |
| parent.setParsedOption(name, value, propName); | |
| } | |
| function parseInlineOptions(parent) { | |
| if (skip("[", true)) { | |
| do { | |
| parseOption(parent, "option"); | |
| } while (skip(",", true)); | |
| skip("]"); | |
| } | |
| return parent; | |
| } | |
| function parseService(parent, token) { | |
| /* istanbul ignore if */ | |
| if (!nameRe.test(token = next())) | |
| throw illegal(token, "service name"); | |
| var service = new Service(token); | |
| ifBlock(service, function parseService_block(token) { | |
| if (parseCommon(service, token)) { | |
| return; | |
| } | |
| /* istanbul ignore else */ | |
| if (token === "rpc") | |
| parseMethod(service, token); | |
| else | |
| throw illegal(token); | |
| }); | |
| parent.add(service); | |
| if (parent === ptr) { | |
| topLevelObjects.push(service); | |
| } | |
| } | |
| function parseMethod(parent, token) { | |
| // Get the comment of the preceding line now (if one exists) in case the | |
| // method is defined across multiple lines. | |
| var commentText = cmnt(); | |
| var type = token; | |
| /* istanbul ignore if */ | |
| if (!nameRe.test(token = next())) | |
| throw illegal(token, "name"); | |
| var name = token, | |
| requestType, requestStream, | |
| responseType, responseStream; | |
| skip("("); | |
| if (skip("stream", true)) | |
| requestStream = true; | |
| /* istanbul ignore if */ | |
| if (!typeRefRe.test(token = next())) | |
| throw illegal(token); | |
| requestType = token; | |
| skip(")"); skip("returns"); skip("("); | |
| if (skip("stream", true)) | |
| responseStream = true; | |
| /* istanbul ignore if */ | |
| if (!typeRefRe.test(token = next())) | |
| throw illegal(token); | |
| responseType = token; | |
| skip(")"); | |
| var method = new Method(name, type, requestType, responseType, requestStream, responseStream); | |
| method.comment = commentText; | |
| ifBlock(method, function parseMethod_block(token) { | |
| /* istanbul ignore else */ | |
| if (token === "option") { | |
| parseOption(method, token); | |
| skip(";"); | |
| } else | |
| throw illegal(token); | |
| }); | |
| parent.add(method); | |
| } | |
| function parseExtension(parent, token) { | |
| /* istanbul ignore if */ | |
| if (!typeRefRe.test(token = next())) | |
| throw illegal(token, "reference"); | |
| var reference = token; | |
| ifBlock(null, function parseExtension_block(token) { | |
| switch (token) { | |
| case "required": | |
| case "repeated": | |
| parseField(parent, token, reference); | |
| break; | |
| case "optional": | |
| /* istanbul ignore if */ | |
| if (edition === "proto3") { | |
| parseField(parent, "proto3_optional", reference); | |
| } else { | |
| parseField(parent, "optional", reference); | |
| } | |
| break; | |
| default: | |
| /* istanbul ignore if */ | |
| if (edition === "proto2" || !typeRefRe.test(token)) | |
| throw illegal(token); | |
| push(token); | |
| parseField(parent, "optional", reference); | |
| break; | |
| } | |
| }); | |
| } | |
| var token; | |
| while ((token = next()) !== null) { | |
| switch (token) { | |
| case "package": | |
| /* istanbul ignore if */ | |
| if (!head) | |
| throw illegal(token); | |
| parsePackage(); | |
| break; | |
| case "import": | |
| /* istanbul ignore if */ | |
| if (!head) | |
| throw illegal(token); | |
| parseImport(); | |
| break; | |
| case "syntax": | |
| /* istanbul ignore if */ | |
| if (!head) | |
| throw illegal(token); | |
| parseSyntax(); | |
| break; | |
| case "edition": | |
| /* istanbul ignore if */ | |
| if (!head) | |
| throw illegal(token); | |
| parseEdition(); | |
| break; | |
| case "option": | |
| parseOption(ptr, token); | |
| skip(";", true); | |
| break; | |
| default: | |
| /* istanbul ignore else */ | |
| if (parseCommon(ptr, token)) { | |
| head = false; | |
| continue; | |
| } | |
| /* istanbul ignore next */ | |
| throw illegal(token); | |
| } | |
| } | |
| resolveFileFeatures(); | |
| parse.filename = null; | |
| return { | |
| "package" : pkg, | |
| "imports" : imports, | |
| weakImports : weakImports, | |
| root : root | |
| }; | |
| } | |
| /** | |
| * Parses the given .proto source and returns an object with the parsed contents. | |
| * @name parse | |
| * @function | |
| * @param {string} source Source contents | |
| * @param {IParseOptions} [options] Parse options. Defaults to {@link parse.defaults} when omitted. | |
| * @returns {IParserResult} Parser result | |
| * @property {string} filename=null Currently processing file name for error reporting, if known | |
| * @property {IParseOptions} defaults Default {@link IParseOptions} | |
| * @variation 2 | |
| */ | |
Xet Storage Details
- Size:
- 29.3 kB
- Xet hash:
- b7268c8a30492e1fcd7fc15481f7a6ab66bff1cbd3455f8733fcafd87714d20c
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.