// StoreVariableString //------------------------------------------------------------------------------ bool BFFParser::StoreVariableString( const AString & name, const BFFIterator & valueStart, const BFFIterator & valueEnd, const BFFIterator & operatorIter, BFFStackFrame * frame ) { // unescape and subsitute embedded variables AStackString< 2048 > value; if ( PerformVariableSubstitutions( valueStart, valueEnd, value ) == false ) { return false; } // are we concatenating? const BFFVariable * varToConcat = nullptr; if ( *operatorIter == BFF_VARIABLE_CONCATENATION ) { // find existing varToConcat = BFFStackFrame::GetVar( name, frame ); if ( varToConcat == nullptr ) { Error::Error_1026_VariableNotFoundForConcatenation( operatorIter, name ); return false; } // make sure types are compatible if ( varToConcat->IsString() ) { // OK - can concat String to String AStackString< 1024 > finalValue( varToConcat->GetString() ); finalValue += value; BFFStackFrame::SetVarString( name, finalValue, frame ); FLOG_INFO( "Appended '%s' to <String> variable '%s' with result '%s'", value.Get(), name.Get(), finalValue.Get() ); return true; } else if ( varToConcat->IsArrayOfStrings() ) { // OK - can concat String to ArrayOfStrings Array< AString > finalValues( varToConcat->GetArrayOfStrings().GetSize() + 1, false ); finalValues = varToConcat->GetArrayOfStrings(); finalValues.Append( value ); BFFStackFrame::SetVarArrayOfStrings( name, finalValues, frame ); FLOG_INFO( "Appended '%s' to <ArrayOfStrings> variable '%s' with result of %i items", value.Get(), name.Get(), finalValues.GetSize() ); return true; } else { Error::Error_1027_CannotConcatenate( operatorIter, name, varToConcat->GetType(), BFFVariable::VAR_STRING ); return false; } } // handle regular assignment of string BFFStackFrame::SetVarString( name, value, frame ); FLOG_INFO( "Registered <string> variable '%s' with value '%s'", name.Get(), value.Get() ); return true; }
// ParseIncludeDirective //------------------------------------------------------------------------------ bool BFFParser::ParseIncludeDirective( BFFIterator & iter ) { // Sanity check include depth to detect cyclic includes if ( s_Depth >= 128 ) { Error::Error_1035_ExcessiveDepthComplexity( iter ); return false; } // we expect a " quoted string if ( *iter != '"' ) { Error::Error_1031_UnexpectedCharFollowingDirectiveName( iter, AStackString<>( "include" ), '"' ); return false; } BFFIterator stringStart( iter ); stringStart++; // first actual character // find end of string if ( iter.ParseToNext( '"' ) == false ) { Error::Error_1012_UnexpectedEndOfFile( iter ); return false; } // unescape and substitute variables AStackString<> include; if ( PerformVariableSubstitutions( stringStart, iter, include ) == false ) { return false; } iter++; // skip closing quote before returning FLOG_INFO( "Including: %s\n", include.Get() ); // open include // 1) Try current directory AStackString<> includeToUse; if (PathUtils::IsFullPath(include) == false) { const char * lastSlash = iter.GetFileName().FindLast( NATIVE_SLASH ); lastSlash = lastSlash ? lastSlash : iter.GetFileName().FindLast( OTHER_SLASH ); lastSlash = lastSlash ? ( lastSlash + 1 ): iter.GetFileName().Get(); // file only, truncate to empty includeToUse.Assign( iter.GetFileName().Get(), lastSlash ); } includeToUse += include; AStackString<> includeToUseClean; NodeGraph::CleanPath( includeToUse, includeToUseClean ); FileStream f; if ( f.Open( includeToUseClean.Get(), FileStream::READ_ONLY ) == false ) { Error::Error_1032_UnableToOpenInclude( stringStart, includeToUseClean ); return false; } // check if include uses "once" pragma if ( FBuild::Get().GetDependencyGraph().IsOneUseFile( includeToUseClean ) ) { // already seen, and uses #once : don't include again return true; } uint64_t includeTimeStamp = FileIO::GetFileLastWriteTime( includeToUseClean ); // read content of include const uint32_t fileSize = (uint32_t)f.GetFileSize(); AutoPtr< char > mem( (char *)ALLOC( fileSize + 1 ) ); if ( f.Read( mem.Get(), fileSize ) != fileSize ) { Error::Error_1033_ErrorReadingInclude( stringStart, include, Env::GetLastErr() ); return false; } mem.Get()[ fileSize ] = '\000'; // sentinel BFFParser parser; const bool pushStackFrame = false; // include is treated as if injected at this point return parser.Parse( mem.Get(), fileSize, includeToUseClean.Get(), includeTimeStamp, pushStackFrame ); }
// ParseNamedVariableName //------------------------------------------------------------------------------ /*static*/ bool BFFParser::ParseVariableName( BFFIterator & iter, AString & name, bool & parentScope ) { // skip over the declaration symbol ASSERT( *iter == BFF_DECLARE_VAR_INTERNAL || *iter == BFF_DECLARE_VAR_PARENT ); parentScope = ( *iter == BFF_DECLARE_VAR_PARENT ); const BFFIterator varNameStart = iter; // include type token in var name iter++; // make sure we haven't hit the end of the file if ( iter.IsAtEnd() ) { Error::Error_1012_UnexpectedEndOfFile( iter ); return false; } if ( *iter == '\'' || *iter == '"' ) { // parse the string const BFFIterator openToken = iter; iter.SkipString( *openToken ); if ( *iter != *openToken ) { Error::Error_1002_MatchingClosingTokenNotFound( openToken, nullptr, *openToken ); return false; } BFFIterator stringStart = openToken; stringStart++; // unescape and subsitute embedded variables AStackString< 256 > value; if ( PerformVariableSubstitutions( stringStart, iter, value ) == false ) { return false; } iter++; // skip close token BFFIterator varNameIter( value.Get(), value.GetLength(), iter.GetFileName().Get(), iter.GetFileTimeStamp() ); // sanity check it is a sensible length if ( value.GetLength() + 1/* '.' will be added */ > MAX_VARIABLE_NAME_LENGTH ) { Error::Error_1014_VariableNameIsTooLong( varNameIter, (uint32_t)value.GetLength(), (uint32_t)MAX_VARIABLE_NAME_LENGTH ); return false; } // sanity check it is a valid variable name while ( varNameIter.IsAtEnd() == false ) { if ( varNameIter.IsAtValidVariableNameCharacter() == false ) { Error::Error_1013_UnexpectedCharInVariableName( varNameIter, nullptr ); return false; } varNameIter++; } // append '.' to variable name name = "."; name.Append( value ); } else { // make sure immediately after the symbol starts a variable name if ( iter.IsAtValidVariableNameCharacter() == false ) { Error::Error_1013_UnexpectedCharInVariableName( iter, nullptr ); return false; } // find the end of the variable name iter.SkipVariableName(); const BFFIterator varNameEnd = iter; // sanity check it is a sensible length size_t varNameLen = varNameStart.GetDistTo( varNameEnd ); if ( varNameLen > MAX_VARIABLE_NAME_LENGTH ) { Error::Error_1014_VariableNameIsTooLong( iter, (uint32_t)varNameLen, (uint32_t)MAX_VARIABLE_NAME_LENGTH ); return false; } // store variable name name.Assign( varNameStart.GetCurrent(), varNameEnd.GetCurrent() ); } ASSERT( name.GetLength() > 0 ); if ( parentScope ) { // exchange '^' with '.' ASSERT( BFF_DECLARE_VAR_PARENT == name[0] ); name[0] = BFF_DECLARE_VAR_INTERNAL; } return true; }
// StoreVariableArray //------------------------------------------------------------------------------ bool BFFParser::StoreVariableArray( const AString & name, const BFFIterator & valueStart, const BFFIterator & valueEnd, const BFFIterator & operatorIter, BFFStackFrame * frame ) { Array< AString > values( 32, true ); Array< const BFFVariable * > structValues( 32, true ); // are we concatenating? if ( ( *operatorIter == BFF_VARIABLE_CONCATENATION ) || ( *operatorIter == BFF_VARIABLE_SUBTRACTION ) ) { // find existing const BFFVariable * var = BFFStackFrame::GetVar( name, frame ); if ( var == nullptr ) { Error::Error_1026_VariableNotFoundForModification( operatorIter, name ); return false; } // make sure existing is an array if ( var->IsArrayOfStrings() ) { // get values to start with values = var->GetArrayOfStrings(); } else if ( var->IsArrayOfStructs() ) { // get values to start with structValues = var->GetArrayOfStructs(); } else { // TODO:B Improve this error to handle ArrayOfStructs case Error::Error_1027_CannotModify( operatorIter, name, var->GetType(), BFFVariable::VAR_ARRAY_OF_STRINGS ); return false; } } // Parse array of variables BFFIterator iter( valueStart ); for (;;) { iter.SkipWhiteSpaceAndComments(); // end? if ( iter.GetCurrent() == valueEnd.GetCurrent() ) { break; } const char c = *iter; if ( ( c == '"' ) || ( c == '\'' ) ) { // a quoted string // detect mistmatches if ( structValues.IsEmpty() == false ) { // Mixed types in vector Error::Error_1034_OperationNotSupported( iter, BFFVariable::VAR_ARRAY_OF_STRUCTS, BFFVariable::VAR_STRING, operatorIter ); return false; } // subtraction not supported on arrays if ( *operatorIter == BFF_VARIABLE_SUBTRACTION ) { Error::Error_1034_OperationNotSupported( iter, BFFVariable::VAR_ARRAY_OF_STRINGS, BFFVariable::VAR_STRING, operatorIter ); return false; } // a string BFFIterator elementValueStart( iter ); iter.SkipString( c ); ASSERT( iter.GetCurrent() <= valueEnd.GetCurrent() ); // should not be in this function if string is not terminated elementValueStart++; // move to start of actual content AStackString< 2048 > elementValue; // unescape and subsitute embedded variables if ( PerformVariableSubstitutions( elementValueStart, iter, elementValue ) == false ) { return false; } values.Append( elementValue ); iter++; // pass closing quote } else if ( c == BFF_DECLARE_VAR_INTERNAL || c == BFF_DECLARE_VAR_PARENT ) { const BFFIterator elementStartValue = iter; // a variable AStackString< MAX_VARIABLE_NAME_LENGTH > varName; bool parentScope = false; // ignored, the behavior is the same if ( ParseVariableName( iter, varName, parentScope ) == false ) { return false; } // Determine stack frame to use for Src var BFFStackFrame * srcFrame = BFFStackFrame::GetCurrent(); if ( c == BFF_DECLARE_VAR_PARENT ) { srcFrame = BFFStackFrame::GetCurrent()->GetParent(); } // get the variable const BFFVariable * var = srcFrame->GetVariableRecurse( varName ); if ( var == nullptr ) { Error::Error_1026_VariableNotFoundForModification( operatorIter, varName ); return false; } // subtraction not supported on arrays if ( *operatorIter == BFF_VARIABLE_SUBTRACTION ) { const BFFVariable::VarType dstType = structValues.IsEmpty() ? BFFVariable::VAR_ARRAY_OF_STRINGS : BFFVariable::VAR_ARRAY_OF_STRUCTS; const BFFVariable::VarType srcType = var->GetType(); Error::Error_1034_OperationNotSupported( elementStartValue, dstType, srcType, operatorIter ); return false; } if ( var->IsString() || var->IsArrayOfStrings() ) { // dest is consistent? if ( structValues.IsEmpty() == false ) { // inconsistency Error::Error_1034_OperationNotSupported( elementStartValue, BFFVariable::VAR_ARRAY_OF_STRUCTS, var->GetType(), operatorIter ); return false; } if ( var->IsString() ) { values.Append( var->GetString() ); } else { values.Append( var->GetArrayOfStrings() ); } } else if ( var->IsStruct() || var->IsArrayOfStructs() ) { // dest is consistent? if ( values.IsEmpty() == false ) { // inconsistency Error::Error_1034_OperationNotSupported( elementStartValue, BFFVariable::VAR_ARRAY_OF_STRINGS, var->GetType(), operatorIter ); return false; } if ( var->IsStruct() ) { structValues.Append( var ); } else { structValues.Append( var->GetArrayOfStructs() ); } } else { Error::Error_1050_PropertyMustBeOfType( iter, nullptr, name.Get(), var->GetType(), BFFVariable::VAR_STRING, BFFVariable::VAR_STRUCT ); return false; } } else { Error::Error_1001_MissingStringStartToken( iter, nullptr ); return false; } iter.SkipWhiteSpaceAndComments(); if ( *iter == ',' ) // comma seperators are optional { iter++; } // continue looking for more vars... } // should only have one populated array ASSERT( values.IsEmpty() || structValues.IsEmpty() ); // Register this variable if ( structValues.IsEmpty() == false ) { // structs BFFStackFrame::SetVarArrayOfStructs( name, structValues, frame ); FLOG_INFO( "Registered <ArrayOfStructs> variable '%s' with %u elements", name.Get(), structValues.GetSize() ); } else { // strings (or unknown, so default to strings) BFFStackFrame::SetVarArrayOfStrings( name, values, frame ); FLOG_INFO( "Registered <ArrayOfStrings> variable '%s' with %u elements", name.Get(), values.GetSize() ); } return true; }
// StoreVariableString //------------------------------------------------------------------------------ bool BFFParser::StoreVariableString( const AString & name, const BFFIterator & valueStart, const BFFIterator & valueEnd, const BFFIterator & operatorIter, BFFStackFrame * frame ) { // unescape and subsitute embedded variables AStackString< 2048 > value; if ( PerformVariableSubstitutions( valueStart, valueEnd, value ) == false ) { return false; } // are we concatenating? const BFFVariable * varToConcat = nullptr; if ( ( *operatorIter == BFF_VARIABLE_CONCATENATION ) || ( *operatorIter == BFF_VARIABLE_SUBTRACTION ) ) { // find existing varToConcat = BFFStackFrame::GetVar( name, frame ); if ( varToConcat == nullptr ) { Error::Error_1026_VariableNotFoundForModification( operatorIter, name ); return false; } // make sure types are compatible if ( varToConcat->IsString() ) { // OK - can concat String to String AStackString< 1024 > finalValue( varToConcat->GetString() ); if ( *operatorIter == BFF_VARIABLE_CONCATENATION ) { finalValue += value; } else { finalValue.Replace( value.Get(), "" ); } BFFStackFrame::SetVarString( name, finalValue, frame ); FLOG_INFO( "%s '%s' %s <String> variable '%s' with result '%s'", ( *operatorIter == BFF_VARIABLE_CONCATENATION ) ? "Appending" : "Removing", value.Get(), ( *operatorIter == BFF_VARIABLE_CONCATENATION ) ? "to" : "from", name.Get(), finalValue.Get() ); return true; } else if ( varToConcat->IsArrayOfStrings() ) { // OK - can concat String to ArrayOfStrings Array< AString > finalValues( varToConcat->GetArrayOfStrings().GetSize() + 1, false ); if ( *operatorIter == BFF_VARIABLE_CONCATENATION ) { finalValues = varToConcat->GetArrayOfStrings(); finalValues.Append( value ); } else { auto end = varToConcat->GetArrayOfStrings().End(); for ( auto it=varToConcat->GetArrayOfStrings().Begin(); it!=end; ++it ) { if ( *it != value ) // remove equal strings { finalValues.Append( *it ); } } } BFFStackFrame::SetVarArrayOfStrings( name, finalValues, frame ); FLOG_INFO( "%s '%s' %s <ArrayOfStrings> variable '%s' with result of %i items", ( *operatorIter == BFF_VARIABLE_CONCATENATION ) ? "Appending" : "Removing", value.Get(), ( *operatorIter == BFF_VARIABLE_CONCATENATION ) ? "to" : "from", name.Get(), finalValues.GetSize() ); return true; } else { Error::Error_1027_CannotModify( operatorIter, name, varToConcat->GetType(), BFFVariable::VAR_STRING ); return false; } } // handle regular assignment of string BFFStackFrame::SetVarString( name, value, frame ); FLOG_INFO( "Registered <string> variable '%s' with value '%s'", name.Get(), value.Get() ); return true; }