| 281 | } |
| 282 | |
| 283 | ProtocolDecl parseProtocol() { |
| 284 | ProtocolDecl decl; |
| 285 | decl.line = token_.line; |
| 286 | decl.name = expect(Tok::Ident, "protocol name").text; |
| 287 | expect(Tok::LBrace); |
| 288 | std::unordered_set<std::string> commandNames; |
| 289 | while (token_.kind != Tok::RBrace) { |
| 290 | const Token keyword = expect(Tok::Ident, "'request' or 'command'"); |
| 291 | if (keyword.text == "request") { |
| 292 | if (!decl.requestHeader.empty()) fail(keyword.line, "duplicate protocol request header"); |
| 293 | decl.requestHeader = expect(Tok::Ident, "request header type").text; |
| 294 | expect(Tok::Semi); |
| 295 | continue; |
| 296 | } |
| 297 | if (keyword.text != "command") { |
| 298 | fail(keyword.line, "expected 'request' or 'command', got '" + keyword.text + "'"); |
| 299 | } |
| 300 | CommandDecl command; |
| 301 | command.line = token_.line; |
| 302 | command.name = expect(Tok::Ident, "command name").text; |
| 303 | ensureUnique(commandNames, command.name, "command", command.line); |
| 304 | expect(Tok::Equal); |
| 305 | command.enumValue = parseScopedName(); |
| 306 | expect(Tok::LBrace); |
| 307 | bool hasRequest = false; |
| 308 | bool hasResponse = false; |
| 309 | while (token_.kind != Tok::RBrace) { |
| 310 | const Token section = expect(Tok::Ident, "'request' or 'response'"); |
| 311 | if (section.text == "request") { |
| 312 | if (hasRequest) fail(section.line, "duplicate command request section"); |
| 313 | command.request = parseFrame(); |
| 314 | hasRequest = true; |
| 315 | } else if (section.text == "response") { |
| 316 | if (hasResponse) fail(section.line, "duplicate command response section"); |
| 317 | if (token_.kind == Tok::Ident && token_.text == "none") { |
| 318 | command.responseNone = true; |
| 319 | advance(); |
| 320 | expect(Tok::Semi); |
| 321 | } else { |
| 322 | command.response = parseFrame(); |
| 323 | } |
| 324 | hasResponse = true; |
| 325 | } else { |
| 326 | fail(section.line, "expected 'request' or 'response', got '" + section.text + "'"); |
| 327 | } |
| 328 | } |
| 329 | expect(Tok::RBrace); |
| 330 | optionalSemi(); |
| 331 | if (!hasRequest || !hasResponse) fail(command.line, "command requires request and response sections"); |
| 332 | decl.commands.push_back(std::move(command)); |
| 333 | } |
| 334 | expect(Tok::RBrace); |
| 335 | optionalSemi(); |
| 336 | return decl; |
| 337 | } |
| 338 | |
| 339 | FrameDecl parseFrame() { |
| 340 | FrameDecl frame; |