// ParsePreprocessorDirective //------------------------------------------------------------------------------ bool BFFParser::ParsePreprocessorDirective( BFFIterator & iter ) { const BFFIterator directiveStart( iter ); // skip directive start token ASSERT( *iter == BFF_PREPROCESSOR_START ); iter++; // allow whitepace before directive name iter.SkipWhiteSpace(); // start of directive name BFFIterator directiveStartIter( iter ); // find end of directive while ( iter.IsAtValidDirectiveNameCharacter() ) { iter++; } BFFIterator directiveEndIter( iter ); iter.SkipWhiteSpace(); // determine directive AStackString< MAX_DIRECTIVE_NAME_LENGTH > directive( directiveStartIter.GetCurrent(), directiveEndIter.GetCurrent() ); if ( directive == "include" ) { return ParseIncludeDirective( iter ); } else if ( directive == "once" ) { FBuild::Get().GetDependencyGraph().SetCurrentFileAsOneUse(); return true; } else if ( directive == "define" ) { return ParseDefineDirective( iter ); } else if ( directive == "undef" ) { return ParseUndefDirective( iter ); } else if ( directive == "if" ) { return ParseIfDirective( directiveStart, iter ); } else if ( directive == "endif" ) { return ParseEndIfDirective( directiveStartIter ); } else if ( directive == "import" ) { return ParseImportDirective( directiveStart, iter ); } // unknown Error::Error_1030_UnknownDirective( directiveStartIter, directive ); return false; }
// ParseImportDirective //------------------------------------------------------------------------------ bool BFFParser::ParseImportDirective( const BFFIterator & directiveStart, BFFIterator & iter ) { iter.SkipWhiteSpace(); // make sure we haven't hit the end of the file if ( iter.IsAtEnd() ) { Error::Error_1012_UnexpectedEndOfFile( directiveStart ); return false; } // make sure this is a variable name if ( iter.IsAtValidVariableNameCharacter() == false ) { Error::Error_1013_UnexpectedCharInVariableName( iter, nullptr ); return false; } // find the end of the variable name const BFFIterator varNameStart( iter ); iter.SkipVariableName(); if ( iter.IsAtEnd() ) { Error::Error_1012_UnexpectedEndOfFile( iter ); return false; } 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; } AStackString<> varName( varNameStart.GetCurrent(), varNameEnd.GetCurrent() ); // look for varName in system environment AStackString<> varValue; uint32_t varHash = 0; if ( FBuild::Get().ImportEnvironmentVar( varName.Get(), varValue, varHash ) == false ) { Error::Error_1009_UnknownVariable( varNameStart, nullptr ); return false; } // add the dot to variable name varName = "."; varName.Append( varNameStart.GetCurrent(), varNameLen ); // import variable in current scope BFFStackFrame::SetVarString( varName, varValue, nullptr ); FLOG_INFO( "Imported <string> variable '%s' with value '%s' from system environment", varName.Get(), varValue.Get() ); return true; }
// ParseIfDirective //------------------------------------------------------------------------------ bool BFFParser::ParseIfDirective( const BFFIterator & directiveStart, BFFIterator & iter ) { if ( iter.IsAtEnd() ) { Error::Error_1012_UnexpectedEndOfFile( iter ); return false; } bool negate = false; if ( *iter == '!' ) { negate = true; // the condition will be inverted iter++; // skip '!' iter.SkipWhiteSpace(); // allow whitepace after '!' if ( iter.IsAtEnd() ) { Error::Error_1012_UnexpectedEndOfFile( iter ); return false; } } // parse out condition const BFFIterator conditionStart( iter ); iter.SkipVariableName(); if ( conditionStart.GetCurrent() == iter.GetCurrent() ) { Error::Error_1007_ExpectedVariable( directiveStart, nullptr ); return false; } const BFFIterator conditionEnd( iter ); // Evaluate the condition bool result; if ( CheckIfCondition( conditionStart, conditionEnd, result ) == false ) { return false; // CheckIfCondition will have emitted an error } // #ifndef ? if ( negate ) { result = !( result ); } if ( result ) { ++s_IfDepth; // Track that we're inside an if block return true; // continue parsing like normal } // Advance iterator past entire #if block size_t depth = 1; // handle nested ifs while ( depth > 0 ) { // did we hit the end of the file? if ( iter.IsAtEnd() ) { (void)directiveStart; // TODO: Show we're looking for matching endif to this Error::Error_1012_UnexpectedEndOfFile( iter ); // TODO:B better error for this? return false; } // find the next preprocessor directive iter.SkipWhiteSpace(); if ( *iter == BFF_PREPROCESSOR_START ) { iter++; // skip # iter.SkipWhiteSpace(); // allow whitespace between # and directive const BFFIterator directiveNameStart( iter ); while ( iter.IsAtValidDirectiveNameCharacter() ) { iter++; } const BFFIterator directiveNameEnd( iter ); AStackString<> directiveName( directiveNameStart.GetCurrent(), directiveNameEnd.GetCurrent() ); if ( directiveName == "endif" ) { --depth; } else if ( directiveName == "if" ) { ++depth; } // continue to skip rest of line.... } // skip rest of line while ( ( iter.IsAtEnd() == false ) && ( *iter != '\r' ) && ( *iter != '\n' ) ) { iter++; } } return true; }
// Parse //------------------------------------------------------------------------------ bool BFFParser::Parse( BFFIterator & iter ) { for (;;) { iter.SkipWhiteSpace(); // is this a comment? if ( iter.IsAtComment() ) { iter.SkipComment(); continue; } const char c = *iter; switch ( c ) { case BFF_DECLARE_VAR_INTERNAL: case BFF_DECLARE_VAR_PARENT: { if ( ParseNamedVariableDeclaration( iter ) == false ) { return false; } continue; } case BFF_VARIABLE_CONCATENATION: case BFF_VARIABLE_SUBTRACTION: { // concatenation to last used variable if ( ParseUnnamedVariableModification( iter ) == false ) { return false; } continue; } case BFF_SCOPE_OPEN: { // start an unnamed scope if ( ParseUnnamedScope( iter ) == false ) { return false; } continue; } case BFF_PREPROCESSOR_START: { if ( ParsePreprocessorDirective( iter ) == false ) { return false; } continue; } default: { if ( iter.IsAtValidFunctionNameCharacter() ) { if ( ParseFunction( iter ) == false ) { return false; } continue; } } } iter.SkipWhiteSpace(); if ( iter.IsAtEnd() == false ) { Error::Error_1010_UnknownConstruct( iter ); return false; } break; // cleanly hit end of file } return true; }
// ParseFunction //------------------------------------------------------------------------------ bool BFFParser::ParseFunction( BFFIterator & iter ) { ASSERT( iter.IsAtValidFunctionNameCharacter() ); // for variables to be used by this function BFFStackFrame stackFrame; BFFIterator functionNameStart( iter ); iter.SkipFunctionName(); if ( iter.IsAtEnd() ) { Error::Error_1012_UnexpectedEndOfFile( iter ); return false; } // check length if ( functionNameStart.GetDistTo( iter ) > MAX_FUNCTION_NAME_LENGTH ) { // if it's too long, then it can't be a valid function Error::Error_1015_UnknownFunction( functionNameStart ); return false; } // store function name AStackString<MAX_FUNCTION_NAME_LENGTH> functionName( functionNameStart.GetCurrent(), iter.GetCurrent() ); const Function * func = Function::Find( functionName ); if ( func == nullptr ) { Error::Error_1015_UnknownFunction( functionNameStart ); return false; } iter.SkipWhiteSpace(); if ( func->IsUnique() && func->GetSeen() ) { Error::Error_1020_FunctionCanOnlyBeInvokedOnce( functionNameStart, func ); return false; } func->SetSeen(); FLOG_INFO( "Function call '%s'", functionName.Get() ); // header, or body? bool hasHeader = false; BFFIterator functionArgsStartToken( iter ); BFFIterator functionArgsStopToken( iter ); if ( *iter == BFF_FUNCTION_ARGS_OPEN ) { // can this function accept a header? if ( func->AcceptsHeader() == false ) { Error::Error_1021_UnexpectedHeaderForFunction( iter, func ); return false; } // args if ( iter.ParseToMatchingBrace( BFF_FUNCTION_ARGS_OPEN, BFF_FUNCTION_ARGS_CLOSE ) == false ) { Error::Error_1022_MissingFunctionHeaderCloseToken( functionArgsStartToken, func ); return false; } functionArgsStopToken = iter; hasHeader = true; iter++; // skip over closing token iter.SkipWhiteSpaceAndComments(); } if ( func->NeedsHeader() && ( hasHeader == false ) ) { Error::Error_1023_FunctionRequiresAHeader( iter, func ); return false; } // some functions have no body bool hasBody = false; BFFIterator functionBodyStartToken( iter ); BFFIterator functionBodyStopToken( iter ); if ( func->NeedsBody() ) { // find body if ( *iter != BFF_SCOPE_OPEN ) { Error::Error_1024_FunctionRequiresABody( functionNameStart, func ); return false; } if ( iter.ParseToMatchingBrace( BFF_SCOPE_OPEN, BFF_SCOPE_CLOSE ) == false ) { Error::Error_1025_MissingScopeCloseToken( functionBodyStartToken, func ); return false; } functionBodyStopToken = iter; iter++; hasBody = true; } return func->ParseFunction( functionNameStart, hasBody ? &functionBodyStartToken : nullptr, hasBody ? &functionBodyStopToken : nullptr, hasHeader ? &functionArgsStartToken : nullptr, hasHeader ? &functionArgsStopToken : nullptr );}