Browse Source

Output all logging/errors to stderr

I wanted to conform to the standard here.

I don't actually know what makes sense to output to stdout. Executing
processes should output to the proper channels.
windows-support
Macoy Madson 7 months ago
parent
commit
48f2aac251
12 changed files with 195 additions and 197 deletions
  1. +2
    -1
      src/DynamicLoader.cpp
  2. +45
    -45
      src/Evaluator.cpp
  3. +7
    -7
      src/FileUtilities.cpp
  4. +14
    -17
      src/GeneratorHelpers.cpp
  5. +4
    -5
      src/Generators.cpp
  6. +14
    -15
      src/Main.cpp
  7. +41
    -43
      src/ModuleManager.cpp
  8. +12
    -12
      src/RunProcess.cpp
  9. +11
    -11
      src/Tokenizer.cpp
  10. +7
    -6
      src/Utilities.cpp
  11. +18
    -15
      src/Utilities.hpp
  12. +20
    -20
      src/Writer.cpp

+ 2
- 1
src/DynamicLoader.cpp View File

@ -1,6 +1,7 @@
#include "DynamicLoader.hpp"
#include <stdio.h>
#include <string>
#include <unordered_map>
@ -114,7 +115,7 @@ void closeDynamicLibrary(DynamicLibHandle handleToClose)
if (!libHandle)
{
printf("warning: closing library which wasn't in the list of loaded libraries\n");
fprintf(stderr, "warning: closing library which wasn't in the list of loaded libraries\n");
libHandle = handleToClose;
}


+ 45
- 45
src/Evaluator.cpp View File

@ -113,7 +113,7 @@ const ObjectReferenceStatus* addObjectReference(EvaluatorEnvironment& environmen
// Default to the module requiring the reference, for top-level references
std::string definitionName = globalDefinitionName;
if (!reference.context.definitionName && reference.context.scope != EvaluatorScope_Module)
printf("error: addObjectReference() expects a definitionName\n");
Log("error: addObjectReference() expects a definitionName\n");
if (reference.context.definitionName)
{
@ -122,7 +122,7 @@ const ObjectReferenceStatus* addObjectReference(EvaluatorEnvironment& environmen
const char* defName = definitionName.c_str();
if (log.references)
printf("Adding reference %s to %s\n", referenceNameToken.contents.c_str(), defName);
Logf("Adding reference %s to %s\n", referenceNameToken.contents.c_str(), defName);
// Add the reference requirement to the definition it occurred in
ObjectReferenceStatus* refStatus = nullptr;
@ -131,7 +131,7 @@ const ObjectReferenceStatus* addObjectReference(EvaluatorEnvironment& environmen
{
if (definitionName.compare(globalDefinitionName) != 0)
{
printf("error: expected definition %s to already exist. Things will break\n",
Logf("error: expected definition %s to already exist. Things will break\n",
definitionName.c_str());
}
else
@ -202,7 +202,7 @@ bool CreateCompileTimeVariable(EvaluatorEnvironment& environment, const char* na
CompileTimeVariableTableIterator findIt = environment.compileTimeVariables.find(name);
if (findIt != environment.compileTimeVariables.end())
{
printf("error: CreateCompileTimeVariable(): variable %s already defined\n", name);
Logf("error: CreateCompileTimeVariable(): variable %s already defined\n", name);
return false;
}
@ -232,7 +232,7 @@ bool GetCompileTimeVariable(EvaluatorEnvironment& environment, const char* name,
if (findIt->second.type.compare(typeExpression) != 0)
{
printf(
Logf(
"error: GetCompileTimeVariable(): type does not match existing variable %s. Types must "
"match exactly. Expected %s, got %s\n",
name, findIt->second.type.c_str(), typeExpression);
@ -307,7 +307,7 @@ bool HandleInvocation_Recursive(EvaluatorEnvironment& environment, const Evaluat
"code was generated from macro. See erroneous macro "
"expansion below:");
printTokens(*macroOutputTokens);
printf("\n");
Log("\n");
// Deleting these tokens is only safe at this point because we know we have not
// evaluated them. As soon as they are evaluated, they must be kept around
delete macroOutputTokens;
@ -348,7 +348,7 @@ bool HandleInvocation_Recursive(EvaluatorEnvironment& environment, const Evaluat
NoteAtToken(invocationStart,
"code was generated from macro. See macro expansion below:");
printTokens(*macroOutputTokens);
printf("\n");
Log("\n");
return false;
}
@ -571,14 +571,14 @@ bool ReplaceAndEvaluateDefinition(EvaluatorEnvironment& environment,
ObjectDefinitionMap::iterator findIt = environment.definitions.find(definitionToReplaceName);
if (findIt == environment.definitions.end())
{
printf("error: ReplaceAndEvaluateDefinition() could not find definition '%s'\n",
Logf("error: ReplaceAndEvaluateDefinition() could not find definition '%s'\n",
definitionToReplaceName);
return false;
}
if (!validateParentheses(newDefinitionTokens))
{
printf("note: encountered error while validating the following replacement definition:\n");
Log("note: encountered error while validating the following replacement definition:\n");
prettyPrintTokens(newDefinitionTokens);
return false;
}
@ -609,7 +609,7 @@ bool ReplaceAndEvaluateDefinition(EvaluatorEnvironment& environment,
if (!result)
{
printf("note: encountered error while evaluating the following replacement definition:\n");
Log("note: encountered error while evaluating the following replacement definition:\n");
prettyPrintTokens(newDefinitionTokens);
}
@ -641,7 +641,7 @@ static void PropagateRequiredToReferences(EvaluatorEnvironment& environment)
if (findIt != environment.requiredCompileTimeFunctions.end())
{
if (log.dependencyPropagation)
printf("Define %s promoted to required because %s\n",
Logf("Define %s promoted to required because %s\n",
definition.name.c_str(), findIt->second);
definition.isRequired = true;
@ -652,7 +652,7 @@ static void PropagateRequiredToReferences(EvaluatorEnvironment& environment)
if (log.dependencyPropagation)
{
const char* status = definition.isRequired ? "(required)" : "(not required)";
printf("Define %s %s\n", definition.name.c_str(), status);
Logf("Define %s %s\n", definition.name.c_str(), status);
}
for (ObjectReferenceStatusPair& reference : definition.references)
@ -660,7 +660,7 @@ static void PropagateRequiredToReferences(EvaluatorEnvironment& environment)
ObjectReferenceStatus& referenceStatus = reference.second;
if (log.dependencyPropagation)
printf("\tRefers to %s\n", referenceStatus.name->contents.c_str());
Logf("\tRefers to %s\n", referenceStatus.name->contents.c_str());
if (definition.isRequired)
{
@ -669,7 +669,7 @@ static void PropagateRequiredToReferences(EvaluatorEnvironment& environment)
if (findIt != environment.definitions.end() && !findIt->second.isRequired)
{
if (log.dependencyPropagation)
printf("\t Infecting %s with required due to %s\n",
Logf("\t Infecting %s with required due to %s\n",
referenceStatus.name->contents.c_str(), definition.name.c_str());
++numRequiresStatusChanged;
@ -729,7 +729,7 @@ int BuildExecuteCompileTimeFunctions(EvaluatorEnvironment& environment,
ObjectDefinition* definition = buildObject.definition;
if (log.buildProcess)
printf("Build %s\n", definition->name.c_str());
Logf("Build %s\n", definition->name.c_str());
if (!definition->output)
{
@ -847,7 +847,7 @@ int BuildExecuteCompileTimeFunctions(EvaluatorEnvironment& environment,
if (canUseCachedFile(environment, sourceOutputName, buildObject.dynamicLibraryPath.c_str()))
{
if (log.buildProcess)
printf("Skipping compiling %s (using cached library)\n", sourceOutputName);
Logf("Skipping compiling %s (using cached library)\n", sourceOutputName);
// Skip straight to linking, which immediately becomes loading
buildObject.stage = BuildStage_Linking;
buildObject.status = 0;
@ -930,7 +930,7 @@ int BuildExecuteCompileTimeFunctions(EvaluatorEnvironment& environment,
buildObject.stage = BuildStage_Linking;
if (log.buildProcess)
printf("Compiled %s successfully\n", buildObject.definition->name.c_str());
Logf("Compiled %s successfully\n", buildObject.definition->name.c_str());
ProcessCommandInput linkTimeInputs[] = {
{ProcessCommandArgumentType_DynamicLibraryOutput,
@ -973,7 +973,7 @@ int BuildExecuteCompileTimeFunctions(EvaluatorEnvironment& environment,
buildObject.stage = BuildStage_Loading;
if (log.buildProcess)
printf("Linked %s successfully\n", buildObject.definition->name.c_str());
Logf("Linked %s successfully\n", buildObject.definition->name.c_str());
DynamicLibHandle builtLib = loadDynamicLibrary(buildObject.dynamicLibraryPath.c_str());
if (!builtLib)
@ -1035,7 +1035,7 @@ int BuildExecuteCompileTimeFunctions(EvaluatorEnvironment& environment,
if (referencePoolIt == environment.referencePools.end())
{
if (!buildObject.definition->environmentRequired)
printf(
Log(
"error: built an object which had no references. It should not have been "
"required. There must be a problem with Cakelisp internally\n");
continue;
@ -1112,7 +1112,7 @@ int BuildExecuteCompileTimeFunctions(EvaluatorEnvironment& environment,
continue;
if (log.buildProcess)
printf("Resolved %d references\n", numReferencesResolved);
Logf("Resolved %d references\n", numReferencesResolved);
// Remove need to build
buildObject.definition->isLoaded = true;
@ -1120,7 +1120,7 @@ int BuildExecuteCompileTimeFunctions(EvaluatorEnvironment& environment,
buildObject.stage = BuildStage_Finished;
if (log.buildProcess)
printf("Successfully built, loaded, and executed %s\n",
Logf("Successfully built, loaded, and executed %s\n",
buildObject.definition->name.c_str());
}
@ -1160,7 +1160,7 @@ int BuildEvaluateReferences(EvaluatorEnvironment& environment, int& numErrorsOut
const char* defName = definition.name.c_str();
if (log.buildReasons)
printf("Checking to build %s\n", defName);
Logf("Checking to build %s\n", defName);
// Can it be built in the current environment?
bool canBuild = true;
@ -1207,7 +1207,7 @@ int BuildEvaluateReferences(EvaluatorEnvironment& environment, int& numErrorsOut
if (referenceStatus.guessState != GuessState_Resolved)
{
if (log.buildReasons)
printf("\tRequired code has been loaded\n");
Log("\tRequired code has been loaded\n");
hasRelevantChangeOccurred = true;
}
@ -1219,7 +1219,7 @@ int BuildEvaluateReferences(EvaluatorEnvironment& environment, int& numErrorsOut
// If we know we are missing a compile time function, we won't try to
// guess
if (log.buildReasons)
printf("\tCannot build until %s is loaded\n",
Logf("\tCannot build until %s is loaded\n",
referenceStatus.name->contents.c_str());
referenceStatus.guessState = GuessState_WaitingForLoad;
@ -1252,7 +1252,7 @@ int BuildEvaluateReferences(EvaluatorEnvironment& environment, int& numErrorsOut
if (referenceStatus.guessState == GuessState_None)
{
if (log.buildReasons)
printf("\tCannot build until %s is guessed. Guessing now\n",
Logf("\tCannot build until %s is guessed. Guessing now\n",
referenceStatus.name->contents.c_str());
// Find all the times the definition makes this reference
@ -1302,11 +1302,11 @@ int BuildEvaluateReferences(EvaluatorEnvironment& environment, int& numErrorsOut
if (log.compileTimeBuildObjects && !definitionsToBuild.empty())
{
int numToBuild = (int)definitionsToBuild.size();
printf("Building %d compile-time object%c\n", numToBuild, numToBuild > 1 ? 's' : ' ');
Logf("Building %d compile-time object%c\n", numToBuild, numToBuild > 1 ? 's' : ' ');
for (BuildObject& buildObject : definitionsToBuild)
{
printf("\t%s\n", buildObject.definition->name.c_str());
Logf("\t%s\n", buildObject.definition->name.c_str());
}
}
@ -1324,11 +1324,11 @@ bool EvaluateResolveReferences(EvaluatorEnvironment& environment)
for (ObjectDefinitionPair& definitionPair : environment.definitions)
{
ObjectDefinition& definition = definitionPair.second;
printf("%s %s:\n", objectTypeToString(definition.type), definition.name.c_str());
Logf("%s %s:\n", objectTypeToString(definition.type), definition.name.c_str());
for (ObjectReferenceStatusPair& reference : definition.references)
{
ObjectReferenceStatus& referenceStatus = reference.second;
printf("\t%s\n", referenceStatus.name->contents.c_str());
Logf("\t%s\n", referenceStatus.name->contents.c_str());
}
}
}
@ -1361,7 +1361,7 @@ bool EvaluateResolveReferences(EvaluatorEnvironment& environment)
bool codeModifiedByHook = false;
if (!hook(environment, codeModifiedByHook))
{
printf("error: hook returned failure\n");
Log("error: hook returned failure\n");
numBuildResolveErrors += 1;
break;
}
@ -1375,10 +1375,10 @@ bool EvaluateResolveReferences(EvaluatorEnvironment& environment)
// Check whether everything is resolved
if (log.phases)
printf("\nResult:\n");
Log("\nResult:\n");
if (numBuildResolveErrors)
printf("Failed with %d errors.\n", numBuildResolveErrors);
Logf("Failed with %d errors.\n", numBuildResolveErrors);
int errors = 0;
for (const ObjectDefinitionPair& definitionPair : environment.definitions)
@ -1456,7 +1456,7 @@ EvaluatorEnvironment::~EvaluatorEnvironment()
{
if (!comptimeTokens.empty())
{
printf(
Log(
"Warning: environmentDestroyInvalidateTokens() has not been called. This will leak "
"memory.\n Call it once you are certain no tokens in any expansions will be "
"referenced.\n");
@ -1482,7 +1482,7 @@ void environmentDestroyInvalidateTokens(EvaluatorEnvironment& environment)
if (!tokenizeLinePrintError(g_environmentCompileTimeVariableDestroySignature,
__FILE__, __LINE__, expectedSignature))
{
printf(
Log(
"error: failed to tokenize "
"g_environmentCompileTimeVariableDestroySignature! Internal code "
"error. Compile time variable memory will leak\n");
@ -1494,7 +1494,7 @@ void environmentDestroyInvalidateTokens(EvaluatorEnvironment& environment)
findObjectDefinition(environment, destroyFuncName.c_str());
if (!destroyFuncDefinition)
{
printf(
Log(
"error: could not find compile-time variable destroy function to verify "
"signature. Internal code error?\n");
continue;
@ -1515,7 +1515,7 @@ void environmentDestroyInvalidateTokens(EvaluatorEnvironment& environment)
}
else
{
printf(
Logf(
"error: destruction function '%s' for compile-time variable '%s' was not "
"loaded before the environment started destruction. Was it ever defined, or "
"defined but not required? Memory will leak\n",
@ -1619,16 +1619,16 @@ bool searchForFileInPaths(const char* shortPath, const char* encounteredInFile,
SafeSnprinf(foundFilePathOut, foundFilePathOutSize, "%s/%s", relativePathBuffer, shortPath);
if (log.fileSearch)
printf("File exists? %s (", foundFilePathOut);
Logf("File exists? %s (", foundFilePathOut);
if (fileExists(foundFilePathOut))
{
if (log.fileSearch)
printf("yes)\n");
Log("yes)\n");
return true;
}
if (log.fileSearch)
printf("no)\n");
Log("no)\n");
}
for (const std::string& path : searchPaths)
@ -1636,16 +1636,16 @@ bool searchForFileInPaths(const char* shortPath, const char* encounteredInFile,
SafeSnprinf(foundFilePathOut, foundFilePathOutSize, "%s/%s", path.c_str(), shortPath);
if (log.fileSearch)
printf("File exists? %s (", foundFilePathOut);
Logf("File exists? %s (", foundFilePathOut);
if (fileExists(foundFilePathOut))
{
if (log.fileSearch)
printf("yes)\n");
Log("yes)\n");
return true;
}
if (log.fileSearch)
printf("no)\n");
Log("no)\n");
}
return false;
@ -1660,11 +1660,11 @@ bool searchForFileInPathsWithError(const char* shortPath, const char* encountere
foundFilePathOutSize))
{
ErrorAtToken(blameToken, "file not found! Checked the following paths:");
printf("Checked if relative to %s\n", encounteredInFile);
printf("Checked search paths:\n");
Logf("Checked if relative to %s\n", encounteredInFile);
Log("Checked search paths:\n");
for (const std::string& path : searchPaths)
{
printf("\t%s\n", path.c_str());
Logf("\t%s\n", path.c_str());
}
return false;
}


+ 7
- 7
src/FileUtilities.cpp View File

@ -142,7 +142,7 @@ void makeAbsoluteOrRelativeToWorkingDir(const char* filePath, char* bufferOut, i
return;
}
// printf("workingDirAbsolute %s\nfilePathAbsolute %s\n", workingDirAbsolute, filePathAbsolute);
// Logf("workingDirAbsolute %s\nfilePathAbsolute %s\n", workingDirAbsolute, filePathAbsolute);
size_t workingDirPathLength = strlen(workingDirAbsolute);
if (strncmp(workingDirAbsolute, filePathAbsolute, workingDirPathLength) == 0)
@ -178,7 +178,7 @@ void makeAbsoluteOrRelativeToWorkingDir(const char* filePath, char* bufferOut, i
// {
// char resultBuffer[MAX_PATH_LENGTH] = {0};
// makeAbsoluteOrRelativeToWorkingDir(testCases[i], resultBuffer, ArraySize(resultBuffer));
// printf("%s = %s\n\n", testCases[i], resultBuffer);
// Logf("%s = %s\n\n", testCases[i], resultBuffer);
// }
// return 0;
// }
@ -212,7 +212,7 @@ bool copyBinaryFileTo(const char* srcFilename, const char* destFilename)
if (!srcFile || !destFile)
{
perror("fopen: ");
printf("error: failed to copy %s to %s", srcFilename, destFilename);
Logf("error: failed to copy %s to %s", srcFilename, destFilename);
return false;
}
@ -224,7 +224,7 @@ bool copyBinaryFileTo(const char* srcFilename, const char* destFilename)
fclose(destFile);
if (log.fileSystem)
printf("Wrote %s\n", destFilename);
Logf("Wrote %s\n", destFilename);
return true;
}
@ -236,7 +236,7 @@ bool copyFileTo(const char* srcFilename, const char* destFilename)
if (!srcFile || !destFile)
{
perror("fopen: ");
printf("error: failed to copy %s to %s", srcFilename, destFilename);
Logf("error: failed to copy %s to %s", srcFilename, destFilename);
return false;
}
@ -248,7 +248,7 @@ bool copyFileTo(const char* srcFilename, const char* destFilename)
fclose(destFile);
if (log.fileSystem)
printf("Wrote %s\n", destFilename);
Logf("Wrote %s\n", destFilename);
return true;
}
@ -261,7 +261,7 @@ bool moveFile(const char* srcFilename, const char* destFilename)
if (remove(srcFilename) != 0)
{
perror("remove: ");
printf("Failed to remove %s\n", srcFilename);
Logf("Failed to remove %s\n", srcFilename);
return false;
}


+ 14
- 17
src/GeneratorHelpers.cpp View File

@ -20,7 +20,7 @@ void StripInvocation(int& startTokenIndex, int& endTokenIndex)
int FindCloseParenTokenIndex(const std::vector<Token>& tokens, int startTokenIndex)
{
if (tokens[startTokenIndex].type != TokenType_OpenParen)
printf("Warning: FindCloseParenTokenIndex() expects to start on the opening parenthesis\n");
Log("Warning: FindCloseParenTokenIndex() expects to start on the opening parenthesis\n");
int depth = 0;
int numTokens = tokens.size();
@ -99,7 +99,7 @@ bool isSpecialSymbol(const Token& token)
}
else
{
printf("Warning: isSpecialSymbol() expects only Symbol types\n");
Log("Warning: isSpecialSymbol() expects only Symbol types\n");
return true;
}
}
@ -301,7 +301,7 @@ bool CreateDefinitionCopyMacroExpanded(const ObjectDefinition& definition,
{
if (!(definition.type == ObjectType_Function || definition.type == ObjectType_Variable))
{
printf(
Logf(
"error: CreateDefinitionCopyMacroExpanded() called on definition type %s which is not "
"explicitly supported by this function. Check CreateDefinitionCopyMacroExpanded() and "
"ensure your type's signature is understood, and that your type is tracking its macro "
@ -312,8 +312,7 @@ bool CreateDefinitionCopyMacroExpanded(const ObjectDefinition& definition,
if (!definition.definitionInvocation)
{
printf(
"error: CreateDefinitionCopyMacroExpanded() called on definition which did not set "
Log("error: CreateDefinitionCopyMacroExpanded() called on definition which did not set "
"invocation token. This is necessary to accurately copy the definition\n");
return false;
}
@ -341,7 +340,7 @@ void PushBackTokenExpression(std::vector<Token>& output, const Token* startToken
{
if (!startToken)
{
printf("error: PushBackTokenExpression() received null token\n");
Log("error: PushBackTokenExpression() received null token\n");
return;
}
@ -619,8 +618,7 @@ bool tokenizedCTypeToString_Recursive(const std::vector<Token>& tokens, int star
{
if (&typeOutput == &afterNameOutput)
{
printf(
"Error: tokenizedCTypeToString_Recursive() requires a separate output buffer for "
Log("Error: tokenizedCTypeToString_Recursive() requires a separate output buffer for "
"after-name types\n");
return false;
}
@ -840,8 +838,7 @@ bool CompileTimeFunctionSignatureMatches(EvaluatorEnvironment& environment, cons
"printed below (' = name does not need to match):");
printTokens(expectedSignature);
printf("too many/few tokens. %i need %lu\n", numArgumentsProvided,
expectedSignature.size());
Logf("too many/few tokens. %i need %lu\n", numArgumentsProvided, expectedSignature.size());
return false;
}
for (unsigned int i = 0; i < expectedSignature.size() && currentUserArgToken != endUserArgs;
@ -897,7 +894,7 @@ bool CStatementOutput(EvaluatorEnvironment& environment, const EvaluatorContext&
{
if (operation[i].argumentIndex < 0)
{
printf("Error: Expected valid argument index for start of splice list\n");
Log("Error: Expected valid argument index for start of splice list\n");
return false;
}
int startSpliceListIndex =
@ -947,7 +944,7 @@ bool CStatementOutput(EvaluatorEnvironment& environment, const EvaluatorContext&
{
if (operation[i].argumentIndex < 0)
{
printf("Error: Expected valid argument index for expression\n");
Log("Error: Expected valid argument index for expression\n");
return false;
}
int startTypeIndex = getExpectedArgument("expected type", tokens, startTokenIndex,
@ -968,7 +965,7 @@ bool CStatementOutput(EvaluatorEnvironment& environment, const EvaluatorContext&
{
if (operation[i].argumentIndex < 0)
{
printf("Error: Expected valid argument index for expression\n");
Log("Error: Expected valid argument index for expression\n");
return false;
}
int startExpressionIndex =
@ -989,7 +986,7 @@ bool CStatementOutput(EvaluatorEnvironment& environment, const EvaluatorContext&
{
if (operation[i].argumentIndex < 0)
{
printf("Error: Expected valid argument index for expression\n");
Log("Error: Expected valid argument index for expression\n");
return false;
}
int startExpressionIndex =
@ -1008,7 +1005,7 @@ bool CStatementOutput(EvaluatorEnvironment& environment, const EvaluatorContext&
{
if (operation[i].argumentIndex < 0)
{
printf("Error: Expected valid argument index for expression\n");
Log("Error: Expected valid argument index for expression\n");
return false;
}
// We're actually fine with no arguments
@ -1031,7 +1028,7 @@ bool CStatementOutput(EvaluatorEnvironment& environment, const EvaluatorContext&
{
if (operation[i].argumentIndex < 0)
{
printf("Error: Expected valid argument index for body\n");
Log("Error: Expected valid argument index for body\n");
return false;
}
int startBodyIndex = getExpectedArgument("expected body", tokens, startTokenIndex,
@ -1049,7 +1046,7 @@ bool CStatementOutput(EvaluatorEnvironment& environment, const EvaluatorContext&
break;
}
default:
printf("Output type not handled\n");
Log("Output type not handled\n");
return false;
}
}


+ 4
- 5
src/Generators.cpp View File

@ -88,7 +88,7 @@ bool SetProcessCommandArguments(EvaluatorEnvironment& environment, const std::ve
"suitable for this command):");
for (unsigned int i = 0; i < ArraySize(symbolsToCommandTypes); ++i)
{
printf("\t%s\n", symbolsToCommandTypes[i].symbolName);
Logf("\t%s\n", symbolsToCommandTypes[i].symbolName);
}
return false;
}
@ -521,7 +521,7 @@ bool AddCSearchDirectoryGenerator(EvaluatorEnvironment& environment,
{
ErrorAtToken(destinationToken, "unrecognized destination. Available destinations:");
for (unsigned int i = 0; i < ArraySize(possibleDestinations); ++i)
printf("\t%s\n", possibleDestinations[i].name);
Logf("\t%s\n", possibleDestinations[i].name);
return false;
}
@ -1409,7 +1409,7 @@ static ComptimeTokenArgContainedType ComptimeParseTokenArgumentType(const Token&
ErrorAtToken(expectedType, "unrecognized type. Recognized types listed below.");
for (unsigned int i = 0; i < ArraySize(containedTypeOptions); ++i)
{
printf("\t%s\n", containedTypeOptions[i].keyword);
Logf("\t%s\n", containedTypeOptions[i].keyword);
}
}
@ -1623,8 +1623,7 @@ static bool ComptimeGenerateTokenArguments(const std::vector<Token>& tokens, int
&currentToken);
break;
default:
printf(
"ComptimeGenerateTokenArguments: Programmer missing type. Internal "
Log("ComptimeGenerateTokenArguments: Programmer missing type. Internal "
"code error\n");
return false;
}


+ 14
- 15
src/Main.cpp View File

@ -25,11 +25,11 @@ void printHelp(const CommandLineOption* options, int numOptions)
"Copyright (c) 2020 Macoy Madson.\n\n"
"USAGE: cakelisp [options] <input .cake files>\nAll options must precede .cake files.\n\n"
"OPTIONS:\n";
printf("%s", helpString);
Logf("%s", helpString);
for (int optionIndex = 0; optionIndex < numOptions; ++optionIndex)
{
printf(" %s\n %s\n\n", options[optionIndex].handle, options[optionIndex].help);
Logf(" %s\n %s\n\n", options[optionIndex].handle, options[optionIndex].help);
}
}
@ -88,7 +88,7 @@ int main(int numArguments, char* arguments[])
if (numArguments == 1)
{
printf("Error: expected file(s) to evaluate\n\n");
Log("Error: expected file(s) to evaluate\n\n");
printHelp(options, ArraySize(options));
return 1;
}
@ -111,7 +111,7 @@ int main(int numArguments, char* arguments[])
{
if (startFiles < numArguments)
{
printf("Error: Options must precede files\n\n");
Log("Error: Options must precede files\n\n");
printHelp(options, ArraySize(options));
return 1;
}
@ -130,7 +130,7 @@ int main(int numArguments, char* arguments[])
if (!foundOption)
{
printf("Error: Unrecognized argument %s\n\n", arguments[i]);
Logf("Error: Unrecognized argument %s\n\n", arguments[i]);
printHelp(options, ArraySize(options));
return 1;
}
@ -143,7 +143,7 @@ int main(int numArguments, char* arguments[])
if (filesToEvaluate.empty())
{
printf("Error: expected file(s) to evaluate\n\n");
Log("Error: expected file(s) to evaluate\n\n");
printHelp(options, ArraySize(options));
return 1;
}
@ -155,8 +155,7 @@ int main(int numArguments, char* arguments[])
{
if (ignoreCachedFiles)
{
printf(
"cache will be used for output, but files from previous runs will be ignored "
Log("cache will be used for output, but files from previous runs will be ignored "
"(--ignore-cache)\n");
moduleManager.environment.useCachedFiles = false;
}
@ -184,10 +183,10 @@ int main(int numArguments, char* arguments[])
}
if (log.phases)
printf("Successfully generated files\n");
Log("Successfully generated files\n");
if (log.phases)
printf("\nBuild:\n");
Log("\nBuild:\n");
std::vector<std::string> builtOutputs;
if (!moduleManagerBuild(moduleManager, builtOutputs))
@ -199,11 +198,11 @@ int main(int numArguments, char* arguments[])
if (executeOutput)
{
if (log.phases)
printf("\nExecute:\n");
Log("\nExecute:\n");
if (builtOutputs.empty())
{
printf("error: --execute: No executables were output\n");
Log("error: --execute: No executables were output\n");
moduleManagerDestroy(moduleManager);
return 1;
}
@ -225,7 +224,7 @@ int main(int numArguments, char* arguments[])
if (runProcess(arguments, &status) != 0)
{
printf("error: execution of %s failed\n", output.c_str());
Logf("error: execution of %s failed\n", output.c_str());
free((void*)executablePath);
free((void*)commandLineArguments[0]);
moduleManagerDestroy(moduleManager);
@ -239,8 +238,8 @@ int main(int numArguments, char* arguments[])
if (status != 0)
{
printf("error: execution of %s returned non-zero exit code %d\n", output.c_str(),
status);
Logf("error: execution of %s returned non-zero exit code %d\n", output.c_str(),
status);
moduleManagerDestroy(moduleManager);
// Why not return the exit code? Because some exit codes end up becoming 0 after the
// mod 256. I'm not really sure how other programs handle this


+ 41
- 43
src/ModuleManager.cpp View File

@ -53,8 +53,7 @@ void moduleManagerInitialize(ModuleManager& manager)
GeneratorOutput* compTimeOutput = new GeneratorOutput;
moduleDefinition.output = compTimeOutput;
if (!addObjectDefinition(manager.environment, moduleDefinition))
printf(
"error: <global> couldn't be added. Was module manager initialized twice? Things "
Log("error: <global> couldn't be added. Was module manager initialized twice? Things "
"will definitely break\n");
}
@ -105,7 +104,7 @@ void moduleManagerInitialize(ModuleManager& manager)
manager.environment.useCachedFiles = true;
makeDirectory(cakelispWorkingDir);
if (log.fileSystem || log.phases)
printf("Using cache at %s\n", cakelispWorkingDir);
Logf("Using cache at %s\n", cakelispWorkingDir);
// By always searching relative to CWD, any subsequent imports with the module filename will
// resolve correctly
@ -144,13 +143,13 @@ bool moduleLoadTokenizeValidate(const char* filename, const std::vector<Token>**
while (fgets(lineBuffer, sizeof(lineBuffer), file))
{
if (log.tokenization)
printf("%s", lineBuffer);
Logf("%s", lineBuffer);
const char* error =
tokenizeLine(lineBuffer, filename, lineNumber, *tokens_CREATIONONLY);
if (error != nullptr)
{
printf("%s:%d: error: %s\n", filename, lineNumber, error);
Logf("%s:%d: error: %s\n", filename, lineNumber, error);
delete tokens_CREATIONONLY;
return false;
@ -164,11 +163,11 @@ bool moduleLoadTokenizeValidate(const char* filename, const std::vector<Token>**
}
if (log.tokenization)
printf("Tokenized %d lines\n", lineNumber - 1);
Logf("Tokenized %d lines\n", lineNumber - 1);
if (tokens->empty())
{
printf("error: empty file. Please remove from system, or add (ignore)\n");
Log("error: empty file. Please remove from system, or add (ignore)\n");
delete tokens;
return false;
}
@ -181,7 +180,7 @@ bool moduleLoadTokenizeValidate(const char* filename, const std::vector<Token>**
if (log.tokenization)
{
printf("\nResult:\n");
Log("\nResult:\n");
// No need to validate, we already know it's safe
int nestingDepth = 0;
@ -189,13 +188,13 @@ bool moduleLoadTokenizeValidate(const char* filename, const std::vector<Token>**
{
printIndentToDepth(nestingDepth);
printf("%s", tokenTypeToString(token.type));
Logf("%s", tokenTypeToString(token.type));
bool printRanges = true;
if (printRanges)
{
printf("\t\tline %d, from line character %d to %d\n", token.lineNumber,
token.columnStart, token.columnEnd);
Logf("\t\tline %d, from line character %d to %d\n", token.lineNumber,
token.columnStart, token.columnEnd);
}
if (token.type == TokenType_OpenParen)
@ -210,7 +209,7 @@ bool moduleLoadTokenizeValidate(const char* filename, const std::vector<Token>**
if (!token.contents.empty())
{
printIndentToDepth(nestingDepth);
printf("\t%s\n", token.contents.c_str());
Logf("\t%s\n", token.contents.c_str());
}
}
}
@ -241,7 +240,7 @@ bool moduleManagerAddEvaluateFile(ModuleManager& manager, const char* filename,
#ifdef UNIX
perror("failed to normalize filename: ");
#else
printf("error: could not normalize filename, or file not found: %s\n", filename);
Logf("error: could not normalize filename, or file not found: %s\n", filename);
#endif
return false;
}
@ -253,7 +252,7 @@ bool moduleManagerAddEvaluateFile(ModuleManager& manager, const char* filename,
const char* normalizedProspectiveModuleFilename = makeAbsolutePath_Allocated(".", filename);
if (!normalizedProspectiveModuleFilename)
{
printf("error: failed to normalize path %s", filename);
Logf("error: failed to normalize path %s", filename);
free((void*)normalizedFilename);
return false;
}
@ -262,7 +261,7 @@ bool moduleManagerAddEvaluateFile(ModuleManager& manager, const char* filename,
if (!normalizedModuleFilename)
{
printf("error: failed to normalize path %s", module->filename);
Logf("error: failed to normalize path %s", module->filename);
free((void*)normalizedProspectiveModuleFilename);
free((void*)normalizedFilename);
return false;
@ -274,7 +273,7 @@ bool moduleManagerAddEvaluateFile(ModuleManager& manager, const char* filename,
*moduleOut = module;
if (log.imports)
printf("Already loaded %s\n", normalizedFilename);
Logf("Already loaded %s\n", normalizedFilename);
free((void*)normalizedFilename);
free((void*)normalizedProspectiveModuleFilename);
free((void*)normalizedModuleFilename);
@ -291,7 +290,7 @@ bool moduleManagerAddEvaluateFile(ModuleManager& manager, const char* filename,
// This stage cleans up after itself if it fails
if (!moduleLoadTokenizeValidate(newModule->filename, &newModule->tokens))
{
printf("error: failed to tokenize %s\n", newModule->filename);
Logf("error: failed to tokenize %s\n", newModule->filename);
delete newModule;
free((void*)normalizedFilename);
return false;
@ -318,7 +317,7 @@ bool moduleManagerAddEvaluateFile(ModuleManager& manager, const char* filename,
// cannot destroy it until we're done evaluating everything
if (numErrors)
{
printf("error: failed to evaluate %s\n", newModule->filename);
Logf("error: failed to evaluate %s\n", newModule->filename);
return false;
}
@ -326,7 +325,7 @@ bool moduleManagerAddEvaluateFile(ModuleManager& manager, const char* filename,
*moduleOut = newModule;
if (log.imports)
printf("Loaded %s\n", newModule->filename);
Logf("Loaded %s\n", newModule->filename);
return true;
}
@ -352,12 +351,12 @@ static bool createBuildOutputDirectory(EvaluatorEnvironment& environment, std::s
if (!writeStringToBuffer(cakelispWorkingDir, &writeHead, outputDirName, sizeof(outputDirName)))
{
printf("error: ran out of space writing build configuration output directory name\n");
Log("error: ran out of space writing build configuration output directory name\n");
return false;
}
if (!writeCharToBuffer('/', &writeHead, outputDirName, sizeof(outputDirName)))
{
printf("error: ran out of space writing build configuration output directory name\n");
Log("error: ran out of space writing build configuration output directory name\n");
return false;
}
@ -366,7 +365,7 @@ static bool createBuildOutputDirectory(EvaluatorEnvironment& environment, std::s
const std::string& label = environment.buildConfigurationLabels[i];
if (!writeStringToBuffer(label.c_str(), &writeHead, outputDirName, sizeof(outputDirName)))
{
printf("error: ran out of space writing build configuration output directory name\n");
Log("error: ran out of space writing build configuration output directory name\n");
break;
}
@ -375,8 +374,7 @@ static bool createBuildOutputDirectory(EvaluatorEnvironment& environment, std::s
{
if (!writeCharToBuffer('-', &writeHead, outputDirName, sizeof(outputDirName)))
{
printf(
"error: ran out of space writing build configuration output directory name\n");
Log("error: ran out of space writing build configuration output directory name\n");
break;
}
}
@ -388,7 +386,7 @@ static bool createBuildOutputDirectory(EvaluatorEnvironment& environment, std::s
makeDirectory(outputDirName);
if (log.fileSystem || log.phases)
printf("Outputting artifacts to %s\n", outputDirName);
Logf("Outputting artifacts to %s\n", outputDirName);
outputDirOut = outputDirName;
@ -448,7 +446,7 @@ bool moduleManagerWriteGeneratedOutput(ModuleManager& manager)
}
if (log.phases || log.performance)
printf("Processed %d lines\n", g_totalLinesTokenized);
Logf("Processed %d lines\n", g_totalLinesTokenized);
return true;
}
@ -505,7 +503,7 @@ bool moduleManagerBuild(ModuleManager& manager, std::vector<std::string>& builtO
{
if (!hook(manager, module))
{
printf("error: hook returned failure. Aborting build\n");
Log("error: hook returned failure. Aborting build\n");
builtObjectsFree(builtObjects);
return false;
}
@ -535,11 +533,11 @@ bool moduleManagerBuild(ModuleManager& manager, std::vector<std::string>& builtO
}
if (log.buildProcess)
printf("Build module %s\n", module->sourceOutputName.c_str());
Logf("Build module %s\n", module->sourceOutputName.c_str());
for (ModuleDependency& dependency : module->dependencies)
{
if (log.buildProcess)
printf("\tRequires %s\n", dependency.name.c_str());
Logf("\tRequires %s\n", dependency.name.c_str());
// Cakelisp files are built at the module manager level, so we need not concern
// ourselves with them
@ -558,7 +556,7 @@ bool moduleManagerBuild(ModuleManager& manager, std::vector<std::string>& builtO
compilerObjectExtension, buildObjectName, sizeof(buildObjectName)))
{
delete newBuiltObject;
printf("error: failed to create suitable output filename");
Log("error: failed to create suitable output filename");
builtObjectsFree(builtObjects);
return false;
}
@ -587,7 +585,7 @@ bool moduleManagerBuild(ModuleManager& manager, std::vector<std::string>& builtO
manager.buildOutputDir.c_str(), module->sourceOutputName.c_str(),
compilerObjectExtension, buildObjectName, sizeof(buildObjectName)))
{
printf("error: failed to create suitable output filename");
Log("error: failed to create suitable output filename");
builtObjectsFree(builtObjects);
return false;
}
@ -618,8 +616,8 @@ bool moduleManagerBuild(ModuleManager& manager, std::vector<std::string>& builtO
object->filename.c_str()))
{
if (log.buildProcess)
printf("Skipping compiling %s (using cached object)\n",
object->sourceFilename.c_str());
Logf("Skipping compiling %s (using cached object)\n",
object->sourceFilename.c_str());
}
else
{
@ -661,7 +659,7 @@ bool moduleManagerBuild(ModuleManager& manager, std::vector<std::string>& builtO
buildCommand, buildTimeInputs, ArraySize(buildTimeInputs));
if (!buildArguments)
{
printf("error: failed to construct build arguments\n");
Log("error: failed to construct build arguments\n");
builtObjectsFree(builtObjects);
return false;
}
@ -672,7 +670,7 @@ bool moduleManagerBuild(ModuleManager& manager, std::vector<std::string>& builtO
if (runProcess(compileArguments, &object->buildStatus) != 0)
{
printf("error: failed to invoke compiler\n");
Log("error: failed to invoke compiler\n");
free(buildArguments);
builtObjectsFree(builtObjects);
return false;
@ -725,13 +723,13 @@ bool moduleManagerBuild(ModuleManager& manager, std::vector<std::string>& builtO
int buildResult = object->buildStatus;
if (buildResult != 0)
{
printf("error: failed to make target %s\n", object->filename.c_str());
Logf("error: failed to make target %s\n", object->filename.c_str());
succeededBuild = false;
continue;
}
if (log.buildProcess)
printf("Need to link %s\n", object->filename.c_str());
Logf("Need to link %s\n", object->filename.c_str());
++numObjectsToLink;
@ -749,14 +747,14 @@ bool moduleManagerBuild(ModuleManager& manager, std::vector<std::string>& builtO
if (!needsLink)
{
if (log.buildProcess)
printf("Skipping linking (no built objects are newer than cached executable)\n");
Log("Skipping linking (no built objects are newer than cached executable)\n");
// TODO: There's no easy way to know whether this exe is the current build configuration's
// output exe, so copy it every time
{
std::string finalOutputName;
if (log.fileSystem)
printf("Copying executable from cache\n");
Log("Copying executable from cache\n");
if (!manager.environment.executableOutput.empty())
finalOutputName = manager.environment.executableOutput;
else
@ -770,7 +768,7 @@ bool moduleManagerBuild(ModuleManager& manager, std::vector<std::string>& builtO
addExecutablePermission(finalOutputName.c_str());
printf("No changes needed for %s\n", finalOutputName.c_str());
Logf("No changes needed for %s\n", finalOutputName.c_str());
builtOutputs.push_back(finalOutputName);
}
@ -799,7 +797,7 @@ bool moduleManagerBuild(ModuleManager& manager, std::vector<std::string>& builtO
{
if (!preLinkHook(manager, linkCommand, linkTimeInputs, ArraySize(linkTimeInputs)))
{
printf("error: hook returned failure. Aborting build\n");
Log("error: hook returned failure. Aborting build\n");
builtObjectsFree(builtObjects);
return false;
}
@ -840,7 +838,7 @@ bool moduleManagerBuild(ModuleManager& manager, std::vector<std::string>& builtO
{
std::string finalOutputName;
if (log.fileSystem)
printf("Copying executable from cache\n");
Log("Copying executable from cache\n");
if (!manager.environment.executableOutput.empty())
finalOutputName = manager.environment.executableOutput;
else
@ -854,7 +852,7 @@ bool moduleManagerBuild(ModuleManager& manager, std::vector<std::string>& builtO
addExecutablePermission(finalOutputName.c_str());
printf("Successfully built and linked %s\n", finalOutputName.c_str());
Logf("Successfully built and linked %s\n", finalOutputName.c_str());
builtOutputs.push_back(finalOutputName);
}


+ 12
- 12
src/RunProcess.cpp View File

@ -39,19 +39,19 @@ void systemExecute(const char* fileToExecute, char** arguments)
// pid_t pid;
execvp(fileToExecute, arguments);
perror("RunProcess execvp() error: ");
printf("Failed to execute %s\n", fileToExecute);
Logf("Failed to execute %s\n", fileToExecute);
#endif
}
void subprocessReceiveStdOut(const char* processOutputBuffer)
{
printf("%s", processOutputBuffer);
Logf("%s", processOutputBuffer);
}
// TODO: Make separate pipe for std err?
// void subprocessReceiveStdErr(const char* processOutputBuffer)
// {
// printf("%s", processOutputBuffer);
// Logf("%s", processOutputBuffer);
// }
int runProcess(const RunProcessArguments& arguments, int* statusOut)
@ -59,12 +59,12 @@ int runProcess(const RunProcessArguments& arguments, int* statusOut)
#ifdef UNIX
if (log.processes)
{
printf("RunProcess command: ");
Log("RunProcess command: ");
for (const char** arg = arguments.arguments; *arg != nullptr; ++arg)
{
printf("%s ", *arg);
Logf("%s ", *arg);
}
printf("\n");
Log("\n");
}
int pipeFileDescriptors[2] = {0};
@ -123,7 +123,7 @@ int runProcess(const RunProcessArguments& arguments, int* statusOut)
}
if (log.processes)
printf("Set working directory to %s\n", arguments.workingDir);
Logf("Set working directory to %s\n", arguments.workingDir);
}
systemExecute(arguments.fileToExecute, nonConstArguments);
@ -144,7 +144,7 @@ int runProcess(const RunProcessArguments& arguments, int* statusOut)
close(pipeFileDescriptors[PipeWrite]);
if (log.processes)
printf("Created child process %d\n", pid);
Logf("Created child process %d\n", pid);
std::string command = "";
for (const char** arg = arguments.arguments; *arg != nullptr; ++arg)
@ -187,7 +187,7 @@ void waitForAllProcessesClosed(SubprocessOnOutputFunc onOutput)
// It's pretty useful to see the command which resulted in failure
if (*process.statusOut != 0)
printf("%s\n", process.command.c_str());
Logf("%s\n", process.command.c_str());
#endif
}
@ -197,8 +197,8 @@ void waitForAllProcessesClosed(SubprocessOnOutputFunc onOutput)
void PrintProcessArguments(const char** processArguments)
{
for (const char** argument = processArguments; *argument; ++argument)
printf("%s ", *argument);
printf("\n");
Logf("%s ", *argument);
Log("\n");
}
// The array will need to be deleted, but the array members will not
@ -228,7 +228,7 @@ const char** MakeProcessArgumentsFromCommand(ProcessCommand& command,
}
if (!found)
{
printf("error: command missing input\n");
Log("error: command missing input\n");
return nullptr;
}
}


+ 11
- 11
src/Tokenizer.cpp View File

@ -99,7 +99,7 @@ const char* tokenizeLine(const char* inputLine, const char* source, unsigned int
if (std::isspace(*currentChar) || *currentChar == '\n' || isParenthesis)
{
if (log.tokenization)
printf("%s\n", contentsBuffer);
Logf("%s\n", contentsBuffer);
Token symbol = {TokenType_Symbol, EmptyString, source,
lineNumber, columnStart, currentColumn};
CopyContentsAndReset(symbol.contents);
@ -226,19 +226,19 @@ void printFormattedToken(const Token& token)
switch (token.type)
{
case TokenType_OpenParen:
printf("(");
Log("(");
break;
case TokenType_CloseParen:
printf(")");
Log(")");
break;
case TokenType_Symbol:
printf("%s", token.contents.c_str());
Logf("%s", token.contents.c_str());
break;
case TokenType_String:
printf("\"%s\"", token.contents.c_str());
Logf("\"%s\"", token.contents.c_str());
break;
default:
printf("Unknown type");
Log("Unknown type");
break;
}
}
@ -298,10 +298,10 @@ static void printTokensInternal(const std::vector<Token>& tokens, bool prettyPri
if (prettyPrint && previousTokenType == TokenType_CloseParen &&
token.type == TokenType_OpenParen)
{
printf("\n");
Log("\n");
for (int i = 0; i < depth; ++i)
{
printf(" ");
Log(" ");
}
}
@ -315,13 +315,13 @@ static void printTokensInternal(const std::vector<Token>& tokens, bool prettyPri
if ((tokenIsSymbolOrString && !previousTokenIsParen) ||
(tokenIsSymbolOrString && previousTokenType == TokenType_CloseParen) ||
(token.type == TokenType_OpenParen && previousTokenIsSymbolOrString))
printf(" ");
Log(" ");
printFormattedToken(token);
previousTokenType = token.type;
}
printf("\n");
Log("\n");
}
void printTokens(const std::vector<Token>& tokens)
@ -364,7 +364,7 @@ bool tokenizeLinePrintError(const char* inputLine, const char* source, unsigned
const char* error = tokenizeLine(inputLine, source, lineNumber, tokensOut);
if (error != nullptr)
{
printf("error: %s\n", error);
Logf("error: %s\n", error);
return false;
}
return true;


+ 7
- 6
src/Utilities.cpp View File

@ -1,14 +1,15 @@
#include "Utilities.hpp"
#include "Logging.hpp"
#include <stdio.h>
#include "Logging.hpp"
std::string EmptyString;
void printIndentToDepth(int depth)
{
for (int i = 0; i < depth; ++i)
printf("\t");
Log("\t");
}
FILE* fileOpen(const char* filename, const char* mode)
@ -17,13 +18,13 @@ FILE* fileOpen(const char* filename, const char* mode)
file = fopen(filename, mode);
if (!file)
{
printf("error: Could not open %s\n", filename);
Logf("error: Could not open %s\n", filename);
return nullptr;
}
else
{
if (log.fileSystem)
printf("Opened %s\n", filename);
Logf("Opened %s\n", filename);
}
return file;
}
@ -35,7 +36,7 @@ bool writeCharToBuffer(char c, char** at, char* bufferStart, int bufferSize)
char* endOfBuffer = bufferStart + (bufferSize - 1);
if (*at > endOfBuffer)
{
printf("error: buffer of size %d was too small. String will be cut off\n", bufferSize);
Logf("error: buffer of size %d was too small. String will be cut off\n", bufferSize);
*endOfBuffer = '\0';
return false;
}
@ -52,7 +53,7 @@ bool writeStringToBuffer(const char* str, char** at, char* bufferStart, int buff
char* endOfBuffer = bufferStart + (bufferSize - 1);
if (*at > endOfBuffer)
{
printf("error: buffer of size %d was too small. String will be cut off\n", bufferSize);
Logf("error: buffer of size %d was too small. String will be cut off\n", bufferSize);
*(endOfBuffer) = '\0';
return false;
}


+ 18
- 15
src/Utilities.hpp View File

@ -1,10 +1,9 @@
#pragma once
#include <cstdio> // sprintf
#include <algorithm> // std::find
#include <cstdio> // sprintf
#include <string>
#include <algorithm> // std::find