-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathgroup.ts
167 lines (161 loc) · 5.41 KB
/
group.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import { ParseNode, ParseNodeJSON } from "./parsenode.ts";
import { Visitor } from "./visitor.ts";
import { nextTokenIs, Scanner, Token, TokenError } from "./deps.ts";
import { Field } from "./field.ts";
import { Enum } from "./enum.ts";
import { Message } from "./message.ts";
import { Option } from "./option.ts";
import { Extend } from "./extend.ts";
import { Extensions } from "./extensions.ts";
import { Oneof } from "./oneof.ts";
import { MapField } from "./mapfield.ts";
import { Reserved } from "./reserved.ts";
import { expectSimpleIdent } from "./util.ts";
type MessageStatement =
| Field
| Enum
| Message
| Extend
| Extensions
| Option
| Oneof
| MapField
| Reserved;
/**
* Represents a Group definition.
*
* This Node will only appear in Proto2 files. It was removed from Proto3.
*
* https://developers.google.com/protocol-buffers/docs/reference/proto2-spec#group_field
*/
export class Group extends ParseNode {
repeated: boolean;
optional: boolean;
required: boolean;
constructor(
public name: string,
public id: number,
public body: MessageStatement[] = [],
{ repeated = false, optional = false, required = false } = {},
public start: [number, number] = [0, 0],
public end: [number, number] = [0, 0],
) {
super();
this.repeated = repeated;
this.optional = optional;
this.required = required;
}
toProto(syntax: 2 | 3 = 3) {
if (syntax === 3) {
throw new Error("Group fields are not allowed in Proto3");
}
let label = "";
if (this.optional) {
label = "optional ";
}
if (this.repeated) {
label = "repeated ";
}
if (this.required) {
label = "required ";
}
let body = "";
if (this.body.length) {
body = `\n ${
this.body.map((node) => node.toProto(syntax)).join("\n ")
}\n`;
}
return `${label}group ${this.name} = ${this.id} {${body}}`;
}
toJSON(): ParseNodeJSON {
return {
type: "Group",
start: this.start,
end: this.end,
name: this.name,
id: this.id,
repeated: this.repeated,
optional: this.optional,
required: this.required,
body: this.body.map((node) => node.toJSON()),
};
}
accept(visitor: Visitor) {
visitor.visit?.(this);
visitor.visitGroup?.(this);
for (const node of this.body) node.accept(visitor);
}
static async parse(scanner: Scanner, syntax: 2 | 3 = 3): Promise<Group> {
if (!scanner.contents) await scanner.scan();
if (syntax === 3) {
throw new TokenError(
scanner,
Token.identifier,
Token.identifier,
"(Group fields are not allowed in Proto3)",
);
}
const start = scanner.startPos;
const opts = { repeated: false, optional: false, required: false };
if (scanner.contents === "repeated") {
opts.repeated = true;
await nextTokenIs(scanner, Token.identifier, "group");
}
if (scanner.contents === "optional") {
opts.optional = true;
await nextTokenIs(scanner, Token.identifier, "group");
}
if (scanner.contents === "required") {
opts.required = true;
await nextTokenIs(scanner, Token.identifier, "group");
}
const name = await expectSimpleIdent(scanner);
if (name[0].toUpperCase() !== name[0]) {
throw new TokenError(
scanner,
Token.identifier,
Token.identifier,
"(Group names must start with a capital letter)",
);
}
await nextTokenIs(scanner, Token.token, "=");
const id = Number(await nextTokenIs(scanner, Token.int));
const body: MessageStatement[] = [];
await nextTokenIs(scanner, Token.token, "{");
for await (const token of scanner) {
const str = scanner.contents;
if (token === Token.token && str === "}") {
return new Group(name, id, body, opts, start, scanner.endPos);
} else if (token === Token.token && str === ";") {
// Empty statements are allowed!
} else if (token === Token.keyword && str === "repeated") {
body.push(await Field.parse(scanner, syntax));
} else if (token === Token.keyword && str === "optional") {
body.push(await Field.parse(scanner, syntax));
} else if (token === Token.keyword && str === "required") {
body.push(await Field.parse(scanner, syntax));
} else if (token === Token.identifier && str !== "oneof") {
body.push(await Field.parse(scanner, syntax));
} else if (token === Token.keyword && str === "enum") {
body.push(await Enum.parse(scanner));
} else if (token === Token.keyword && str === "message") {
body.push(await Message.parse(scanner, syntax));
} else if (token === Token.keyword && str === "extend") {
body.push(await Extend.parse(scanner, syntax));
} else if (token === Token.keyword && str === "extensions") {
body.push(await Extensions.parse(scanner, syntax));
} else if (token === Token.keyword && str === "option") {
body.push(await Option.parse(scanner));
} else if (token === Token.identifier && str === "oneof") {
body.push(await Oneof.parse(scanner, syntax));
} else if (token === Token.keyword && str === "map") {
body.push(await MapField.parse(scanner));
} else if (token === Token.identifier && str === "reserved") {
body.push(await Reserved.parse(scanner));
} else {
throw new TokenError(scanner, token);
}
}
throw new TokenError(scanner, Token.eof);
}
}