1 // Copyright 2018 The Amber Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "src/amberscript/parser.h"
16
17 #include <cassert>
18 #include <limits>
19 #include <map>
20 #include <string>
21 #include <utility>
22 #include <vector>
23
24 #include "src/make_unique.h"
25 #include "src/shader_data.h"
26 #include "src/tokenizer.h"
27 #include "src/type_parser.h"
28
29 namespace amber {
30 namespace amberscript {
31 namespace {
32
IsComparator(const std::string & in)33 bool IsComparator(const std::string& in) {
34 return in == "EQ" || in == "NE" || in == "GT" || in == "LT" || in == "GE" ||
35 in == "LE";
36 }
37
ToComparator(const std::string & in)38 ProbeSSBOCommand::Comparator ToComparator(const std::string& in) {
39 if (in == "EQ")
40 return ProbeSSBOCommand::Comparator::kEqual;
41 if (in == "NE")
42 return ProbeSSBOCommand::Comparator::kNotEqual;
43 if (in == "GT")
44 return ProbeSSBOCommand::Comparator::kGreater;
45 if (in == "LT")
46 return ProbeSSBOCommand::Comparator::kLess;
47 if (in == "GE")
48 return ProbeSSBOCommand::Comparator::kGreaterOrEqual;
49
50 assert(in == "LE");
51 return ProbeSSBOCommand::Comparator::kLessOrEqual;
52 }
53
ToType(const std::string & str)54 std::unique_ptr<type::Type> ToType(const std::string& str) {
55 TypeParser parser;
56 if (str == "int8")
57 return parser.Parse("R8_SINT");
58 if (str == "int16")
59 return parser.Parse("R16_SINT");
60 if (str == "int32")
61 return parser.Parse("R32_SINT");
62 if (str == "int64")
63 return parser.Parse("R64_SINT");
64 if (str == "uint8")
65 return parser.Parse("R8_UINT");
66 if (str == "uint16")
67 return parser.Parse("R16_UINT");
68 if (str == "uint32")
69 return parser.Parse("R32_UINT");
70 if (str == "uint64")
71 return parser.Parse("R64_UINT");
72 if (str == "float")
73 return parser.Parse("R32_SFLOAT");
74 if (str == "double")
75 return parser.Parse("R64_SFLOAT");
76
77 if (str.length() > 7 && str.substr(0, 3) == "vec") {
78 if (str[4] != '<' || str[str.length() - 1] != '>')
79 return nullptr;
80
81 int component_count = str[3] - '0';
82 if (component_count < 2 || component_count > 4)
83 return nullptr;
84
85 auto type = ToType(str.substr(5, str.length() - 6));
86 if (!type)
87 return nullptr;
88
89 if (!type->IsNumber() || type->IsArray() || type->IsVec() ||
90 type->IsMatrix()) {
91 return nullptr;
92 }
93
94 type->SetRowCount(static_cast<uint32_t>(component_count));
95 return type;
96 }
97
98 if (str.length() > 9 && str.substr(0, 3) == "mat") {
99 if (str[4] != 'x' || str[6] != '<' || str[str.length() - 1] != '>')
100 return nullptr;
101
102 int column_count = str[3] - '0';
103 if (column_count < 2 || column_count > 4)
104 return nullptr;
105
106 int row_count = str[5] - '0';
107 if (row_count < 2 || row_count > 4)
108 return nullptr;
109
110 auto type = ToType(str.substr(7, str.length() - 8));
111 if (!type)
112 return nullptr;
113 if (!type->IsNumber() || type->IsArray() || type->IsVec() ||
114 type->IsMatrix()) {
115 return nullptr;
116 }
117
118 type->SetRowCount(static_cast<uint32_t>(row_count));
119 type->SetColumnCount(static_cast<uint32_t>(column_count));
120 return type;
121 }
122 return nullptr;
123 }
124
125 } // namespace
126
Parser()127 Parser::Parser() : amber::Parser() {}
128
129 Parser::~Parser() = default;
130
make_error(const std::string & err)131 std::string Parser::make_error(const std::string& err) {
132 return std::to_string(tokenizer_->GetCurrentLine()) + ": " + err;
133 }
134
Parse(const std::string & data)135 Result Parser::Parse(const std::string& data) {
136 tokenizer_ = MakeUnique<Tokenizer>(data);
137
138 for (auto token = tokenizer_->NextToken(); !token->IsEOS();
139 token = tokenizer_->NextToken()) {
140 if (token->IsEOL())
141 continue;
142 if (!token->IsString())
143 return Result(make_error("expected string"));
144
145 Result r;
146 std::string tok = token->AsString();
147 if (IsRepeatable(tok)) {
148 r = ParseRepeatableCommand(tok);
149 } else if (tok == "BUFFER") {
150 r = ParseBuffer();
151 } else if (tok == "DERIVE_PIPELINE") {
152 r = ParseDerivePipelineBlock();
153 } else if (tok == "DEVICE_FEATURE") {
154 r = ParseDeviceFeature();
155 } else if (tok == "DEVICE_EXTENSION") {
156 r = ParseDeviceExtension();
157 } else if (tok == "INSTANCE_EXTENSION") {
158 r = ParseInstanceExtension();
159 } else if (tok == "PIPELINE") {
160 r = ParsePipelineBlock();
161 } else if (tok == "REPEAT") {
162 r = ParseRepeat();
163 } else if (tok == "SET") {
164 r = ParseSet();
165 } else if (tok == "SHADER") {
166 r = ParseShaderBlock();
167 } else if (tok == "STRUCT") {
168 r = ParseStruct();
169 } else {
170 r = Result("unknown token: " + tok);
171 }
172 if (!r.IsSuccess())
173 return Result(make_error(r.Error()));
174 }
175 script_->SetCommands(std::move(command_list_));
176
177 // Generate any needed color attachments. This is done before
178 // validating in case one of the pipelines specifies the framebuffer size
179 // it needs to be verified against all other pipelines.
180 for (const auto& pipeline : script_->GetPipelines()) {
181 // Add a color attachment if needed
182 if (pipeline->GetColorAttachments().empty()) {
183 auto* buf = script_->GetBuffer(Pipeline::kGeneratedColorBuffer);
184 if (!buf) {
185 auto color_buf = pipeline->GenerateDefaultColorAttachmentBuffer();
186 buf = color_buf.get();
187
188 Result r = script_->AddBuffer(std::move(color_buf));
189 if (!r.IsSuccess())
190 return r;
191 }
192 Result r = pipeline->AddColorAttachment(buf, 0);
193 if (!r.IsSuccess())
194 return r;
195 }
196 }
197
198 // Validate all the pipelines at the end. This allows us to verify the
199 // framebuffer sizes are consistent over pipelines.
200 for (const auto& pipeline : script_->GetPipelines()) {
201 Result r = pipeline->Validate();
202 if (!r.IsSuccess())
203 return r;
204 }
205
206 return {};
207 }
208
IsRepeatable(const std::string & name) const209 bool Parser::IsRepeatable(const std::string& name) const {
210 return name == "CLEAR" || name == "CLEAR_COLOR" || name == "COPY" ||
211 name == "EXPECT" || name == "RUN";
212 }
213
214 // The given |name| must be one of the repeatable commands or this method
215 // returns an error result.
ParseRepeatableCommand(const std::string & name)216 Result Parser::ParseRepeatableCommand(const std::string& name) {
217 if (name == "CLEAR")
218 return ParseClear();
219 if (name == "CLEAR_COLOR")
220 return ParseClearColor();
221 if (name == "COPY")
222 return ParseCopy();
223 if (name == "EXPECT")
224 return ParseExpect();
225 if (name == "RUN")
226 return ParseRun();
227
228 return Result("invalid repeatable command: " + name);
229 }
230
ToShaderType(const std::string & str,ShaderType * type)231 Result Parser::ToShaderType(const std::string& str, ShaderType* type) {
232 assert(type);
233
234 if (str == "vertex")
235 *type = kShaderTypeVertex;
236 else if (str == "fragment")
237 *type = kShaderTypeFragment;
238 else if (str == "geometry")
239 *type = kShaderTypeGeometry;
240 else if (str == "tessellation_evaluation")
241 *type = kShaderTypeTessellationEvaluation;
242 else if (str == "tessellation_control")
243 *type = kShaderTypeTessellationControl;
244 else if (str == "compute")
245 *type = kShaderTypeCompute;
246 else if (str == "multi")
247 *type = kShaderTypeMulti;
248 else
249 return Result("unknown shader type: " + str);
250 return {};
251 }
252
ToShaderFormat(const std::string & str,ShaderFormat * fmt)253 Result Parser::ToShaderFormat(const std::string& str, ShaderFormat* fmt) {
254 assert(fmt);
255
256 if (str == "GLSL")
257 *fmt = kShaderFormatGlsl;
258 else if (str == "HLSL")
259 *fmt = kShaderFormatHlsl;
260 else if (str == "SPIRV-ASM")
261 *fmt = kShaderFormatSpirvAsm;
262 else if (str == "SPIRV-HEX")
263 *fmt = kShaderFormatSpirvHex;
264 else if (str == "OPENCL-C")
265 *fmt = kShaderFormatOpenCLC;
266 else
267 return Result("unknown shader format: " + str);
268 return {};
269 }
270
ToPipelineType(const std::string & str,PipelineType * type)271 Result Parser::ToPipelineType(const std::string& str, PipelineType* type) {
272 assert(type);
273
274 if (str == "compute")
275 *type = PipelineType::kCompute;
276 else if (str == "graphics")
277 *type = PipelineType::kGraphics;
278 else
279 return Result("unknown pipeline type: " + str);
280 return {};
281 }
282
ValidateEndOfStatement(const std::string & name)283 Result Parser::ValidateEndOfStatement(const std::string& name) {
284 auto token = tokenizer_->NextToken();
285 if (token->IsEOL() || token->IsEOS())
286 return {};
287 return Result("extra parameters after " + name);
288 }
289
ParseShaderBlock()290 Result Parser::ParseShaderBlock() {
291 auto token = tokenizer_->NextToken();
292 if (!token->IsString())
293 return Result("invalid token when looking for shader type");
294
295 ShaderType type = kShaderTypeVertex;
296 Result r = ToShaderType(token->AsString(), &type);
297 if (!r.IsSuccess())
298 return r;
299
300 auto shader = MakeUnique<Shader>(type);
301
302 token = tokenizer_->NextToken();
303 if (!token->IsString())
304 return Result("invalid token when looking for shader name");
305
306 shader->SetName(token->AsString());
307
308 token = tokenizer_->NextToken();
309 if (!token->IsString())
310 return Result("invalid token when looking for shader format");
311
312 std::string fmt = token->AsString();
313 if (fmt == "PASSTHROUGH") {
314 if (type != kShaderTypeVertex) {
315 return Result(
316 "invalid shader type for PASSTHROUGH. Only vertex "
317 "PASSTHROUGH allowed");
318 }
319 shader->SetFormat(kShaderFormatSpirvAsm);
320 shader->SetData(kPassThroughShader);
321
322 r = script_->AddShader(std::move(shader));
323 if (!r.IsSuccess())
324 return r;
325
326 return ValidateEndOfStatement("SHADER PASSTHROUGH");
327 }
328
329 ShaderFormat format = kShaderFormatGlsl;
330 r = ToShaderFormat(fmt, &format);
331 if (!r.IsSuccess())
332 return r;
333
334 shader->SetFormat(format);
335
336 r = ValidateEndOfStatement("SHADER command");
337 if (!r.IsSuccess())
338 return r;
339
340 std::string data = tokenizer_->ExtractToNext("END");
341 if (data.empty())
342 return Result("SHADER must not be empty");
343
344 shader->SetData(data);
345
346 token = tokenizer_->NextToken();
347 if (!token->IsString() || token->AsString() != "END")
348 return Result("SHADER missing END command");
349
350 r = script_->AddShader(std::move(shader));
351 if (!r.IsSuccess())
352 return r;
353
354 return ValidateEndOfStatement("END");
355 }
356
ParsePipelineBlock()357 Result Parser::ParsePipelineBlock() {
358 auto token = tokenizer_->NextToken();
359 if (!token->IsString())
360 return Result("invalid token when looking for pipeline type");
361
362 PipelineType type = PipelineType::kCompute;
363 Result r = ToPipelineType(token->AsString(), &type);
364 if (!r.IsSuccess())
365 return r;
366
367 auto pipeline = MakeUnique<Pipeline>(type);
368
369 token = tokenizer_->NextToken();
370 if (!token->IsString())
371 return Result("invalid token when looking for pipeline name");
372
373 pipeline->SetName(token->AsString());
374
375 r = ValidateEndOfStatement("PIPELINE command");
376 if (!r.IsSuccess())
377 return r;
378
379 return ParsePipelineBody("PIPELINE", std::move(pipeline));
380 }
381
ParsePipelineBody(const std::string & cmd_name,std::unique_ptr<Pipeline> pipeline)382 Result Parser::ParsePipelineBody(const std::string& cmd_name,
383 std::unique_ptr<Pipeline> pipeline) {
384 std::unique_ptr<Token> token;
385 for (token = tokenizer_->NextToken(); !token->IsEOS();
386 token = tokenizer_->NextToken()) {
387 if (token->IsEOL())
388 continue;
389 if (!token->IsString())
390 return Result("expected string");
391
392 Result r;
393 std::string tok = token->AsString();
394 if (tok == "END") {
395 break;
396 } else if (tok == "ATTACH") {
397 r = ParsePipelineAttach(pipeline.get());
398 } else if (tok == "SHADER_OPTIMIZATION") {
399 r = ParsePipelineShaderOptimizations(pipeline.get());
400 } else if (tok == "FRAMEBUFFER_SIZE") {
401 r = ParsePipelineFramebufferSize(pipeline.get());
402 } else if (tok == "BIND") {
403 r = ParsePipelineBind(pipeline.get());
404 } else if (tok == "VERTEX_DATA") {
405 r = ParsePipelineVertexData(pipeline.get());
406 } else if (tok == "INDEX_DATA") {
407 r = ParsePipelineIndexData(pipeline.get());
408 } else if (tok == "SET") {
409 r = ParsePipelineSet(pipeline.get());
410 } else if (tok == "COMPILE_OPTIONS") {
411 r = ParsePipelineShaderCompileOptions(pipeline.get());
412 } else {
413 r = Result("unknown token in pipeline block: " + tok);
414 }
415 if (!r.IsSuccess())
416 return r;
417 }
418
419 if (!token->IsString() || token->AsString() != "END")
420 return Result(cmd_name + " missing END command");
421
422 Result r = script_->AddPipeline(std::move(pipeline));
423 if (!r.IsSuccess())
424 return r;
425
426 return ValidateEndOfStatement("END");
427 }
428
ParsePipelineAttach(Pipeline * pipeline)429 Result Parser::ParsePipelineAttach(Pipeline* pipeline) {
430 auto token = tokenizer_->NextToken();
431 if (!token->IsString())
432 return Result("invalid token in ATTACH command");
433
434 auto* shader = script_->GetShader(token->AsString());
435 if (!shader)
436 return Result("unknown shader in ATTACH command");
437
438 token = tokenizer_->NextToken();
439 if (token->IsEOL() || token->IsEOS()) {
440 if (shader->GetType() == kShaderTypeMulti)
441 return Result("multi shader ATTACH requires TYPE");
442
443 Result r = pipeline->AddShader(shader, shader->GetType());
444 if (!r.IsSuccess())
445 return r;
446 return {};
447 }
448 if (!token->IsString())
449 return Result("invalid token after ATTACH");
450
451 bool set_shader_type = false;
452 ShaderType shader_type = shader->GetType();
453 auto type = token->AsString();
454 if (type == "TYPE") {
455 token = tokenizer_->NextToken();
456 if (!token->IsString())
457 return Result("invalid type in ATTACH");
458
459 Result r = ToShaderType(token->AsString(), &shader_type);
460 if (!r.IsSuccess())
461 return r;
462
463 set_shader_type = true;
464
465 token = tokenizer_->NextToken();
466 if (!token->IsString())
467 return Result("ATTACH TYPE requires an ENTRY_POINT");
468
469 type = token->AsString();
470 }
471 if (set_shader_type && type != "ENTRY_POINT")
472 return Result("unknown ATTACH parameter: " + type);
473
474 if (shader->GetType() == ShaderType::kShaderTypeMulti && !set_shader_type)
475 return Result("ATTACH missing TYPE for multi shader");
476
477 Result r = pipeline->AddShader(shader, shader_type);
478 if (!r.IsSuccess())
479 return r;
480
481 if (type == "ENTRY_POINT") {
482 token = tokenizer_->NextToken();
483 if (!token->IsString())
484 return Result("missing shader name in ATTACH ENTRY_POINT command");
485
486 r = pipeline->SetShaderEntryPoint(shader, token->AsString());
487 if (!r.IsSuccess())
488 return r;
489
490 token = tokenizer_->NextToken();
491 }
492
493 while (true) {
494 if (token->IsString() && token->AsString() == "SPECIALIZE") {
495 r = ParseShaderSpecialization(pipeline);
496 if (!r.IsSuccess())
497 return r;
498
499 token = tokenizer_->NextToken();
500 } else {
501 if (token->IsEOL() || token->IsEOS())
502 return {};
503 if (token->IsString())
504 return Result("unknown ATTACH parameter: " + token->AsString());
505 return Result("extra parameters after ATTACH command");
506 }
507 }
508 }
509
ParseShaderSpecialization(Pipeline * pipeline)510 Result Parser::ParseShaderSpecialization(Pipeline* pipeline) {
511 auto token = tokenizer_->NextToken();
512 if (!token->IsInteger())
513 return Result("specialization ID must be an integer");
514
515 auto spec_id = token->AsUint32();
516
517 token = tokenizer_->NextToken();
518 if (!token->IsString() || token->AsString() != "AS")
519 return Result("expected AS as next token");
520
521 token = tokenizer_->NextToken();
522 if (!token->IsString())
523 return Result("expected data type in SPECIALIZE subcommand");
524
525 auto type = ToType(token->AsString());
526 if (!type)
527 return Result("invalid data type '" + token->AsString() + "' provided");
528 if (!type->IsNumber())
529 return Result("only numeric types are accepted for specialization values");
530
531 auto num = type->AsNumber();
532
533 token = tokenizer_->NextToken();
534 uint32_t value = 0;
535 if (type::Type::IsUint32(num->GetFormatMode(), num->NumBits()) ||
536 type::Type::IsInt32(num->GetFormatMode(), num->NumBits())) {
537 value = token->AsUint32();
538 } else if (type::Type::IsFloat32(num->GetFormatMode(), num->NumBits())) {
539 Result r = token->ConvertToDouble();
540 if (!r.IsSuccess())
541 return Result("value is not a floating point value");
542
543 union {
544 uint32_t u;
545 float f;
546 } u;
547 u.f = token->AsFloat();
548 value = u.u;
549 } else {
550 return Result(
551 "only 32-bit types are currently accepted for specialization values");
552 }
553
554 auto& shader = pipeline->GetShaders()[pipeline->GetShaders().size() - 1];
555 shader.AddSpecialization(spec_id, value);
556 return {};
557 }
558
ParsePipelineShaderOptimizations(Pipeline * pipeline)559 Result Parser::ParsePipelineShaderOptimizations(Pipeline* pipeline) {
560 auto token = tokenizer_->NextToken();
561 if (!token->IsString())
562 return Result("missing shader name in SHADER_OPTIMIZATION command");
563
564 auto* shader = script_->GetShader(token->AsString());
565 if (!shader)
566 return Result("unknown shader in SHADER_OPTIMIZATION command");
567
568 token = tokenizer_->NextToken();
569 if (!token->IsEOL())
570 return Result("extra parameters after SHADER_OPTIMIZATION command");
571
572 std::vector<std::string> optimizations;
573 while (true) {
574 token = tokenizer_->NextToken();
575 if (token->IsEOL())
576 continue;
577 if (token->IsEOS())
578 return Result("SHADER_OPTIMIZATION missing END command");
579 if (!token->IsString())
580 return Result("SHADER_OPTIMIZATION options must be strings");
581 if (token->AsString() == "END")
582 break;
583
584 optimizations.push_back(token->AsString());
585 }
586
587 Result r = pipeline->SetShaderOptimizations(shader, optimizations);
588 if (!r.IsSuccess())
589 return r;
590
591 return ValidateEndOfStatement("SHADER_OPTIMIZATION command");
592 }
593
ParsePipelineShaderCompileOptions(Pipeline * pipeline)594 Result Parser::ParsePipelineShaderCompileOptions(Pipeline* pipeline) {
595 auto token = tokenizer_->NextToken();
596 if (!token->IsString())
597 return Result("missing shader name in COMPILE_OPTIONS command");
598
599 auto* shader = script_->GetShader(token->AsString());
600 if (!shader)
601 return Result("unknown shader in COMPILE_OPTIONS command");
602
603 if (shader->GetFormat() != kShaderFormatOpenCLC) {
604 return Result("COMPILE_OPTIONS currently only supports OPENCL-C shaders");
605 }
606
607 token = tokenizer_->NextToken();
608 if (!token->IsEOL())
609 return Result("extra parameters after COMPILE_OPTIONS command");
610
611 std::vector<std::string> options;
612 while (true) {
613 token = tokenizer_->NextToken();
614 if (token->IsEOL())
615 continue;
616 if (token->IsEOS())
617 return Result("COMPILE_OPTIONS missing END command");
618 if (token->AsString() == "END")
619 break;
620
621 options.push_back(token->AsString());
622 }
623
624 Result r = pipeline->SetShaderCompileOptions(shader, options);
625 if (!r.IsSuccess())
626 return r;
627
628 return ValidateEndOfStatement("COMPILE_OPTIONS command");
629 }
630
ParsePipelineFramebufferSize(Pipeline * pipeline)631 Result Parser::ParsePipelineFramebufferSize(Pipeline* pipeline) {
632 auto token = tokenizer_->NextToken();
633 if (token->IsEOL() || token->IsEOS())
634 return Result("missing size for FRAMEBUFFER_SIZE command");
635 if (!token->IsInteger())
636 return Result("invalid width for FRAMEBUFFER_SIZE command");
637
638 pipeline->SetFramebufferWidth(token->AsUint32());
639
640 token = tokenizer_->NextToken();
641 if (token->IsEOL() || token->IsEOS())
642 return Result("missing height for FRAMEBUFFER_SIZE command");
643 if (!token->IsInteger())
644 return Result("invalid height for FRAMEBUFFER_SIZE command");
645
646 pipeline->SetFramebufferHeight(token->AsUint32());
647
648 return ValidateEndOfStatement("FRAMEBUFFER_SIZE command");
649 }
650
ToBufferType(const std::string & name,BufferType * type)651 Result Parser::ToBufferType(const std::string& name, BufferType* type) {
652 assert(type);
653 if (name == "uniform")
654 *type = BufferType::kUniform;
655 else if (name == "storage")
656 *type = BufferType::kStorage;
657 else
658 return Result("unknown buffer_type: " + name);
659
660 return {};
661 }
662
ParsePipelineBind(Pipeline * pipeline)663 Result Parser::ParsePipelineBind(Pipeline* pipeline) {
664 auto token = tokenizer_->NextToken();
665 if (!token->IsString())
666 return Result("missing BUFFER in BIND command");
667 if (token->AsString() != "BUFFER")
668 return Result("missing BUFFER in BIND command");
669
670 token = tokenizer_->NextToken();
671 if (!token->IsString())
672 return Result("missing buffer name in BIND command");
673
674 auto* buffer = script_->GetBuffer(token->AsString());
675 if (!buffer)
676 return Result("unknown buffer: " + token->AsString());
677
678 token = tokenizer_->NextToken();
679 if (token->IsString() && token->AsString() == "AS") {
680 token = tokenizer_->NextToken();
681 if (!token->IsString())
682 return Result("invalid token for BUFFER type");
683
684 if (token->AsString() == "color") {
685 token = tokenizer_->NextToken();
686 if (!token->IsString() || token->AsString() != "LOCATION")
687 return Result("BIND missing LOCATION");
688
689 token = tokenizer_->NextToken();
690 if (!token->IsInteger())
691 return Result("invalid value for BIND LOCATION");
692
693 buffer->SetBufferType(BufferType::kColor);
694
695 Result r = pipeline->AddColorAttachment(buffer, token->AsUint32());
696 if (!r.IsSuccess())
697 return r;
698 } else if (token->AsString() == "depth_stencil") {
699 buffer->SetBufferType(BufferType::kDepth);
700 Result r = pipeline->SetDepthBuffer(buffer);
701 if (!r.IsSuccess())
702 return r;
703 } else if (token->AsString() == "push_constant") {
704 buffer->SetBufferType(BufferType::kPushConstant);
705 Result r = pipeline->SetPushConstantBuffer(buffer);
706 if (!r.IsSuccess())
707 return r;
708 } else {
709 BufferType type = BufferType::kColor;
710 Result r = ToBufferType(token->AsString(), &type);
711 if (!r.IsSuccess())
712 return r;
713
714 if (buffer->GetBufferType() == BufferType::kUnknown)
715 buffer->SetBufferType(type);
716 else if (buffer->GetBufferType() != type)
717 return Result("buffer type does not match intended usage");
718 }
719 }
720
721 if (buffer->GetBufferType() == BufferType::kUnknown ||
722 buffer->GetBufferType() == BufferType::kStorage ||
723 buffer->GetBufferType() == BufferType::kUniform) {
724 // If AS was parsed above consume the next token.
725 if (buffer->GetBufferType() != BufferType::kUnknown)
726 token = tokenizer_->NextToken();
727 // DESCRIPTOR_SET requires a buffer type to have been specified.
728 if (buffer->GetBufferType() != BufferType::kUnknown && token->IsString() &&
729 token->AsString() == "DESCRIPTOR_SET") {
730 token = tokenizer_->NextToken();
731 if (!token->IsInteger())
732 return Result("invalid value for DESCRIPTOR_SET in BIND command");
733 uint32_t descriptor_set = token->AsUint32();
734
735 token = tokenizer_->NextToken();
736 if (!token->IsString() || token->AsString() != "BINDING")
737 return Result("missing BINDING for BIND command");
738
739 token = tokenizer_->NextToken();
740 if (!token->IsInteger())
741 return Result("invalid value for BINDING in BIND command");
742 pipeline->AddBuffer(buffer, descriptor_set, token->AsUint32());
743 } else if (token->IsString() && token->AsString() == "KERNEL") {
744 token = tokenizer_->NextToken();
745 if (!token->IsString())
746 return Result("missing kernel arg identifier");
747
748 if (token->AsString() == "ARG_NAME") {
749 token = tokenizer_->NextToken();
750 if (!token->IsString())
751 return Result("expected argument identifier");
752
753 pipeline->AddBuffer(buffer, token->AsString());
754 } else if (token->AsString() == "ARG_NUMBER") {
755 token = tokenizer_->NextToken();
756 if (!token->IsInteger())
757 return Result("expected argument number");
758
759 pipeline->AddBuffer(buffer, token->AsUint32());
760 } else {
761 return Result("missing ARG_NAME or ARG_NUMBER keyword");
762 }
763 } else {
764 return Result("missing DESCRIPTOR_SET or KERNEL for BIND command");
765 }
766 }
767
768 return ValidateEndOfStatement("BIND command");
769 }
770
ParsePipelineVertexData(Pipeline * pipeline)771 Result Parser::ParsePipelineVertexData(Pipeline* pipeline) {
772 auto token = tokenizer_->NextToken();
773 if (!token->IsString())
774 return Result("missing buffer name in VERTEX_DATA command");
775
776 auto* buffer = script_->GetBuffer(token->AsString());
777 if (!buffer)
778 return Result("unknown buffer: " + token->AsString());
779
780 token = tokenizer_->NextToken();
781 if (!token->IsString() || token->AsString() != "LOCATION")
782 return Result("VERTEX_DATA missing LOCATION");
783
784 token = tokenizer_->NextToken();
785 if (!token->IsInteger())
786 return Result("invalid value for VERTEX_DATA LOCATION");
787
788 buffer->SetBufferType(BufferType::kVertex);
789 Result r = pipeline->AddVertexBuffer(buffer, token->AsUint32());
790 if (!r.IsSuccess())
791 return r;
792
793 return ValidateEndOfStatement("VERTEX_DATA command");
794 }
795
ParsePipelineIndexData(Pipeline * pipeline)796 Result Parser::ParsePipelineIndexData(Pipeline* pipeline) {
797 auto token = tokenizer_->NextToken();
798 if (!token->IsString())
799 return Result("missing buffer name in INDEX_DATA command");
800
801 auto* buffer = script_->GetBuffer(token->AsString());
802 if (!buffer)
803 return Result("unknown buffer: " + token->AsString());
804
805 buffer->SetBufferType(BufferType::kIndex);
806 Result r = pipeline->SetIndexBuffer(buffer);
807 if (!r.IsSuccess())
808 return r;
809
810 return ValidateEndOfStatement("INDEX_DATA command");
811 }
812
ParsePipelineSet(Pipeline * pipeline)813 Result Parser::ParsePipelineSet(Pipeline* pipeline) {
814 if (pipeline->GetShaders().empty() ||
815 pipeline->GetShaders()[0].GetShader()->GetFormat() !=
816 kShaderFormatOpenCLC) {
817 return Result("SET can only be used with OPENCL-C shaders");
818 }
819
820 auto token = tokenizer_->NextToken();
821 if (!token->IsString() || token->AsString() != "KERNEL")
822 return Result("missing KERNEL in SET command");
823
824 token = tokenizer_->NextToken();
825 if (!token->IsString())
826 return Result("expected ARG_NAME or ARG_NUMBER");
827
828 std::string arg_name = "";
829 uint32_t arg_no = std::numeric_limits<uint32_t>::max();
830 if (token->AsString() == "ARG_NAME") {
831 token = tokenizer_->NextToken();
832 if (!token->IsString())
833 return Result("expected argument identifier");
834
835 arg_name = token->AsString();
836 } else if (token->AsString() == "ARG_NUMBER") {
837 token = tokenizer_->NextToken();
838 if (!token->IsInteger())
839 return Result("expected argument number");
840
841 arg_no = token->AsUint32();
842 } else {
843 return Result("expected ARG_NAME or ARG_NUMBER");
844 }
845
846 token = tokenizer_->NextToken();
847 if (!token->IsString() || token->AsString() != "AS")
848 return Result("missing AS in SET command");
849
850 token = tokenizer_->NextToken();
851 if (!token->IsString())
852 return Result("expected data type");
853
854 auto type = ToType(token->AsString());
855 if (!type)
856 return Result("invalid data type '" + token->AsString() + "' provided");
857
858 token = tokenizer_->NextToken();
859 if (!token->IsInteger() && !token->IsDouble())
860 return Result("expected data value");
861
862 auto fmt = MakeUnique<Format>(type.get());
863 Value value;
864 if (fmt->IsFloat32() || fmt->IsFloat64())
865 value.SetDoubleValue(token->AsDouble());
866 else
867 value.SetIntValue(token->AsUint64());
868
869 Pipeline::ArgSetInfo info;
870 info.name = arg_name;
871 info.ordinal = arg_no;
872 info.fmt = fmt.get();
873 info.value = value;
874 pipeline->SetArg(std::move(info));
875 script_->RegisterFormat(std::move(fmt));
876 script_->RegisterType(std::move(type));
877
878 return ValidateEndOfStatement("SET command");
879 }
880
ParseStruct()881 Result Parser::ParseStruct() {
882 auto token = tokenizer_->NextToken();
883 if (!token->IsString())
884 return Result("invalid STRUCT name provided");
885
886 auto struct_name = token->AsString();
887 if (struct_name == "STRIDE")
888 return Result("missing STRUCT name");
889
890 auto s = MakeUnique<type::Struct>();
891 auto type = s.get();
892
893 Result r = script_->AddType(struct_name, std::move(s));
894 if (!r.IsSuccess())
895 return r;
896
897 token = tokenizer_->NextToken();
898 if (token->IsString()) {
899 if (token->AsString() != "STRIDE")
900 return Result("invalid token in STRUCT definition");
901
902 token = tokenizer_->NextToken();
903 if (token->IsEOL() || token->IsEOS())
904 return Result("missing value for STRIDE");
905 if (!token->IsInteger())
906 return Result("invalid value for STRIDE");
907
908 type->SetStrideInBytes(token->AsUint32());
909 token = tokenizer_->NextToken();
910 }
911 if (!token->IsEOL()) {
912 return Result("extra token " + token->ToOriginalString() +
913 " after STRUCT header");
914 }
915
916 std::map<std::string, bool> seen;
917 for (;;) {
918 token = tokenizer_->NextToken();
919 if (!token->IsString())
920 return Result("invalid type for STRUCT member");
921 if (token->AsString() == "END")
922 break;
923
924 if (token->AsString() == struct_name)
925 return Result("recursive types are not allowed");
926
927 type::Type* member_type = script_->GetType(token->AsString());
928 if (!member_type) {
929 auto t = ToType(token->AsString());
930 if (!t) {
931 return Result("unknown type '" + token->AsString() +
932 "' for STRUCT member");
933 }
934
935 member_type = t.get();
936 script_->RegisterType(std::move(t));
937 }
938
939 token = tokenizer_->NextToken();
940 if (token->IsEOL())
941 return Result("missing name for STRUCT member");
942 if (!token->IsString())
943 return Result("invalid name for STRUCT member");
944
945 auto member_name = token->AsString();
946 if (seen.find(member_name) != seen.end())
947 return Result("duplicate name for STRUCT member");
948
949 seen[member_name] = true;
950
951 auto m = type->AddMember(member_type);
952 m->name = member_name;
953
954 token = tokenizer_->NextToken();
955 while (token->IsString()) {
956 if (token->AsString() == "OFFSET") {
957 token = tokenizer_->NextToken();
958 if (token->IsEOL())
959 return Result("missing value for STRUCT member OFFSET");
960 if (!token->IsInteger())
961 return Result("invalid value for STRUCT member OFFSET");
962
963 m->offset_in_bytes = token->AsInt32();
964 } else if (token->AsString() == "ARRAY_STRIDE") {
965 token = tokenizer_->NextToken();
966 if (token->IsEOL())
967 return Result("missing value for STRUCT member ARRAY_STRIDE");
968 if (!token->IsInteger())
969 return Result("invalid value for STRUCT member ARRAY_STRIDE");
970 if (!member_type->IsArray())
971 return Result("ARRAY_STRIDE only valid on array members");
972
973 m->array_stride_in_bytes = token->AsInt32();
974 } else if (token->AsString() == "MATRIX_STRIDE") {
975 token = tokenizer_->NextToken();
976 if (token->IsEOL())
977 return Result("missing value for STRUCT member MATRIX_STRIDE");
978 if (!token->IsInteger())
979 return Result("invalid value for STRUCT member MATRIX_STRIDE");
980 if (!member_type->IsMatrix())
981 return Result("MATRIX_STRIDE only valid on matrix members");
982
983 m->matrix_stride_in_bytes = token->AsInt32();
984 } else {
985 return Result("unknown param '" + token->AsString() +
986 "' for STRUCT member");
987 }
988
989 token = tokenizer_->NextToken();
990 }
991
992 if (!token->IsEOL())
993 return Result("extra param for STRUCT member");
994 }
995
996 return {};
997 }
998
ParseBuffer()999 Result Parser::ParseBuffer() {
1000 auto token = tokenizer_->NextToken();
1001 if (!token->IsString())
1002 return Result("invalid BUFFER name provided");
1003
1004 auto name = token->AsString();
1005 if (name == "DATA_TYPE" || name == "FORMAT")
1006 return Result("missing BUFFER name");
1007
1008 token = tokenizer_->NextToken();
1009 if (!token->IsString())
1010 return Result("invalid BUFFER command provided");
1011
1012 std::unique_ptr<Buffer> buffer;
1013 auto& cmd = token->AsString();
1014 if (cmd == "DATA_TYPE") {
1015 buffer = MakeUnique<Buffer>();
1016
1017 Result r = ParseBufferInitializer(buffer.get());
1018 if (!r.IsSuccess())
1019 return r;
1020 } else if (cmd == "FORMAT") {
1021 token = tokenizer_->NextToken();
1022 if (!token->IsString())
1023 return Result("BUFFER FORMAT must be a string");
1024
1025 buffer = MakeUnique<Buffer>();
1026
1027 auto type = script_->ParseType(token->AsString());
1028 if (!type)
1029 return Result("invalid BUFFER FORMAT");
1030
1031 auto fmt = MakeUnique<Format>(type);
1032 buffer->SetFormat(fmt.get());
1033 script_->RegisterFormat(std::move(fmt));
1034 } else {
1035 return Result("unknown BUFFER command provided: " + cmd);
1036 }
1037 buffer->SetName(name);
1038
1039 Result r = script_->AddBuffer(std::move(buffer));
1040 if (!r.IsSuccess())
1041 return r;
1042
1043 return {};
1044 }
1045
ParseBufferInitializer(Buffer * buffer)1046 Result Parser::ParseBufferInitializer(Buffer* buffer) {
1047 auto token = tokenizer_->NextToken();
1048 if (!token->IsString())
1049 return Result("BUFFER invalid data type");
1050
1051 auto type = script_->ParseType(token->AsString());
1052 std::unique_ptr<Format> fmt;
1053 if (type != nullptr) {
1054 fmt = MakeUnique<Format>(type);
1055 buffer->SetFormat(fmt.get());
1056 } else {
1057 auto new_type = ToType(token->AsString());
1058 if (!new_type)
1059 return Result("invalid data type '" + token->AsString() + "' provided");
1060
1061 fmt = MakeUnique<Format>(new_type.get());
1062 buffer->SetFormat(fmt.get());
1063 type = new_type.get();
1064 script_->RegisterType(std::move(new_type));
1065 }
1066 script_->RegisterFormat(std::move(fmt));
1067
1068 token = tokenizer_->NextToken();
1069 if (!token->IsString())
1070 return Result("BUFFER missing initializer");
1071
1072 if (token->AsString() == "STD140") {
1073 buffer->GetFormat()->SetLayout(Format::Layout::kStd140);
1074 token = tokenizer_->NextToken();
1075 } else if (token->AsString() == "STD430") {
1076 buffer->GetFormat()->SetLayout(Format::Layout::kStd430);
1077 token = tokenizer_->NextToken();
1078 }
1079
1080 if (!token->IsString())
1081 return Result("BUFFER missing initializer");
1082
1083 if (token->AsString() == "SIZE")
1084 return ParseBufferInitializerSize(buffer);
1085 if (token->AsString() == "DATA")
1086 return ParseBufferInitializerData(buffer);
1087
1088 return Result("unknown initializer for BUFFER");
1089 }
1090
ParseBufferInitializerSize(Buffer * buffer)1091 Result Parser::ParseBufferInitializerSize(Buffer* buffer) {
1092 auto token = tokenizer_->NextToken();
1093 if (token->IsEOS() || token->IsEOL())
1094 return Result("BUFFER size missing");
1095 if (!token->IsInteger())
1096 return Result("BUFFER size invalid");
1097
1098 uint32_t size_in_items = token->AsUint32();
1099 buffer->SetElementCount(size_in_items);
1100
1101 token = tokenizer_->NextToken();
1102 if (!token->IsString())
1103 return Result("BUFFER invalid initializer");
1104
1105 if (token->AsString() == "FILL")
1106 return ParseBufferInitializerFill(buffer, size_in_items);
1107 if (token->AsString() == "SERIES_FROM")
1108 return ParseBufferInitializerSeries(buffer, size_in_items);
1109
1110 return Result("invalid BUFFER initializer provided");
1111 }
1112
ParseBufferInitializerFill(Buffer * buffer,uint32_t size_in_items)1113 Result Parser::ParseBufferInitializerFill(Buffer* buffer,
1114 uint32_t size_in_items) {
1115 auto token = tokenizer_->NextToken();
1116 if (token->IsEOS() || token->IsEOL())
1117 return Result("missing BUFFER fill value");
1118 if (!token->IsInteger() && !token->IsDouble())
1119 return Result("invalid BUFFER fill value");
1120
1121 auto fmt = buffer->GetFormat();
1122 bool is_double_data = fmt->IsFloat32() || fmt->IsFloat64();
1123
1124 // Inflate the size because our items are multi-dimensional.
1125 size_in_items = size_in_items * fmt->InputNeededPerElement();
1126
1127 std::vector<Value> values;
1128 values.resize(size_in_items);
1129 for (size_t i = 0; i < size_in_items; ++i) {
1130 if (is_double_data)
1131 values[i].SetDoubleValue(token->AsDouble());
1132 else
1133 values[i].SetIntValue(token->AsUint64());
1134 }
1135 Result r = buffer->SetData(std::move(values));
1136 if (!r.IsSuccess())
1137 return r;
1138
1139 return ValidateEndOfStatement("BUFFER fill command");
1140 }
1141
ParseBufferInitializerSeries(Buffer * buffer,uint32_t size_in_items)1142 Result Parser::ParseBufferInitializerSeries(Buffer* buffer,
1143 uint32_t size_in_items) {
1144 auto token = tokenizer_->NextToken();
1145 if (token->IsEOS() || token->IsEOL())
1146 return Result("missing BUFFER series_from value");
1147 if (!token->IsInteger() && !token->IsDouble())
1148 return Result("invalid BUFFER series_from value");
1149
1150 auto type = buffer->GetFormat()->GetType();
1151 if (type->IsMatrix() || type->IsVec())
1152 return Result("BUFFER series_from must not be multi-row/column types");
1153
1154 Value counter;
1155
1156 auto n = type->AsNumber();
1157 FormatMode mode = n->GetFormatMode();
1158 uint32_t num_bits = n->NumBits();
1159 if (type::Type::IsFloat32(mode, num_bits) ||
1160 type::Type::IsFloat64(mode, num_bits)) {
1161 counter.SetDoubleValue(token->AsDouble());
1162 } else {
1163 counter.SetIntValue(token->AsUint64());
1164 }
1165
1166 token = tokenizer_->NextToken();
1167 if (!token->IsString())
1168 return Result("missing BUFFER series_from inc_by");
1169 if (token->AsString() != "INC_BY")
1170 return Result("BUFFER series_from invalid command");
1171
1172 token = tokenizer_->NextToken();
1173 if (token->IsEOS() || token->IsEOL())
1174 return Result("missing BUFFER series_from inc_by value");
1175 if (!token->IsInteger() && !token->IsDouble())
1176 return Result("invalid BUFFER series_from inc_by value");
1177
1178 std::vector<Value> values;
1179 values.resize(size_in_items);
1180 for (size_t i = 0; i < size_in_items; ++i) {
1181 if (type::Type::IsFloat32(mode, num_bits) ||
1182 type::Type::IsFloat64(mode, num_bits)) {
1183 double value = counter.AsDouble();
1184 values[i].SetDoubleValue(value);
1185 counter.SetDoubleValue(value + token->AsDouble());
1186 } else {
1187 uint64_t value = counter.AsUint64();
1188 values[i].SetIntValue(value);
1189 counter.SetIntValue(value + token->AsUint64());
1190 }
1191 }
1192 Result r = buffer->SetData(std::move(values));
1193 if (!r.IsSuccess())
1194 return r;
1195
1196 return ValidateEndOfStatement("BUFFER series_from command");
1197 }
1198
ParseBufferInitializerData(Buffer * buffer)1199 Result Parser::ParseBufferInitializerData(Buffer* buffer) {
1200 auto fmt = buffer->GetFormat();
1201 const auto& segs = fmt->GetSegments();
1202 size_t seg_idx = 0;
1203 uint32_t value_count = 0;
1204
1205 std::vector<Value> values;
1206 for (auto token = tokenizer_->NextToken();; token = tokenizer_->NextToken()) {
1207 if (token->IsEOL())
1208 continue;
1209 if (token->IsEOS())
1210 return Result("missing BUFFER END command");
1211 if (token->IsString() && token->AsString() == "END")
1212 break;
1213 if (!token->IsInteger() && !token->IsDouble() && !token->IsHex())
1214 return Result("invalid BUFFER data value: " + token->ToOriginalString());
1215
1216 while (segs[seg_idx].IsPadding()) {
1217 ++seg_idx;
1218 if (seg_idx >= segs.size())
1219 seg_idx = 0;
1220 }
1221
1222 Value v;
1223 if (type::Type::IsFloat(segs[seg_idx].GetFormatMode())) {
1224 token->ConvertToDouble();
1225
1226 double val = token->IsHex() ? static_cast<double>(token->AsHex())
1227 : token->AsDouble();
1228 v.SetDoubleValue(val);
1229 ++value_count;
1230 } else {
1231 if (token->IsDouble()) {
1232 return Result("invalid BUFFER data value: " +
1233 token->ToOriginalString());
1234 }
1235
1236 uint64_t val = token->IsHex() ? token->AsHex() : token->AsUint64();
1237 v.SetIntValue(val);
1238 ++value_count;
1239 }
1240 ++seg_idx;
1241 if (seg_idx >= segs.size())
1242 seg_idx = 0;
1243
1244 values.emplace_back(v);
1245 }
1246 // Write final padding bytes
1247 while (segs[seg_idx].IsPadding()) {
1248 ++seg_idx;
1249 if (seg_idx >= segs.size())
1250 break;
1251 }
1252
1253 buffer->SetValueCount(value_count);
1254 Result r = buffer->SetData(std::move(values));
1255 if (!r.IsSuccess())
1256 return r;
1257
1258 return ValidateEndOfStatement("BUFFER data command");
1259 }
1260
ParseRun()1261 Result Parser::ParseRun() {
1262 auto token = tokenizer_->NextToken();
1263 if (!token->IsString())
1264 return Result("missing pipeline name for RUN command");
1265
1266 size_t line = tokenizer_->GetCurrentLine();
1267
1268 auto* pipeline = script_->GetPipeline(token->AsString());
1269 if (!pipeline)
1270 return Result("unknown pipeline for RUN command: " + token->AsString());
1271
1272 token = tokenizer_->NextToken();
1273 if (token->IsEOL() || token->IsEOS())
1274 return Result("RUN command requires parameters");
1275
1276 if (token->IsInteger()) {
1277 if (!pipeline->IsCompute())
1278 return Result("RUN command requires compute pipeline");
1279
1280 auto cmd = MakeUnique<ComputeCommand>(pipeline);
1281 cmd->SetLine(line);
1282 cmd->SetX(token->AsUint32());
1283
1284 token = tokenizer_->NextToken();
1285 if (!token->IsInteger()) {
1286 return Result("invalid parameter for RUN command: " +
1287 token->ToOriginalString());
1288 }
1289 cmd->SetY(token->AsUint32());
1290
1291 token = tokenizer_->NextToken();
1292 if (!token->IsInteger()) {
1293 return Result("invalid parameter for RUN command: " +
1294 token->ToOriginalString());
1295 }
1296 cmd->SetZ(token->AsUint32());
1297
1298 command_list_.push_back(std::move(cmd));
1299 return ValidateEndOfStatement("RUN command");
1300 }
1301 if (!token->IsString())
1302 return Result("invalid token in RUN command: " + token->ToOriginalString());
1303
1304 if (token->AsString() == "DRAW_RECT") {
1305 if (!pipeline->IsGraphics())
1306 return Result("RUN command requires graphics pipeline");
1307
1308 if (pipeline->GetVertexBuffers().size() > 1) {
1309 return Result(
1310 "RUN DRAW_RECT is not supported in a pipeline with more than one "
1311 "vertex buffer attached");
1312 }
1313
1314 token = tokenizer_->NextToken();
1315 if (token->IsEOS() || token->IsEOL())
1316 return Result("RUN DRAW_RECT command requires parameters");
1317
1318 if (!token->IsString() || token->AsString() != "POS") {
1319 return Result("invalid token in RUN command: " +
1320 token->ToOriginalString() + "; expected POS");
1321 }
1322
1323 token = tokenizer_->NextToken();
1324 if (!token->IsInteger())
1325 return Result("missing X position for RUN command");
1326
1327 auto cmd = MakeUnique<DrawRectCommand>(pipeline, PipelineData{});
1328 cmd->SetLine(line);
1329 cmd->EnableOrtho();
1330
1331 Result r = token->ConvertToDouble();
1332 if (!r.IsSuccess())
1333 return r;
1334 cmd->SetX(token->AsFloat());
1335
1336 token = tokenizer_->NextToken();
1337 if (!token->IsInteger())
1338 return Result("missing Y position for RUN command");
1339
1340 r = token->ConvertToDouble();
1341 if (!r.IsSuccess())
1342 return r;
1343 cmd->SetY(token->AsFloat());
1344
1345 token = tokenizer_->NextToken();
1346 if (!token->IsString() || token->AsString() != "SIZE") {
1347 return Result("invalid token in RUN command: " +
1348 token->ToOriginalString() + "; expected SIZE");
1349 }
1350
1351 token = tokenizer_->NextToken();
1352 if (!token->IsInteger())
1353 return Result("missing width value for RUN command");
1354
1355 r = token->ConvertToDouble();
1356 if (!r.IsSuccess())
1357 return r;
1358 cmd->SetWidth(token->AsFloat());
1359
1360 token = tokenizer_->NextToken();
1361 if (!token->IsInteger())
1362 return Result("missing height value for RUN command");
1363
1364 r = token->ConvertToDouble();
1365 if (!r.IsSuccess())
1366 return r;
1367 cmd->SetHeight(token->AsFloat());
1368
1369 command_list_.push_back(std::move(cmd));
1370 return ValidateEndOfStatement("RUN command");
1371 }
1372
1373 if (token->AsString() == "DRAW_ARRAY") {
1374 if (!pipeline->IsGraphics())
1375 return Result("RUN command requires graphics pipeline");
1376
1377 if (pipeline->GetVertexBuffers().empty())
1378 return Result("RUN DRAW_ARRAY requires attached vertex buffer");
1379
1380 token = tokenizer_->NextToken();
1381 if (!token->IsString() || token->AsString() != "AS")
1382 return Result("missing AS for RUN command");
1383
1384 token = tokenizer_->NextToken();
1385 if (!token->IsString()) {
1386 return Result("invalid topology for RUN command: " +
1387 token->ToOriginalString());
1388 }
1389
1390 Topology topo = NameToTopology(token->AsString());
1391 if (topo == Topology::kUnknown)
1392 return Result("invalid topology for RUN command: " + token->AsString());
1393
1394 token = tokenizer_->NextToken();
1395 bool indexed = false;
1396 if (token->IsString() && token->AsString() == "INDEXED") {
1397 if (!pipeline->GetIndexBuffer())
1398 return Result("RUN DRAW_ARRAYS INDEXED requires attached index buffer");
1399
1400 indexed = true;
1401 token = tokenizer_->NextToken();
1402 }
1403
1404 uint32_t start_idx = 0;
1405 uint32_t count = 0;
1406 if (!token->IsEOS() && !token->IsEOL()) {
1407 if (!token->IsString() || token->AsString() != "START_IDX")
1408 return Result("missing START_IDX for RUN command");
1409
1410 token = tokenizer_->NextToken();
1411 if (!token->IsInteger()) {
1412 return Result("invalid START_IDX value for RUN command: " +
1413 token->ToOriginalString());
1414 }
1415 if (token->AsInt32() < 0)
1416 return Result("START_IDX value must be >= 0 for RUN command");
1417 start_idx = token->AsUint32();
1418
1419 token = tokenizer_->NextToken();
1420
1421 if (!token->IsEOS() && !token->IsEOL()) {
1422 if (!token->IsString() || token->AsString() != "COUNT")
1423 return Result("missing COUNT for RUN command");
1424
1425 token = tokenizer_->NextToken();
1426 if (!token->IsInteger()) {
1427 return Result("invalid COUNT value for RUN command: " +
1428 token->ToOriginalString());
1429 }
1430 if (token->AsInt32() <= 0)
1431 return Result("COUNT value must be > 0 for RUN command");
1432
1433 count = token->AsUint32();
1434 }
1435 }
1436 // If we get here then we never set count, as if count was set it must
1437 // be > 0.
1438 if (count == 0) {
1439 count =
1440 pipeline->GetVertexBuffers()[0].buffer->ElementCount() - start_idx;
1441 }
1442
1443 if (start_idx + count >
1444 pipeline->GetVertexBuffers()[0].buffer->ElementCount()) {
1445 return Result("START_IDX plus COUNT exceeds vertex buffer data size");
1446 }
1447
1448 auto cmd = MakeUnique<DrawArraysCommand>(pipeline, PipelineData{});
1449 cmd->SetLine(line);
1450 cmd->SetTopology(topo);
1451 cmd->SetFirstVertexIndex(start_idx);
1452 cmd->SetVertexCount(count);
1453
1454 if (indexed)
1455 cmd->EnableIndexed();
1456
1457 command_list_.push_back(std::move(cmd));
1458 return ValidateEndOfStatement("RUN command");
1459 }
1460
1461 return Result("invalid token in RUN command: " + token->AsString());
1462 }
1463
ParseClear()1464 Result Parser::ParseClear() {
1465 auto token = tokenizer_->NextToken();
1466 if (!token->IsString())
1467 return Result("missing pipeline name for CLEAR command");
1468
1469 size_t line = tokenizer_->GetCurrentLine();
1470
1471 auto* pipeline = script_->GetPipeline(token->AsString());
1472 if (!pipeline)
1473 return Result("unknown pipeline for CLEAR command: " + token->AsString());
1474 if (!pipeline->IsGraphics())
1475 return Result("CLEAR command requires graphics pipeline");
1476
1477 auto cmd = MakeUnique<ClearCommand>(pipeline);
1478 cmd->SetLine(line);
1479 command_list_.push_back(std::move(cmd));
1480
1481 return ValidateEndOfStatement("CLEAR command");
1482 }
1483
ParseValues(const std::string & name,Format * fmt,std::vector<Value> * values)1484 Result Parser::ParseValues(const std::string& name,
1485 Format* fmt,
1486 std::vector<Value>* values) {
1487 assert(values);
1488
1489 auto token = tokenizer_->NextToken();
1490 const auto& segs = fmt->GetSegments();
1491 size_t seg_idx = 0;
1492 while (!token->IsEOL() && !token->IsEOS()) {
1493 Value v;
1494
1495 while (segs[seg_idx].IsPadding()) {
1496 ++seg_idx;
1497 if (seg_idx >= segs.size())
1498 seg_idx = 0;
1499 }
1500
1501 if (type::Type::IsFloat(segs[seg_idx].GetFormatMode())) {
1502 if (!token->IsInteger() && !token->IsDouble()) {
1503 return Result(std::string("Invalid value provided to ") + name +
1504 " command: " + token->ToOriginalString());
1505 }
1506
1507 Result r = token->ConvertToDouble();
1508 if (!r.IsSuccess())
1509 return r;
1510
1511 v.SetDoubleValue(token->AsDouble());
1512 } else {
1513 if (!token->IsInteger()) {
1514 return Result(std::string("Invalid value provided to ") + name +
1515 " command: " + token->ToOriginalString());
1516 }
1517
1518 v.SetIntValue(token->AsUint64());
1519 }
1520 ++seg_idx;
1521 if (seg_idx >= segs.size())
1522 seg_idx = 0;
1523
1524 values->push_back(v);
1525 token = tokenizer_->NextToken();
1526 }
1527 return {};
1528 }
1529
ParseExpect()1530 Result Parser::ParseExpect() {
1531 auto token = tokenizer_->NextToken();
1532 if (!token->IsString())
1533 return Result("invalid buffer name in EXPECT command");
1534
1535 if (token->AsString() == "IDX")
1536 return Result("missing buffer name between EXPECT and IDX");
1537 if (token->AsString() == "EQ_BUFFER")
1538 return Result("missing buffer name between EXPECT and EQ_BUFFER");
1539 if (token->AsString() == "RMSE_BUFFER")
1540 return Result("missing buffer name between EXPECT and RMSE_BUFFER");
1541 if (token->AsString() == "EQ_HISTOGRAM_EMD_BUFFER") {
1542 return Result(
1543 "missing buffer name between EXPECT and EQ_HISTOGRAM_EMD_BUFFER");
1544 }
1545
1546 size_t line = tokenizer_->GetCurrentLine();
1547 auto* buffer = script_->GetBuffer(token->AsString());
1548 if (!buffer)
1549 return Result("unknown buffer name for EXPECT command: " +
1550 token->AsString());
1551
1552 token = tokenizer_->NextToken();
1553
1554 if (!token->IsString())
1555 return Result("invalid comparator in EXPECT command");
1556
1557 if (token->AsString() == "EQ_BUFFER" || token->AsString() == "RMSE_BUFFER" ||
1558 token->AsString() == "EQ_HISTOGRAM_EMD_BUFFER") {
1559 auto type = token->AsString();
1560
1561 token = tokenizer_->NextToken();
1562 if (!token->IsString())
1563 return Result("invalid buffer name in EXPECT " + type + " command");
1564
1565 auto* buffer_2 = script_->GetBuffer(token->AsString());
1566 if (!buffer_2) {
1567 return Result("unknown buffer name for EXPECT " + type +
1568 " command: " + token->AsString());
1569 }
1570
1571 if (!buffer->GetFormat()->Equal(buffer_2->GetFormat())) {
1572 return Result("EXPECT " + type +
1573 " command cannot compare buffers of differing format");
1574 }
1575 if (buffer->ElementCount() != buffer_2->ElementCount()) {
1576 return Result("EXPECT " + type +
1577 " command cannot compare buffers of different size: " +
1578 std::to_string(buffer->ElementCount()) + " vs " +
1579 std::to_string(buffer_2->ElementCount()));
1580 }
1581 if (buffer->GetWidth() != buffer_2->GetWidth()) {
1582 return Result("EXPECT " + type +
1583 " command cannot compare buffers of different width");
1584 }
1585 if (buffer->GetHeight() != buffer_2->GetHeight()) {
1586 return Result("EXPECT " + type +
1587 " command cannot compare buffers of different height");
1588 }
1589
1590 auto cmd = MakeUnique<CompareBufferCommand>(buffer, buffer_2);
1591 if (type == "RMSE_BUFFER") {
1592 cmd->SetComparator(CompareBufferCommand::Comparator::kRmse);
1593
1594 token = tokenizer_->NextToken();
1595 if (!token->IsString() && token->AsString() == "TOLERANCE")
1596 return Result("missing TOLERANCE for EXPECT RMSE_BUFFER");
1597
1598 token = tokenizer_->NextToken();
1599 if (!token->IsInteger() && !token->IsDouble())
1600 return Result("invalid TOLERANCE for EXPECT RMSE_BUFFER");
1601
1602 Result r = token->ConvertToDouble();
1603 if (!r.IsSuccess())
1604 return r;
1605
1606 cmd->SetTolerance(token->AsFloat());
1607 } else if (type == "EQ_HISTOGRAM_EMD_BUFFER") {
1608 cmd->SetComparator(CompareBufferCommand::Comparator::kHistogramEmd);
1609
1610 token = tokenizer_->NextToken();
1611 if (!token->IsString() && token->AsString() == "TOLERANCE")
1612 return Result("missing TOLERANCE for EXPECT EQ_HISTOGRAM_EMD_BUFFER");
1613
1614 token = tokenizer_->NextToken();
1615 if (!token->IsInteger() && !token->IsDouble())
1616 return Result("invalid TOLERANCE for EXPECT EQ_HISTOGRAM_EMD_BUFFER");
1617
1618 Result r = token->ConvertToDouble();
1619 if (!r.IsSuccess())
1620 return r;
1621
1622 cmd->SetTolerance(token->AsFloat());
1623 }
1624
1625 command_list_.push_back(std::move(cmd));
1626
1627 // Early return
1628 return ValidateEndOfStatement("EXPECT " + type + " command");
1629 }
1630
1631 if (token->AsString() != "IDX")
1632 return Result("missing IDX in EXPECT command");
1633
1634 token = tokenizer_->NextToken();
1635 if (!token->IsInteger() || token->AsInt32() < 0)
1636 return Result("invalid X value in EXPECT command");
1637 token->ConvertToDouble();
1638 float x = token->AsFloat();
1639
1640 bool has_y_val = false;
1641 float y = 0;
1642 token = tokenizer_->NextToken();
1643 if (token->IsInteger()) {
1644 has_y_val = true;
1645
1646 if (token->AsInt32() < 0)
1647 return Result("invalid Y value in EXPECT command");
1648 token->ConvertToDouble();
1649 y = token->AsFloat();
1650
1651 token = tokenizer_->NextToken();
1652 }
1653
1654 if (token->IsString() && token->AsString() == "SIZE") {
1655 if (!has_y_val)
1656 return Result("invalid Y value in EXPECT command");
1657
1658 auto probe = MakeUnique<ProbeCommand>(buffer);
1659 probe->SetLine(line);
1660 probe->SetX(x);
1661 probe->SetY(y);
1662 probe->SetProbeRect();
1663
1664 token = tokenizer_->NextToken();
1665 if (!token->IsInteger() || token->AsInt32() <= 0)
1666 return Result("invalid width in EXPECT command");
1667 token->ConvertToDouble();
1668 probe->SetWidth(token->AsFloat());
1669
1670 token = tokenizer_->NextToken();
1671 if (!token->IsInteger() || token->AsInt32() <= 0)
1672 return Result("invalid height in EXPECT command");
1673 token->ConvertToDouble();
1674 probe->SetHeight(token->AsFloat());
1675
1676 token = tokenizer_->NextToken();
1677 if (!token->IsString()) {
1678 return Result("invalid token in EXPECT command:" +
1679 token->ToOriginalString());
1680 }
1681
1682 if (token->AsString() == "EQ_RGBA") {
1683 probe->SetIsRGBA();
1684 } else if (token->AsString() != "EQ_RGB") {
1685 return Result("unknown comparator type in EXPECT: " +
1686 token->ToOriginalString());
1687 }
1688
1689 token = tokenizer_->NextToken();
1690 if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255)
1691 return Result("invalid R value in EXPECT command");
1692 token->ConvertToDouble();
1693 probe->SetR(token->AsFloat() / 255.f);
1694
1695 token = tokenizer_->NextToken();
1696 if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255)
1697 return Result("invalid G value in EXPECT command");
1698 token->ConvertToDouble();
1699 probe->SetG(token->AsFloat() / 255.f);
1700
1701 token = tokenizer_->NextToken();
1702 if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255)
1703 return Result("invalid B value in EXPECT command");
1704 token->ConvertToDouble();
1705 probe->SetB(token->AsFloat() / 255.f);
1706
1707 if (probe->IsRGBA()) {
1708 token = tokenizer_->NextToken();
1709 if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255)
1710 return Result("invalid A value in EXPECT command");
1711 token->ConvertToDouble();
1712 probe->SetA(token->AsFloat() / 255.f);
1713 }
1714
1715 command_list_.push_back(std::move(probe));
1716 return ValidateEndOfStatement("EXPECT command");
1717 }
1718
1719 auto probe = MakeUnique<ProbeSSBOCommand>(buffer);
1720 probe->SetLine(line);
1721
1722 if (token->IsString() && token->AsString() == "TOLERANCE") {
1723 std::vector<Probe::Tolerance> tolerances;
1724
1725 token = tokenizer_->NextToken();
1726 while (!token->IsEOL() && !token->IsEOS()) {
1727 if (!token->IsInteger() && !token->IsDouble())
1728 break;
1729
1730 Result r = token->ConvertToDouble();
1731 if (!r.IsSuccess())
1732 return r;
1733
1734 double value = token->AsDouble();
1735 token = tokenizer_->NextToken();
1736 if (token->IsString() && token->AsString() == "%") {
1737 tolerances.push_back(Probe::Tolerance{true, value});
1738 token = tokenizer_->NextToken();
1739 } else {
1740 tolerances.push_back(Probe::Tolerance{false, value});
1741 }
1742 }
1743 if (tolerances.empty())
1744 return Result("TOLERANCE specified but no tolerances provided");
1745 if (tolerances.size() > 4)
1746 return Result("TOLERANCE has a maximum of 4 values");
1747
1748 probe->SetTolerances(std::move(tolerances));
1749 }
1750
1751 if (!token->IsString() || !IsComparator(token->AsString())) {
1752 return Result("unexpected token in EXPECT command: " +
1753 token->ToOriginalString());
1754 }
1755
1756 if (has_y_val)
1757 return Result("Y value not needed for non-color comparator");
1758
1759 auto cmp = ToComparator(token->AsString());
1760 if (probe->HasTolerances()) {
1761 if (cmp != ProbeSSBOCommand::Comparator::kEqual)
1762 return Result("TOLERANCE only available with EQ probes");
1763
1764 cmp = ProbeSSBOCommand::Comparator::kFuzzyEqual;
1765 }
1766
1767 probe->SetComparator(cmp);
1768 probe->SetFormat(buffer->GetFormat());
1769 probe->SetOffset(static_cast<uint32_t>(x));
1770
1771 std::vector<Value> values;
1772 Result r = ParseValues("EXPECT", buffer->GetFormat(), &values);
1773 if (!r.IsSuccess())
1774 return r;
1775
1776 if (values.empty())
1777 return Result("missing comparison values for EXPECT command");
1778
1779 probe->SetValues(std::move(values));
1780 command_list_.push_back(std::move(probe));
1781
1782 return {};
1783 }
1784
ParseCopy()1785 Result Parser::ParseCopy() {
1786 auto token = tokenizer_->NextToken();
1787 if (token->IsEOL() || token->IsEOS())
1788 return Result("missing buffer name after COPY");
1789 if (!token->IsString())
1790 return Result("invalid buffer name after COPY");
1791
1792 size_t line = tokenizer_->GetCurrentLine();
1793
1794 auto name = token->AsString();
1795 if (name == "TO")
1796 return Result("missing buffer name between COPY and TO");
1797
1798 Buffer* buffer_from = script_->GetBuffer(name);
1799 if (!buffer_from)
1800 return Result("COPY origin buffer was not declared");
1801
1802 token = tokenizer_->NextToken();
1803 if (token->IsEOL() || token->IsEOS())
1804 return Result("missing 'TO' after COPY and buffer name");
1805 if (!token->IsString())
1806 return Result("expected 'TO' after COPY and buffer name");
1807
1808 name = token->AsString();
1809 if (name != "TO")
1810 return Result("expected 'TO' after COPY and buffer name");
1811
1812 token = tokenizer_->NextToken();
1813 if (token->IsEOL() || token->IsEOS())
1814 return Result("missing buffer name after TO");
1815 if (!token->IsString())
1816 return Result("invalid buffer name after TO");
1817
1818 name = token->AsString();
1819 Buffer* buffer_to = script_->GetBuffer(name);
1820 if (!buffer_to)
1821 return Result("COPY destination buffer was not declared");
1822
1823 if (buffer_to->GetBufferType() == amber::BufferType::kUnknown) {
1824 // Set destination buffer to mirror origin buffer
1825 buffer_to->SetBufferType(buffer_from->GetBufferType());
1826 buffer_to->SetWidth(buffer_from->GetWidth());
1827 buffer_to->SetHeight(buffer_from->GetHeight());
1828 buffer_to->SetElementCount(buffer_from->ElementCount());
1829 }
1830
1831 if (buffer_from->GetBufferType() != buffer_to->GetBufferType())
1832 return Result("cannot COPY between buffers of different types");
1833 if (buffer_from == buffer_to)
1834 return Result("COPY origin and destination buffers are identical");
1835
1836 auto cmd = MakeUnique<CopyCommand>(buffer_from, buffer_to);
1837 cmd->SetLine(line);
1838 command_list_.push_back(std::move(cmd));
1839
1840 return ValidateEndOfStatement("COPY command");
1841 }
1842
ParseClearColor()1843 Result Parser::ParseClearColor() {
1844 auto token = tokenizer_->NextToken();
1845 if (!token->IsString())
1846 return Result("missing pipeline name for CLEAR_COLOR command");
1847
1848 size_t line = tokenizer_->GetCurrentLine();
1849
1850 auto* pipeline = script_->GetPipeline(token->AsString());
1851 if (!pipeline) {
1852 return Result("unknown pipeline for CLEAR_COLOR command: " +
1853 token->AsString());
1854 }
1855 if (!pipeline->IsGraphics()) {
1856 return Result("CLEAR_COLOR command requires graphics pipeline");
1857 }
1858
1859 auto cmd = MakeUnique<ClearColorCommand>(pipeline);
1860 cmd->SetLine(line);
1861
1862 token = tokenizer_->NextToken();
1863 if (token->IsEOL() || token->IsEOS())
1864 return Result("missing R value for CLEAR_COLOR command");
1865 if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255) {
1866 return Result("invalid R value for CLEAR_COLOR command: " +
1867 token->ToOriginalString());
1868 }
1869 token->ConvertToDouble();
1870 cmd->SetR(token->AsFloat() / 255.f);
1871
1872 token = tokenizer_->NextToken();
1873 if (token->IsEOL() || token->IsEOS())
1874 return Result("missing G value for CLEAR_COLOR command");
1875 if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255) {
1876 return Result("invalid G value for CLEAR_COLOR command: " +
1877 token->ToOriginalString());
1878 }
1879 token->ConvertToDouble();
1880 cmd->SetG(token->AsFloat() / 255.f);
1881
1882 token = tokenizer_->NextToken();
1883 if (token->IsEOL() || token->IsEOS())
1884 return Result("missing B value for CLEAR_COLOR command");
1885 if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255) {
1886 return Result("invalid B value for CLEAR_COLOR command: " +
1887 token->ToOriginalString());
1888 }
1889 token->ConvertToDouble();
1890 cmd->SetB(token->AsFloat() / 255.f);
1891
1892 token = tokenizer_->NextToken();
1893 if (token->IsEOL() || token->IsEOS())
1894 return Result("missing A value for CLEAR_COLOR command");
1895 if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255) {
1896 return Result("invalid A value for CLEAR_COLOR command: " +
1897 token->ToOriginalString());
1898 }
1899 token->ConvertToDouble();
1900 cmd->SetA(token->AsFloat() / 255.f);
1901
1902 command_list_.push_back(std::move(cmd));
1903 return ValidateEndOfStatement("CLEAR_COLOR command");
1904 }
1905
ParseDeviceFeature()1906 Result Parser::ParseDeviceFeature() {
1907 auto token = tokenizer_->NextToken();
1908 if (token->IsEOS() || token->IsEOL())
1909 return Result("missing feature name for DEVICE_FEATURE command");
1910 if (!token->IsString())
1911 return Result("invalid feature name for DEVICE_FEATURE command");
1912 if (!script_->IsKnownFeature(token->AsString()))
1913 return Result("unknown feature name for DEVICE_FEATURE command");
1914
1915 script_->AddRequiredFeature(token->AsString());
1916
1917 return ValidateEndOfStatement("DEVICE_FEATURE command");
1918 }
1919
ParseRepeat()1920 Result Parser::ParseRepeat() {
1921 auto token = tokenizer_->NextToken();
1922 if (token->IsEOL() || token->IsEOL())
1923 return Result("missing count parameter for REPEAT command");
1924 if (!token->IsInteger()) {
1925 return Result("invalid count parameter for REPEAT command: " +
1926 token->ToOriginalString());
1927 }
1928 if (token->AsInt32() <= 0)
1929 return Result("count parameter must be > 0 for REPEAT command");
1930
1931 uint32_t count = token->AsUint32();
1932
1933 std::vector<std::unique_ptr<Command>> cur_commands;
1934 std::swap(cur_commands, command_list_);
1935
1936 for (token = tokenizer_->NextToken(); !token->IsEOS();
1937 token = tokenizer_->NextToken()) {
1938 if (token->IsEOL())
1939 continue;
1940 if (!token->IsString())
1941 return Result("expected string");
1942
1943 std::string tok = token->AsString();
1944 if (tok == "END")
1945 break;
1946 if (!IsRepeatable(tok))
1947 return Result("unknown token: " + tok);
1948
1949 Result r = ParseRepeatableCommand(tok);
1950 if (!r.IsSuccess())
1951 return r;
1952 }
1953 if (!token->IsString() || token->AsString() != "END")
1954 return Result("missing END for REPEAT command");
1955
1956 auto cmd = MakeUnique<RepeatCommand>(count);
1957 cmd->SetCommands(std::move(command_list_));
1958
1959 std::swap(cur_commands, command_list_);
1960 command_list_.push_back(std::move(cmd));
1961
1962 return ValidateEndOfStatement("REPEAT command");
1963 }
1964
ParseDerivePipelineBlock()1965 Result Parser::ParseDerivePipelineBlock() {
1966 auto token = tokenizer_->NextToken();
1967 if (!token->IsString() || token->AsString() == "FROM")
1968 return Result("missing pipeline name for DERIVE_PIPELINE command");
1969
1970 std::string name = token->AsString();
1971 if (script_->GetPipeline(name) != nullptr)
1972 return Result("duplicate pipeline name for DERIVE_PIPELINE command");
1973
1974 token = tokenizer_->NextToken();
1975 if (!token->IsString() || token->AsString() != "FROM")
1976 return Result("missing FROM in DERIVE_PIPELINE command");
1977
1978 token = tokenizer_->NextToken();
1979 if (!token->IsString())
1980 return Result("missing parent pipeline name in DERIVE_PIPELINE command");
1981
1982 Pipeline* parent = script_->GetPipeline(token->AsString());
1983 if (!parent)
1984 return Result("unknown parent pipeline in DERIVE_PIPELINE command");
1985
1986 Result r = ValidateEndOfStatement("DERIVE_PIPELINE command");
1987 if (!r.IsSuccess())
1988 return r;
1989
1990 auto pipeline = parent->Clone();
1991 pipeline->SetName(name);
1992
1993 return ParsePipelineBody("DERIVE_PIPELINE", std::move(pipeline));
1994 }
1995
ParseDeviceExtension()1996 Result Parser::ParseDeviceExtension() {
1997 auto token = tokenizer_->NextToken();
1998 if (token->IsEOL() || token->IsEOS())
1999 return Result("DEVICE_EXTENSION missing name");
2000 if (!token->IsString()) {
2001 return Result("DEVICE_EXTENSION invalid name: " +
2002 token->ToOriginalString());
2003 }
2004
2005 script_->AddRequiredDeviceExtension(token->AsString());
2006
2007 return ValidateEndOfStatement("DEVICE_EXTENSION command");
2008 }
2009
ParseInstanceExtension()2010 Result Parser::ParseInstanceExtension() {
2011 auto token = tokenizer_->NextToken();
2012 if (token->IsEOL() || token->IsEOS())
2013 return Result("INSTANCE_EXTENSION missing name");
2014 if (!token->IsString()) {
2015 return Result("INSTANCE_EXTENSION invalid name: " +
2016 token->ToOriginalString());
2017 }
2018
2019 script_->AddRequiredInstanceExtension(token->AsString());
2020
2021 return ValidateEndOfStatement("INSTANCE_EXTENSION command");
2022 }
2023
ParseSet()2024 Result Parser::ParseSet() {
2025 auto token = tokenizer_->NextToken();
2026 if (!token->IsString() || token->AsString() != "ENGINE_DATA")
2027 return Result("SET missing ENGINE_DATA");
2028
2029 token = tokenizer_->NextToken();
2030 if (token->IsEOS() || token->IsEOL())
2031 return Result("SET missing variable to be set");
2032
2033 if (!token->IsString())
2034 return Result("SET invalid variable to set: " + token->ToOriginalString());
2035
2036 if (token->AsString() != "fence_timeout_ms")
2037 return Result("SET unknown variable provided: " + token->AsString());
2038
2039 token = tokenizer_->NextToken();
2040 if (token->IsEOS() || token->IsEOL())
2041 return Result("SET missing value for fence_timeout_ms");
2042 if (!token->IsInteger())
2043 return Result("SET invalid value for fence_timeout_ms, must be uint32");
2044
2045 script_->GetEngineData().fence_timeout_ms = token->AsUint32();
2046
2047 return ValidateEndOfStatement("SET command");
2048 }
2049
2050 } // namespace amberscript
2051 } // namespace amber
2052