// // File: foldhaus_assembly_parser.cpp // Author: Peter Slattery // Creation Date: 2020-01-01 // #ifndef FOLDHAUS_ASSEMBLY_PARSER_CPP // TODO(pjs): This is good for meta generation // ie. It would be great to have // // enum ident enum prefix // BEGIN_GEN_ENUM(assembly_field, AssemblyField_) // // value name gen string of the value name the paired string identifier // ADD_ENUM_VALUE(AssemblyName, DO_GEN_STRING, "assembly_name") // ADD_ENUM_VALUE(AssemblyScale, DO_GEN_STRING, "assembly_scale") // END_GEN_ENUM(assembly_field) enum assembly_field { AssemblyField_AssemblyName, AssemblyField_AssemblyScale, AssemblyField_AssemblyCenter, AssemblyField_LedStripCount, AssemblyField_OutputMode, AssemblyField_LedStrip, AssemblyField_OutputSACN, AssemblyField_SACN_StartUniverse, AssemblyField_SACN_StartChannel, AssemblyField_OutputUART, AssemblyField_UART_Channel, AssemblyField_UART_ComPort, AssemblyField_PointPlacementType, AssemblyField_InterpolatePoints, AssemblyField_Start, AssemblyField_End, AssemblyField_LedCount, AssemblyField_SegmentSequence, AssemblyField_SegmentSequenceLength, AssemblyField_Segment, AssemblyField_TagsCount, AssemblyField_Tag, AssemblyField_Name, AssemblyField_Value, AssemblyField_Count, }; global gs_const_string AssemblyFieldIdentifiers[] = { ConstString("assembly_name"), // AssemblyField_AssemblyName ConstString("assembly_scale"), // AssemblyField_AssemblyScale ConstString("assembly_center"), // AssemblyField_AssemblyCenter ConstString("led_strip_count"), // AssemblyField_LedStripCount ConstString("output_mode"), // AssemblyField_OutputMode ConstString("led_strip"), // AssemblyField_LedStrip ConstString("output_sacn"), // AssemblyField_OutputSACN ConstString("start_universe"), // AssemblyField_SACN_StartUniverse ConstString("start_channel"), // AssemblyField_SACN_StartChannel ConstString("output_uart"), // AssemblyField_OutputUART ConstString("channel"), // AssemblyField_UART_Channel ConstString("com_port"), // AssemblyField_UART_ComPort ConstString("point_placement_type"), // AssemblyField_PointPlacementType ConstString("interpolate_points"), // AssemblyField_InterpolatePoints ConstString("start"), // AssemblyField_Start ConstString("end"), // AssemblyField_End ConstString("led_count"), // AssemblyField_LedCount ConstString("segment_sequence"), // AssemblyField_SegmentSequence ConstString("segment_count"), // AssemblyField_SegmentSequenceLength ConstString("segment"), // AssemblyField_Segment ConstString("tags_count"), // AssemblyField_TagCount ConstString("tag"), // AssemblyField_Tag ConstString("name"), // AssemblyField_Name ConstString("value"), // AssemblyField_Value }; internal void StripSetTag(v2_strip* Strip, u32 TagIndex, gs_const_string TagName, gs_const_string TagValue) { Assert(TagIndex < Strip->TagsCount); v2_tag* TagAt = &Strip->Tags[TagIndex]; TagAt->NameHash = HashDJB2ToU32(StringExpand(TagName)); TagAt->ValueHash = HashDJB2ToU32(StringExpand(TagValue)); } internal strip_sacn_addr AssemblyParser_ReadSACNAddr(parser* Parser, assembly Assembly) { strip_sacn_addr Result = {0}; if (Parser_ReadOpenStruct(Parser, AssemblyField_OutputSACN)) { Result.StartUniverse = Parser_ReadU32Value(Parser, AssemblyField_SACN_StartUniverse); Result.StartChannel = Parser_ReadU32Value(Parser, AssemblyField_SACN_StartChannel); if (!Parser_ReadCloseStruct(Parser)) { //TokenizerPushError(&Tokenizer, "Struct doesn't close where expected"); } } return Result; } internal strip_uart_addr AssemblyParser_ReadUARTAddr(parser* Parser, assembly Assembly) { strip_uart_addr Result = {0}; if (Parser_ReadOpenStruct(Parser, AssemblyField_OutputUART)) { Result.Channel = (u8)Parser_ReadU32Value(Parser, AssemblyField_UART_Channel); bool HasNetPort = Parser_ReadStringValue(Parser, AssemblyField_UART_ComPort, &Result.ComPort, true); if (Assembly.NetPortMode == NetworkPortMode_PortPerStrip && !HasNetPort) { Parser_PushErrorF(Parser, "NetPortMode for assembly is PortPerStrip, but this strip doesn't have an output port."); } if (!Parser_ReadCloseStruct(Parser)) { Parser_PushErrorF(Parser, "Struct doesn't close where expected"); } } return Result; } internal void AssemblyParser_ReadTag(parser* Parser, v2_strip* StripAt, u32 TagIndex) { if (Parser_ReadOpenStruct(Parser, AssemblyField_Tag)) { // TODO(Peter): Need to store the gs_string somewhere we can look it up for display in the interface // right now they are stored in temp memory and won't persist gs_string TagName = Parser_ReadStringValue(Parser, AssemblyField_Name); gs_string TagValue = Parser_ReadStringValue(Parser, AssemblyField_Value); StripSetTag(StripAt, TagIndex, TagName.ConstString, TagValue.ConstString); if (!Parser_ReadCloseStruct(Parser)) { Parser_PushErrorF(Parser, "Tag struct doesn't close where expected"); } } else { Parser_PushErrorF(Parser, "Expected a tag struct, but none was found."); } } internal void AssemblyParser_ReadTagList(parser* Parser, v2_strip* StripAt, assembly* Assembly) { StripAt->TagsCount = Parser_ReadU32Value(Parser, AssemblyField_TagsCount); // NOTE(pjs): Always add one tag to the input to leave room for the assembly name StripAt->TagsCount += 1; StripAt->Tags = PushArray(&Assembly->Arena, v2_tag, StripAt->TagsCount); StripSetTag(StripAt, 0, ConstString("assembly"), Assembly->Name.ConstString); for (u32 Tag = 1; Tag < StripAt->TagsCount; Tag++) { AssemblyParser_ReadTag(Parser, StripAt, Tag); } } internal strip_gen_data AssemblyParser_ReadStripGenData(parser* Parser, assembly* Assembly); internal strip_gen_interpolate_points AssemblyParser_ReadInterpolatePoints(parser* Parser) { strip_gen_interpolate_points Result = {0}; if (Parser_ReadOpenStruct(Parser, AssemblyField_InterpolatePoints)) { Result.StartPosition = Parser_ReadV3Value(Parser, AssemblyField_Start); Result.EndPosition = Parser_ReadV3Value(Parser, AssemblyField_End); Result.LedCount = Parser_ReadU32Value(Parser, AssemblyField_LedCount); if (!Parser_ReadCloseStruct(Parser)) { // TODO(pjs): } } else { // TODO(pjs): } return Result; } internal strip_gen_sequence AssemblyParser_ReadSequence(parser* Parser, assembly* Assembly) { strip_gen_sequence Result = {0}; if (Parser_ReadOpenStruct(Parser, AssemblyField_SegmentSequence)) { Result.ElementsCount = Parser_ReadU32Value(Parser, AssemblyField_SegmentSequenceLength); Result.Elements = PushArray(&Assembly->Arena, strip_gen_data, Result.ElementsCount); for (u32 i = 0; i < Result.ElementsCount; i++) { Result.Elements[i] = AssemblyParser_ReadStripGenData(Parser, Assembly); } if (!Parser_ReadCloseStruct(Parser)) { // TODO(pjs): } } else { // TODO(pjs): } return Result; } internal strip_gen_data AssemblyParser_ReadStripGenData(parser* Parser, assembly* Assembly) { strip_gen_data Result = {0}; if (Parser_ReadOpenStruct(Parser, AssemblyField_Segment)) { gs_string PointPlacementType = Parser_ReadStringValue(Parser, AssemblyField_PointPlacementType); // TODO(pjs): We want to store enum strings in some unified way // :EnumStringsGen if (StringsEqual(PointPlacementType.ConstString, ConstString("InterpolatePoints"))) { Result.Method = StripGeneration_InterpolatePoints; Result.InterpolatePoints = AssemblyParser_ReadInterpolatePoints(Parser); } else if (StringsEqual(PointPlacementType.ConstString, ConstString("SegmentSequence"))) { Result.Method = StripGeneration_Sequence; Result.Sequence = AssemblyParser_ReadSequence(Parser, Assembly); } else { Parser_PushErrorF(Parser, "Incorrect Point Placement Type found for segment"); } if (!Parser_ReadCloseStruct(Parser)) { Parser_PushErrorF(Parser, "Strip Gen Data did not close the struct where expected"); } } return Result; } internal bool ParseAssemblyFile(assembly* Assembly, gs_const_string FileName, gs_string FileText, gs_memory_arena* Transient) { Assembly->LedCountTotal = 0; parser Parser = {0}; Parser.String = FileText; Parser.Identifiers = &AssemblyFieldIdentifiers[0]; Parser.IdentifiersCount = AssemblyField_Count; Parser.At = Parser.String.Str; Parser.LineStart = Parser.At; Parser.Arena = &Assembly->Arena; Parser.Transient = Transient; Assembly->Name = Parser_ReadStringValue(&Parser, AssemblyField_AssemblyName); Assembly->Scale = Parser_ReadR32Value(&Parser, AssemblyField_AssemblyScale); Assembly->Center = Parser_ReadV3Value(&Parser, AssemblyField_AssemblyCenter); Assembly->StripCount = Parser_ReadU32Value(&Parser, AssemblyField_LedStripCount); Assembly->Strips = PushArray(&Assembly->Arena, v2_strip, Assembly->StripCount); gs_string OutputModeString = Parser_ReadStringValue(&Parser, AssemblyField_OutputMode); if (StringsEqual(OutputModeString.ConstString, ConstString("UART"))) { Assembly->OutputMode = NetworkProtocol_UART; if (Parser_ReadStringValue(&Parser, AssemblyField_UART_ComPort, &Assembly->UARTComPort, true)) { Assembly->NetPortMode = NetworkPortMode_GlobalPort; } else { Assembly->NetPortMode = NetworkPortMode_PortPerStrip; } } else if (StringsEqual(OutputModeString.ConstString, ConstString("SACN"))) { Assembly->OutputMode = NetworkProtocol_SACN; } else { Parser_PushErrorF(&Parser, "Invalid output mode specified for assembly."); } for (u32 i = 0; i < Assembly->StripCount; i++) { v2_strip* StripAt = Assembly->Strips + i; if (Parser_ReadOpenStruct(&Parser, AssemblyField_LedStrip)) { StripAt->SACNAddr = AssemblyParser_ReadSACNAddr(&Parser, *Assembly); StripAt->UARTAddr = AssemblyParser_ReadUARTAddr(&Parser, *Assembly); StripAt->GenerationData = AssemblyParser_ReadStripGenData(&Parser, Assembly); StripAt->LedCount = StripGenData_CountLeds(StripAt->GenerationData); AssemblyParser_ReadTagList(&Parser, StripAt, Assembly); Assembly->LedCountTotal += StripAt->LedCount; if (!Parser_ReadCloseStruct(&Parser)) { Parser_PushErrorF(&Parser, "Strip struct doesn't close where expected"); } } else { Parser_PushErrorF(&Parser, "Expected a strip struct but none was found"); } } // TODO(pjs): invalidate the file if its incorrect return true; //Tokenizer.ParsingIsValid; } #define FOLDHAUS_ASSEMBLY_PARSER_CPP #endif // FOLDHAUS_ASSEMBLY_PARSER_CPP