/** Reads a non-whitespace character and returns it. */ public char readChar() { char result = peekChar(); pos++; return result; }
public boolean peekChar(char c) { if (peekChar() == c) { pos++; return true; } else { return false; } }
/** * Reads options enclosed in '[' and ']' if they are present and returns them. Returns an empty * list if no options are present. */ public ImmutableList<OptionElement> readOptions() { if (!reader.peekChar('[')) return ImmutableList.of(); ImmutableList.Builder<OptionElement> result = ImmutableList.builder(); while (true) { result.add(readOption('=')); // Check for closing ']' if (reader.peekChar(']')) break; // Discard optional ','. if (!reader.peekChar(',')) throw reader.unexpected("Expected ',' or ']"); } return result.build(); }
/** * Returns a list of values. This is similar to JSON with '[' and ']' surrounding the list and ',' * separating values. */ private List<Object> readList() { reader.require('['); List<Object> result = new ArrayList<>(); while (true) { // If we see the close brace, finish immediately. This handles [] and ,] cases. if (reader.peekChar(']')) return result; result.add(readKindAndValue().value()); if (reader.peekChar(',')) continue; if (reader.peekChar() != ']') throw reader.unexpected("expected ',' or ']'"); } }
/** Reads a quoted or unquoted string and returns it. */ public String readString() { skipWhitespace(true); char c = peekChar(); return c == '"' || c == '\'' ? readQuotedString() : readWord(); }
/** Reads extensions like "extensions 101;" or "extensions 101 to max;". */ private ExtensionsElement readExtensions(Location location, String documentation) { int start = reader.readInt(); // Range start. int end = start; if (reader.peekChar() != ';') { if (!"to".equals(reader.readWord())) throw reader.unexpected("expected ';' or 'to'"); String s = reader.readWord(); // Range end. if (s.equals("max")) { end = Util.MAX_TAG_VALUE; } else { end = Integer.parseInt(s); } } reader.require(';'); return ExtensionsElement.create(location, start, end, documentation); }
/** Reads a reserved tags and names list like "reserved 10, 12 to 14, 'foo';". */ private ReservedElement readReserved(Location location, String documentation) { ImmutableList.Builder<Object> valuesBuilder = ImmutableList.builder(); while (true) { char c = reader.peekChar(); if (c == '"' || c == '\'') { valuesBuilder.add(reader.readQuotedString()); } else { int tagStart = reader.readInt(); c = reader.peekChar(); if (c != ',' && c != ';') { if (!reader.readWord().equals("to")) { throw reader.unexpected("expected ',', ';', or 'to'"); } int tagEnd = reader.readInt(); valuesBuilder.add(Range.closed(tagStart, tagEnd)); } else { valuesBuilder.add(tagStart); } } c = reader.readChar(); if (c == ';') break; if (c != ',') throw reader.unexpected("expected ',' or ';'"); } ImmutableList<Object> values = valuesBuilder.build(); if (values.isEmpty()) { throw reader.unexpected("'reserved' must have at least one field name or tag"); } return ReservedElement.create(location, documentation, values); }
/** Reads an enumerated type declaration and returns it. */ private EnumElement readEnumElement(Location location, String documentation) { String name = reader.readName(); EnumElement.Builder builder = EnumElement.builder(location) .name(name) .documentation(documentation); ImmutableList.Builder<EnumConstantElement> constants = ImmutableList.builder(); ImmutableList.Builder<OptionElement> options = ImmutableList.builder(); reader.require('{'); while (true) { String valueDocumentation = reader.readDocumentation(); if (reader.peekChar('}')) break; Object declared = readDeclaration(valueDocumentation, Context.ENUM); if (declared instanceof EnumConstantElement) { constants.add((EnumConstantElement) declared); } else if (declared instanceof OptionElement) { options.add((OptionElement) declared); } } return builder.options(options.build()) .constants(constants.build()) .build(); }
/** Reads a service declaration and returns it. */ private ServiceElement readService(Location location, String documentation) { String name = reader.readName(); ServiceElement.Builder builder = ServiceElement.builder(location) .name(name) .documentation(documentation); reader.require('{'); ImmutableList.Builder<RpcElement> rpcs = ImmutableList.builder(); ImmutableList.Builder<OptionElement> options = ImmutableList.builder(); while (true) { String rpcDocumentation = reader.readDocumentation(); if (reader.peekChar('}')) break; Object declared = readDeclaration(rpcDocumentation, Context.SERVICE); if (declared instanceof RpcElement) { rpcs.add((RpcElement) declared); } else if (declared instanceof OptionElement) { options.add((OptionElement) declared); } } return builder.options(options.build()) .rpcs(rpcs.build()) .build(); }
private OneOfElement readOneOf(String documentation) { OneOfElement.Builder builder = OneOfElement.builder() .name(reader.readName()) .documentation(documentation); ImmutableList.Builder<FieldElement> fields = ImmutableList.builder(); ImmutableList.Builder<GroupElement> groups = ImmutableList.builder(); reader.require('{'); while (true) { String nestedDocumentation = reader.readDocumentation(); if (reader.peekChar('}')) break; Location location = reader.location(); String type = reader.readDataType(); if (type.equals("group")) { groups.add(readGroup(location, nestedDocumentation, null)); } else { fields.add(readField(location, nestedDocumentation, null, type)); } } return builder.fields(fields.build()) .groups(groups.build()) .build(); }
/** Reads an extend declaration. */ private ExtendElement readExtend(Location location, String documentation) { String name = reader.readName(); ExtendElement.Builder builder = ExtendElement.builder(location) .name(name) .documentation(documentation); reader.require('{'); ImmutableList.Builder<FieldElement> fields = ImmutableList.builder(); while (true) { String nestedDocumentation = reader.readDocumentation(); if (reader.peekChar('}')) break; Object declared = readDeclaration(nestedDocumentation, Context.EXTEND); if (declared instanceof FieldElement) { fields.add((FieldElement) declared); } } return builder.fields(fields.build()) .build(); }
&& (!word.equals("map") || reader.peekChar() != '<')) { throw reader.unexpected(location, "unexpected label: " + word);
/** Reads a option containing a name, an '=' or ':', and a value. */ public OptionElement readOption(char keyValueSeparator) { boolean isExtension = (reader.peekChar() == '['); boolean isParenthesized = (reader.peekChar() == '('); String name = reader.readName(); // Option name. if (isExtension) { name = "[" + name + "]"; } String subName = null; char c = reader.readChar(); if (c == '.') { // Read nested field name. For example "baz" in "(foo.bar).baz = 12". subName = reader.readName(); c = reader.readChar(); } if (keyValueSeparator == ':' && c == '{') { // In text format, values which are maps can omit a separator. Backtrack so it can be re-read. reader.pushBack('{'); } else if (c != keyValueSeparator) { throw reader.unexpected("expected '" + keyValueSeparator + "' in option"); } KindAndValue kindAndValue = readKindAndValue(); Kind kind = kindAndValue.kind(); Object value = kindAndValue.value(); if (subName != null) { value = OptionElement.create(subName, kind, value); kind = Kind.OPTION; } return OptionElement.create(name, kind, value, isParenthesized); }
/** Reads a (paren-wrapped), [square-wrapped] or naked symbol name. */ public String readName() { String optionName; char c = peekChar(); if (c == '(') { pos++; optionName = readWord(); if (readChar() != ')') throw unexpected("expected ')'"); } else if (c == '[') { pos++; optionName = readWord(); if (readChar() != ']') throw unexpected("expected ']'"); } else { optionName = readWord(); } return optionName; }
private GroupElement readGroup(Location location, String documentation, Field.Label label) { String name = reader.readWord(); reader.require('='); int tag = reader.readInt(); GroupElement.Builder builder = GroupElement.builder(location) .label(label) .name(name) .tag(tag) .documentation(documentation); ImmutableList.Builder<FieldElement> fields = ImmutableList.builder(); reader.require('{'); while (true) { String nestedDocumentation = reader.readDocumentation(); if (reader.peekChar('}')) break; Location fieldLocation = reader.location(); String fieldLabel = reader.readWord(); Object field = readField(nestedDocumentation, fieldLocation, fieldLabel); if (!(field instanceof FieldElement)) { throw reader.unexpected("expected field declaration, was " + field); } fields.add((FieldElement) field); } return builder.fields(fields.build()) .build(); }
/** Reads a value that can be a map, list, string, number, boolean or enum. */ private KindAndValue readKindAndValue() { char peeked = reader.peekChar(); switch (peeked) { case '{': return KindAndValue.of(MAP, readMap('{', '}', ':')); case '[': return KindAndValue.of(LIST, readList()); case '"': case '\'': return KindAndValue.of(STRING, reader.readString()); default: if (Character.isDigit(peeked) || peeked == '-') { return KindAndValue.of(NUMBER, reader.readWord()); } String word = reader.readWord(); switch (word) { case "true": return KindAndValue.of(BOOLEAN, "true"); case "false": return KindAndValue.of(BOOLEAN, "false"); default: return KindAndValue.of(ENUM, word); } } }
while (!reader.peekChar('}')) { Location wordLocation = reader.location(); String word = reader.readWord();