• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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