Browse Source

References now resolved after definition compiled

* Refactored StringOutput interface to use functions instead
* Added Splice system for inserting output in references
* References are followed and resolved with the new definition
* Build with -fPIC for dynamic loading
ModuleSystem
Macoy Madson 10 months ago
parent
commit
5189e0abbf
14 changed files with 496 additions and 277 deletions
  1. +206
    -56
      src/Evaluator.cpp
  2. +20
    -6
      src/Evaluator.hpp
  3. +2
    -1
      src/EvaluatorEnums.hpp
  4. +38
    -0
      src/GeneratorHelpers.cpp
  5. +9
    -0
      src/GeneratorHelpers.hpp
  6. +103
    -141
      src/Generators.cpp
  7. +2
    -2
      src/Jamfile
  8. +17
    -21
      src/Main.cpp
  9. +10
    -1
      src/OutputPreambles.cpp
  10. +5
    -0
      src/OutputPreambles.hpp
  11. +2
    -4
      src/RunProcess.cpp
  12. +76
    -43
      src/Writer.cpp
  13. +2
    -2
      src/Writer.hpp
  14. +4
    -0
      test/Dependencies.cake

+ 206
- 56
src/Evaluator.cpp View File

@ -34,6 +34,14 @@ MacroFunc findMacro(EvaluatorEnvironment& environment, const char* functionName)
return nullptr;
}
bool isCompileTimeCodeLoaded(EvaluatorEnvironment& environment, const ObjectDefinition& definition)
{
if (definition.type == ObjectType_CompileTimeMacro)
return findMacro(environment, definition.name->contents.c_str()) != nullptr;
else
return findGenerator(environment, definition.name->contents.c_str()) != nullptr;
}
bool addObjectDefinition(EvaluatorEnvironment& environment, ObjectDefinition& definition)
{
ObjectDefinitionMap::iterator findIt = environment.definitions.find(definition.name->contents);
@ -52,7 +60,7 @@ bool addObjectDefinition(EvaluatorEnvironment& environment, ObjectDefinition& de
}
void addObjectReference(EvaluatorEnvironment& environment, EvaluatorContext context,
ObjectReference& reference)
const Token& referenceNameToken, ObjectReference& reference)
{
// Default to the module requiring the reference, for top-level references
const char* moduleDefinitionName = "<module>";
@ -62,17 +70,7 @@ void addObjectReference(EvaluatorEnvironment& environment, EvaluatorContext cont
definitionName = context.definitionName->contents;
}
// TODO This will become necessary once references store referents (append not overwrite)
// ObjectReferenceMap::iterator findIt = environment.references.find(definitionName);
// if (findIt == environment.references.end())
// {
environment.references[definitionName] = reference;
// }
// else
// {
// }
// Add the reference requirement to the definition we're working on
// Add the reference requirement to the definition it occurred in
ObjectDefinitionMap::iterator findDefinition = environment.definitions.find(definitionName);
if (findDefinition == environment.definitions.end())
{
@ -89,7 +87,25 @@ void addObjectReference(EvaluatorEnvironment& environment, EvaluatorContext cont
}
else
{
findDefinition->second.references[reference.name->contents] = {reference.name};
ObjectReferenceStatusMap::iterator findRef =
findDefinition->second.references.find(referenceNameToken.contents.c_str());
if (findRef == findDefinition->second.references.end())
findDefinition->second.references[referenceNameToken.contents.c_str()] = {
&referenceNameToken};
}
// Add the reference to the reference pool. This makes it easier to find all places where it is
// referenced during resolve time
ObjectReferencePoolMap::iterator findIt = environment.referencePools.find(definitionName);
if (findIt == environment.referencePools.end())
{
ObjectReferencePool newPool = {};
newPool.references.push_back(reference);
environment.referencePools[referenceNameToken.contents] = std::move(newPool);
}
else
{
findIt->second.references.push_back(reference);
}
}
@ -98,11 +114,18 @@ int getNextFreeBuildId(EvaluatorEnvironment& environment)
return ++environment.nextFreeBuildId;
}
bool isCompileTimeObject(ObjectType type)
{
return type == ObjectType_CompileTimeMacro || type == ObjectType_CompileTimeGenerator;
}
//
// Evaluator
//
// Dispatch to a generator or expand a macro and evaluate its output recursively
// Dispatch to a generator or expand a macro and evaluate its output recursively. If the reference
// is unknown, add it to a list so EvaluateResolveReferences() can come back and decide what to do
// with it. Only EvaluateResolveReferences() decides whether to create a C/C++ invocation
bool HandleInvocation_Recursive(EvaluatorEnvironment& environment, const EvaluatorContext& context,
const std::vector<Token>& tokens, int invocationStartIndex,
GeneratorOutput& output)
@ -131,6 +154,13 @@ bool HandleInvocation_Recursive(EvaluatorEnvironment& environment, const Evaluat
macroOutputTokens = macroOutputTokensNoConst_CREATIONONLY;
}
// The macro had no output, but we won't let that bother us
if (macroOutputTokens->empty())
{
delete macroOutputTokens;
return true;
}
// Don't even try to validate the code if the macro wasn't satisfied
if (!macroSucceeded)
{
@ -188,12 +218,17 @@ bool HandleInvocation_Recursive(EvaluatorEnvironment& environment, const Evaluat
else
{
// Reference resolver v2
ObjectReference newReference;
ObjectReference newReference = {};
newReference.tokens = &tokens;
newReference.startIndex = invocationStartIndex;
newReference.name = &invocationName;
newReference.isRequired = context.isRequired;
addObjectReference(environment, context, newReference);
// Make room for whatever gets output once this reference is resolved
newReference.spliceOutput = new GeneratorOutput;
addObjectReference(environment, context, invocationName, newReference);
// We push in a StringOutMod_Splice as a sentinel that the splice list needs to be
// checked. Otherwise, it will be a no-op to Writer. It's useful to have this sentinel
// so that multiple splices take up space and will then maintain sequential order
addSpliceOutput(output.source, newReference.spliceOutput, &invocationStart);
// We don't know what this is. We cannot guess it is a C/C++ function yet, because it
// could be a generator or macro invocation that hasn't been defined yet. Leave a note
@ -210,11 +245,6 @@ bool HandleInvocation_Recursive(EvaluatorEnvironment& environment, const Evaluat
// the output list, the splice will redirect to an external output list. It is the
// responsibility of the Writer to watch for splices
unknownInvocation.output = &output.source;
// We push in a StringOutMod_Splice as a sentinel that the splice list needs to be
// checked. Otherwise, it will be a no-op to Writer. It's useful to have this sentinel
// so that multiple splices take up space and will then maintain sequential order
output.source.push_back(
{EmptyString, StringOutMod_Splice, &invocationStart, &invocationStart});
unknownInvocation.spliceOutputIndex = output.source.size() - 1;
if (context.isMacroOrGeneratorDefinition)
@ -290,19 +320,18 @@ int EvaluateGenerate_Recursive(EvaluatorEnvironment& environment, const Evaluato
char secondChar = token.contents.size() > 1 ? token.contents[1] : 0;
if (firstChar == '\'' || isdigit(firstChar) ||
(firstChar == '-' && (secondChar == '.' || isdigit(secondChar))))
output.source.push_back(
{token.contents, StringOutMod_None, &token, &token});
addStringOutput(output.source, token.contents, StringOutMod_None, &token);
else
{
// Potential lisp name. Convert
output.source.push_back(
{token.contents, StringOutMod_ConvertVariableName, &token, &token});
addStringOutput(output.source, token.contents,
StringOutMod_ConvertVariableName, &token);
}
break;
}
case TokenType_String:
output.source.push_back(
{token.contents, StringOutMod_SurroundWithQuotes, &token, &token});
addStringOutput(output.source, token.contents, StringOutMod_SurroundWithQuotes,
&token);
break;
default:
ErrorAtTokenf(token,
@ -404,6 +433,7 @@ bool EvaluateResolveReferencesV1(EvaluatorEnvironment& environment)
const char* objectTypeToString(ObjectType type);
// Determine what needs to be built, iteratively
// TODO This can be made faster. I did the most naive version first, for now
void PropagateRequiredToReferences(EvaluatorEnvironment& environment)
{
@ -444,16 +474,20 @@ void PropagateRequiredToReferences(EvaluatorEnvironment& environment)
void OnCompileProcessOutput(const char* output)
{
// TODO C/C++ error to Cakelisp token mapper
}
int BuildEvaluateReferences(EvaluatorEnvironment& environment)
int BuildEvaluateReferences(EvaluatorEnvironment& environment, int& numErrorsOut)
{
int numReferencesResolved = 0;
enum BuildStage
{
BuildStage_None,
BuildStage_Compiling,
BuildStage_Linking,
BuildStage_Loading,
BuildStage_ResolvingReferences,
BuildStage_Finished
};
@ -474,8 +508,17 @@ int BuildEvaluateReferences(EvaluatorEnvironment& environment)
{
ObjectDefinition& definition = definitionPair.second;
// Does it need to be built?
if (definition.isRequired && definition.type == ObjectType_CompileTimeFunction)
if (definition.isRequired && isCompileTimeObject(definition.type))
{
// Is it already in the environment?
// TODO: Shortcut?
bool compileTimeCodeLoaded =
definition.type == ObjectType_CompileTimeMacro ?
findMacro(environment, definition.name->contents.c_str()) != nullptr :
findGenerator(environment, definition.name->contents.c_str()) != nullptr;
if (compileTimeCodeLoaded)
continue;
// Can it be built in the current environment?
bool canBuild = true;
for (ObjectReferenceStatusPair& reference : definition.references)
@ -487,10 +530,16 @@ int BuildEvaluateReferences(EvaluatorEnvironment& environment)
environment.definitions.find(referenceStatus.name->contents);
if (findIt != environment.definitions.end())
{
if (findIt->second.type == ObjectType_CompileTimeFunction)
if (isCompileTimeObject(findIt->second.type))
{
// TODO: Check for code loaded
if (true)
bool refCompileTimeCodeLoaded =
isCompileTimeCodeLoaded(environment, findIt->second);
if (refCompileTimeCodeLoaded)
{
// No need to build. It has already been handled. Built objects
// immediately resolve references
}
else
{
// If we know we are missing a compile time function, we won't try to
// guess
@ -498,17 +547,13 @@ int BuildEvaluateReferences(EvaluatorEnvironment& environment)
referenceStatus.name->contents.c_str());
canBuild = false;
}
else
{
// Invoke, or mark this reference as resolved, or something
}
}
// TODO: Building references to non-comptime functions at comptime
}
else
{
// Guess it's a C invocation
// TODO Guess it's a C invocation
}
}
@ -523,12 +568,13 @@ int BuildEvaluateReferences(EvaluatorEnvironment& environment)
}
// Spin up as many compile processes as necessary
// TODO: Limit number spawned at once?
// TODO: Combine sure-thing builds into batches (ones where we know all references)
// TODO: Instead of creating files, pipe straight to compiler?
// TODO: Make pipeline able to start e.g. linker while other objects are still compiling
// NOTE: definitionsToBuild must not be resized from when runProcess() is called until
// waitForAllProcessesClosed(), else the status pointer could be invalidated
const int maxProcessesSpawned = 8;
int currentNumProcessesSpawned = 0;
for (BuildObject& buildObject : definitionsToBuild)
{
ObjectDefinition* definition = buildObject.definition;
@ -543,7 +589,7 @@ int BuildEvaluateReferences(EvaluatorEnvironment& environment)
// TODO: Make these come from the top
NameStyleSettings nameSettings;
WriterFormatSettings formatSettings;
WriterOutputSettings outputSettings;
WriterOutputSettings outputSettings = {};
outputSettings.sourceHeading = macroSourceHeading;
outputSettings.sourceFooter = macroSourceFooter;
outputSettings.sourceCakelispFilename = sourceOutputName;
@ -565,7 +611,8 @@ int BuildEvaluateReferences(EvaluatorEnvironment& environment)
// TODO: Get arguments all the way from the top
// If not null terminated, the call will fail
char* arguments[] = {fileToExec, strdup("-c"), sourceOutputName, strdup("-Isrc/"), nullptr};
char* arguments[] = {fileToExec, strdup("-c"), sourceOutputName,
strdup("-Isrc/"), strdup("-fPIC"), nullptr};
RunProcessArguments compileArguments = {};
compileArguments.fileToExecute = fileToExec;
compileArguments.arguments = arguments;
@ -574,6 +621,15 @@ int BuildEvaluateReferences(EvaluatorEnvironment& environment)
// TODO: Abort building if cannot invoke compiler
// return 0;
}
// TODO This could be made smarter by allowing more spawning right when a process closes,
// instead of starting in waves
++currentNumProcessesSpawned;
if (currentNumProcessesSpawned >= maxProcessesSpawned)
{
waitForAllProcessesClosed(OnCompileProcessOutput);
currentNumProcessesSpawned = 0;
}
}
// The result of the builds will go straight to our definitionsToBuild
@ -656,13 +712,59 @@ int BuildEvaluateReferences(EvaluatorEnvironment& environment)
continue;
}
// Add to environment
if (buildObject.definition->type == ObjectType_CompileTimeMacro)
{
environment.macros[buildObject.definition->name->contents] =
(MacroFunc)compileTimeFunction;
}
else if (buildObject.definition->type == ObjectType_CompileTimeGenerator)
{
environment.generators[buildObject.definition->name->contents] =
(GeneratorFunc)compileTimeFunction;
}
buildObject.stage = BuildStage_ResolvingReferences;
// Resolve references
ObjectReferencePoolMap::iterator referencePoolIt =
environment.referencePools.find(buildObject.definition->name->contents);
if (referencePoolIt == environment.referencePools.end())
{
printf(
"Error: built an object which had no references. It should not have been "
"required. There must be a problem with Cakelisp internally\n");
continue;
}
// TODO: Performance: Iterate backwards and quit as soon as any resolved refs are found
for (ObjectReference& reference : referencePoolIt->second.references)
{
if (reference.isResolved)
continue;
// Evaluate from that reference
int result =
EvaluateGenerate_Recursive(environment, reference.context, *reference.tokens,
reference.startIndex, *reference.spliceOutput);
// Regardless of what evaluate turned up, we resolved this as far as we care. Trying
// again isn't going to change the number of errors
reference.isResolved = true;
numErrorsOut += result;
++numReferencesResolved;
}
printf("Resolved %d references\n", numReferencesResolved);
// Remove need to build
buildObject.stage = BuildStage_Finished;
printf("Successfully built and loaded %s\n",
buildObject.definition->name->contents.c_str());
}
return 0;
return numReferencesResolved;
}
bool EvaluateResolveReferences(EvaluatorEnvironment& environment)
@ -682,30 +784,66 @@ bool EvaluateResolveReferences(EvaluatorEnvironment& environment)
// This must be done in passes in case evaluation created more definitions. There's probably a
// smarter way, but I'll do it in this brute-force style first
int numReferencesResolved = 0;
int numBuildResolveErrors = 0;
do
{
PropagateRequiredToReferences(environment);
numReferencesResolved = BuildEvaluateReferences(environment);
numReferencesResolved = BuildEvaluateReferences(environment, numBuildResolveErrors);
if (numBuildResolveErrors)
break;
} while (numReferencesResolved);
// Check everything is resolved
// Check whether everything is resolved
int errors = 0;
for (const ObjectDefinitionPair& definitionPair : environment.definitions)
{
const ObjectDefinition& definition = definitionPair.second;
printf("%s:\n", definition.name->contents.c_str());
if (definition.isRequired)
{
// TODO: Add ready-build runtime function check
if (!findMacro(environment, definition.name->contents.c_str()) &&
!findGenerator(environment, definition.name->contents.c_str()))
if (isCompileTimeObject(definition.type))
{
// TODO: Add note for who required the object
ErrorAtToken(*definition.name, "Failed to build required object");
++errors;
// TODO: Add ready-build runtime function check
if (!findMacro(environment, definition.name->contents.c_str()) &&
!findGenerator(environment, definition.name->contents.c_str()))
{
// TODO: Add note for who required the object
ErrorAtToken(*definition.name, "Failed to build required object");
++errors;
}
else
NoteAtToken(*definition.name, "built successfully");
}
else
NoteAtToken(*definition.name, "Built successfully");
{
// Check all references have been resolved for regular generated code
std::vector<const Token*> missingDefinitionNames;
for (const ObjectReferenceStatusPair& reference : definition.references)
{
const ObjectReferenceStatus& referenceStatus = reference.second;
// TODO: (Performance) Add shortcut in reference
ObjectDefinitionMap::iterator findIt =
environment.definitions.find(referenceStatus.name->contents);
if (findIt != environment.definitions.end())
{
if (isCompileTimeObject(findIt->second.type) &&
!isCompileTimeCodeLoaded(environment, findIt->second))
{
missingDefinitionNames.push_back(findIt->second.name);
++errors;
}
}
}
if (!missingDefinitionNames.empty())
{
ErrorAtTokenf(*definition.name, "failed to generate %s",
definition.name->contents.c_str());
for (const Token* missingDefinitionName : missingDefinitionNames)
NoteAtToken(*missingDefinitionName,
"missing compile-time function defined here");
}
}
}
else
{
@ -713,7 +851,7 @@ bool EvaluateResolveReferences(EvaluatorEnvironment& environment)
}
}
return errors == 0;
return errors == 0 && numBuildResolveErrors == 0;
}
// This serves only as a warning. I want to be very explicit with the lifetime of tokens
@ -736,6 +874,16 @@ void environmentDestroyInvalidateTokens(EvaluatorEnvironment& environment)
}
environment.compileTimeFunctions.clear();
for (ObjectReferencePoolPair& referencePoolPair : environment.referencePools)
{
for (ObjectReference& reference : referencePoolPair.second.references)
{
delete reference.spliceOutput;
}
referencePoolPair.second.references.clear();
}
environment.referencePools.clear();
for (const std::vector<Token>* macroExpansion : environment.macroExpansions)
delete macroExpansion;
environment.macroExpansions.clear();
@ -762,8 +910,10 @@ const char* objectTypeToString(ObjectType type)
{
case ObjectType_Function:
return "Function";
case ObjectType_CompileTimeFunction:
return "CompileTimeFunction";
case ObjectType_CompileTimeMacro:
return "Macro";
case ObjectType_CompileTimeGenerator:
return "Generator";
default:
return "Unknown";
}


+ 20
- 6
src/Evaluator.hpp View File

@ -9,12 +9,19 @@
struct NameStyleSettings;
struct Token;
struct GeneratorOutput;
// Rather than needing to allocate and edit a buffer eventually equal to the size of the final
// output, store output operations instead. This also facilitates source <-> generated mapping data
struct StringOutput
{
// TODO: Putting this in a union means we need to write a destructor which can detect when to
// destroy output
//union {
std::string output;
GeneratorOutput* spliceOutput;
// };
StringOutputModifierFlags modifiers;
// Used to correlate Cakelisp code with generated output code
@ -69,7 +76,7 @@ struct EvaluatorContext
// Macro and generator definitions need to be resolved first
bool isMacroOrGeneratorDefinition;
// TODO Explain
// Whether or not the code in this context is required to be generated, i.e. isn't "dead" code
bool isRequired;
// Associate all unknown references with this definition
const Token* definitionName;
@ -141,6 +148,7 @@ struct ObjectDefinition
// Objects can be referenced by other objects, but something in the chain must be required in
// order for the objects to be built. Required-ness spreads from the top level module scope
bool isRequired;
// Unique references, for dependency checking
ObjectReferenceStatusMap references;
// Used only for compile-time functions
@ -152,20 +160,26 @@ struct ObjectDefinition
struct ObjectReference
{
const Token* name;
const std::vector<Token>* tokens;
int startIndex;
EvaluatorContext context;
bool isRequired;
// This needs to be a pointer in case the references array gets moved while we are iterating it
GeneratorOutput* spliceOutput;
bool isResolved;
};
// ObjectReferentMap referents;
struct ObjectReferencePool
{
std::vector<ObjectReference> references;
};
// NOTE: See comment in BuildEvaluateReferences() before changing this data structure. The current
// implementation assumes references to values will not be invalidated if the hash map changes
typedef std::unordered_map<std::string, ObjectDefinition> ObjectDefinitionMap;
typedef std::pair<const std::string, ObjectDefinition> ObjectDefinitionPair;
typedef std::unordered_map<std::string, ObjectReference> ObjectReferenceMap;
typedef std::unordered_map<std::string, ObjectReferencePool> ObjectReferencePoolMap;
typedef std::pair<const std::string, ObjectReferencePool> ObjectReferencePoolPair;
// Unlike context, which can't be changed, environment can be changed.
// Use care when modifying the environment. Only add things once you know things have succeeded.
@ -191,7 +205,7 @@ struct EvaluatorEnvironment
std::vector<UnknownReference> unknownReferencesForCompileTime;
ObjectDefinitionMap definitions;
ObjectReferenceMap references;
ObjectReferencePoolMap referencePools;
// Used to ensure unique filenames for compile-time artifacts
int nextFreeBuildId;


+ 2
- 1
src/EvaluatorEnums.hpp View File

@ -64,5 +64,6 @@ enum UnknownReferenceType
enum ObjectType
{
ObjectType_Function,
ObjectType_CompileTimeFunction,
ObjectType_CompileTimeMacro,
ObjectType_CompileTimeGenerator,
};

+ 38
- 0
src/GeneratorHelpers.cpp View File

@ -160,3 +160,41 @@ void addModifierToStringOutput(StringOutput& operation, StringOutputModifierFlag
{
operation.modifiers = (StringOutputModifierFlags)((int)operation.modifiers | (int)flag);
}
void addStringOutput(std::vector<StringOutput>& output, const std::string& symbol,
StringOutputModifierFlags modifiers, const Token* startToken)
{
StringOutput newStringOutput = {};
newStringOutput.modifiers = modifiers;
newStringOutput.startToken = startToken;
newStringOutput.endToken = startToken;
newStringOutput.output = symbol;
output.push_back(std::move(newStringOutput));
}
void addLangTokenOutput(std::vector<StringOutput>& output, StringOutputModifierFlags modifiers,
const Token* startToken)
{
StringOutput newStringOutput = {};
newStringOutput.modifiers = modifiers;
newStringOutput.startToken = startToken;
newStringOutput.endToken = startToken;
output.push_back(std::move(newStringOutput));
}
void addSpliceOutput(std::vector<StringOutput>& output, GeneratorOutput* spliceOutput,
const Token* startToken)
{
StringOutput newStringOutput = {};
// No other modifiers are valid because splice is handled outside the normal writer
newStringOutput.modifiers = StringOutMod_Splice;
newStringOutput.startToken = startToken;
newStringOutput.endToken = startToken;
newStringOutput.spliceOutput = spliceOutput;
output.push_back(std::move(newStringOutput));
}

+ 9
- 0
src/GeneratorHelpers.hpp View File

@ -1,12 +1,14 @@
#pragma once
#include <vector>
#include <string>
#include "EvaluatorEnums.hpp"
#include "TokenEnums.hpp"
struct Token;
struct EvaluatorContext;
struct GeneratorOutput;
struct StringOutput;
void StripInvocation(int& startTokenIndex, int& endTokenIndex);
@ -32,3 +34,10 @@ int getExpectedArgument(const char* message, const std::vector<Token>& tokens, i
int getNumArguments(const std::vector<Token>& tokens, int startTokenIndex, int endTokenIndex);
bool isLastArgument(const std::vector<Token>& tokens, int startTokenIndex, int endTokenIndex);
void addModifierToStringOutput(StringOutput& operation, StringOutputModifierFlags flag);
void addStringOutput(std::vector<StringOutput>& output, const std::string& symbol,
StringOutputModifierFlags modifiers, const Token* startToken);
void addLangTokenOutput(std::vector<StringOutput>& output, StringOutputModifierFlags modifiers,
const Token* startToken);
void addSpliceOutput(std::vector<StringOutput>& output, GeneratorOutput* spliceOutput,
const Token* startToken);

+ 103
- 141
src/Generators.cpp View File

@ -66,11 +66,11 @@ bool PowerfulCImportGenerator(EvaluatorEnvironment& environment, const Evaluator
}
if (state == WithDefinitions)
output.source.push_back({std::string(includeBuffer), StringOutMod_NewlineAfter,
&currentToken, &currentToken});
addStringOutput(output.source, std::string(includeBuffer), StringOutMod_NewlineAfter,
&currentToken);
else if (state == WithDeclarations)
output.header.push_back({std::string(includeBuffer), StringOutMod_NewlineAfter,
&currentToken, &currentToken});
addStringOutput(output.header, std::string(includeBuffer), StringOutMod_NewlineAfter,
&currentToken);
output.imports.push_back({currentToken.contents, ImportLanguage_C, &currentToken});
}
@ -157,11 +157,11 @@ bool CIncludeGenerator(EvaluatorEnvironment& environment, const EvaluatorContext
}
if (state == WithDefinitions)
output.source.push_back(
{std::string(includeBuffer), StringOutMod_NewlineAfter, &pathToken, &pathToken});
addStringOutput(output.source, std::string(includeBuffer), StringOutMod_NewlineAfter,
&pathToken);
else if (state == WithDeclarations)
output.header.push_back(
{std::string(includeBuffer), StringOutMod_NewlineAfter, &pathToken, &pathToken});
addStringOutput(output.header, std::string(includeBuffer), StringOutMod_NewlineAfter,
&pathToken);
output.imports.push_back({pathToken.contents,
isCakeImport ? ImportLanguage_Cakelisp : ImportLanguage_C,
@ -195,9 +195,8 @@ bool tokenizedCTypeToString_Recursive(const std::vector<Token>& tokens, int star
return false;
}
typeOutput.push_back({tokens[startTokenIndex].contents.c_str(),
StringOutMod_ConvertTypeName, &tokens[startTokenIndex],
&tokens[startTokenIndex]});
addStringOutput(typeOutput, tokens[startTokenIndex].contents, StringOutMod_ConvertTypeName,
&tokens[startTokenIndex]);
return true;
}
@ -219,8 +218,7 @@ bool tokenizedCTypeToString_Recursive(const std::vector<Token>& tokens, int star
if (typeInvocation.contents.compare("const") == 0)
{
// Prepend const-ness
typeOutput.push_back(
{"const", StringOutMod_SpaceAfter, &typeInvocation, &typeInvocation});
addStringOutput(typeOutput, "const", StringOutMod_SpaceAfter, &typeInvocation);
int typeIndex = getExpectedArgument("const requires type", tokens, startTokenIndex, 1,
endTokenIndex);
@ -243,8 +241,8 @@ bool tokenizedCTypeToString_Recursive(const std::vector<Token>& tokens, int star
afterNameOutput))
return false;
typeOutput.push_back({typeInvocation.contents.c_str(), StringOutMod_None,
&typeInvocation, &typeInvocation});
addStringOutput(typeOutput, typeInvocation.contents.c_str(), StringOutMod_None,
&typeInvocation);
}
else if (typeInvocation.contents.compare("&&") == 0 ||
typeInvocation.contents.compare("rval-ref-to") == 0)
@ -258,7 +256,7 @@ bool tokenizedCTypeToString_Recursive(const std::vector<Token>& tokens, int star
afterNameOutput))
return false;
typeOutput.push_back({"&&", StringOutMod_None, &typeInvocation, &typeInvocation});
addStringOutput(typeOutput, "&&", StringOutMod_None, &typeInvocation);
}
else if (typeInvocation.contents.compare("<>") == 0)
{
@ -271,7 +269,7 @@ bool tokenizedCTypeToString_Recursive(const std::vector<Token>& tokens, int star
afterNameOutput))
return false;
typeOutput.push_back({"<", StringOutMod_None, &typeInvocation, &typeInvocation});
addStringOutput(typeOutput, "<", StringOutMod_None, &typeInvocation);
for (int startTemplateParameter = typeIndex + 1; startTemplateParameter < endTokenIndex;
++startTemplateParameter)
{
@ -284,9 +282,8 @@ bool tokenizedCTypeToString_Recursive(const std::vector<Token>& tokens, int star
return false;
if (!isLastArgument(tokens, startTemplateParameter, endTokenIndex))
typeOutput.push_back({EmptyString, StringOutMod_ListSeparator,
&tokens[startTemplateParameter],
&tokens[startTemplateParameter]});
addLangTokenOutput(typeOutput, StringOutMod_ListSeparator,
&tokens[startTemplateParameter]);
// Skip over tokens of the type we just parsed (the for loop increment will move us
// off the end paren)
@ -294,7 +291,7 @@ bool tokenizedCTypeToString_Recursive(const std::vector<Token>& tokens, int star
startTemplateParameter =
FindCloseParenTokenIndex(tokens, startTemplateParameter);
}
typeOutput.push_back({">", StringOutMod_None, &typeInvocation, &typeInvocation});
addStringOutput(typeOutput, ">", StringOutMod_None, &typeInvocation);
}
else if (typeInvocation.contents.compare("[]") == 0)
{
@ -323,17 +320,13 @@ bool tokenizedCTypeToString_Recursive(const std::vector<Token>& tokens, int star
return false;
// Array size specified as first argument
afterNameOutput.push_back(
{"[", StringOutMod_None, &typeInvocation, &typeInvocation});
afterNameOutput.push_back({tokens[firstArgIndex].contents.c_str(),
StringOutMod_None, &tokens[firstArgIndex],
&tokens[firstArgIndex]});
afterNameOutput.push_back(
{"]", StringOutMod_None, &typeInvocation, &typeInvocation});
addStringOutput(afterNameOutput, "[", StringOutMod_None, &typeInvocation);
addStringOutput(afterNameOutput, tokens[firstArgIndex].contents.c_str(),
StringOutMod_None, &tokens[firstArgIndex]);
addStringOutput(afterNameOutput, "]", StringOutMod_None, &typeInvocation);
}
else
afterNameOutput.push_back(
{"[]", StringOutMod_None, &typeInvocation, &typeInvocation});
addStringOutput(afterNameOutput, "[]", StringOutMod_None, &typeInvocation);
// Type parsing happens after the [] have already been appended because the array's type
// may include another array dimension, which must be specified after the current array
@ -466,17 +459,15 @@ bool DefunGenerator(EvaluatorEnvironment& environment, const EvaluatorContext& c
// TODO: Hot-reloading functions shouldn't be declared static, right?
if (isModuleLocal)
output.source.push_back({"static", StringOutMod_SpaceAfter, &tokens[startTokenIndex],
&tokens[startTokenIndex]});
addStringOutput(output.source, "static", StringOutMod_SpaceAfter, &tokens[startTokenIndex]);
if (returnTypeStart == -1)
{
// The type was implicit; blame the "defun"
output.source.push_back(
{"void", StringOutMod_SpaceAfter, &tokens[startTokenIndex], &tokens[startTokenIndex]});
addStringOutput(output.source, "void", StringOutMod_SpaceAfter, &tokens[startTokenIndex]);
if (!isModuleLocal)
output.header.push_back({"void", StringOutMod_SpaceAfter, &tokens[startTokenIndex],
&tokens[startTokenIndex]});
addStringOutput(output.header, "void", StringOutMod_SpaceAfter,
&tokens[startTokenIndex]);
}
else
{
@ -520,15 +511,15 @@ bool DefunGenerator(EvaluatorEnvironment& environment, const EvaluatorContext& c
output.header.insert(output.header.end(), typeOutput.begin(), typeOutput.end());
}
output.source.push_back(
{nameToken.contents, StringOutMod_ConvertFunctionName, &nameToken, &nameToken});
addStringOutput(output.source, nameToken.contents, StringOutMod_ConvertFunctionName,
&nameToken);
if (!isModuleLocal)
output.header.push_back(
{nameToken.contents, StringOutMod_ConvertFunctionName, &nameToken, &nameToken});
addStringOutput(output.header, nameToken.contents, StringOutMod_ConvertFunctionName,
&nameToken);
output.source.push_back({EmptyString, StringOutMod_OpenParen, &argsStart, &argsStart});
addLangTokenOutput(output.source, StringOutMod_OpenParen, &argsStart);
if (!isModuleLocal)
output.header.push_back({EmptyString, StringOutMod_OpenParen, &argsStart, &argsStart});
addLangTokenOutput(output.header, StringOutMod_OpenParen, &argsStart);
std::vector<FunctionArgumentMetadata> argumentsMetadata;
@ -553,12 +544,11 @@ bool DefunGenerator(EvaluatorEnvironment& environment, const EvaluatorContext& c
PushBackAll(output.header, typeOutput);
// Name
output.source.push_back({tokens[arg.nameIndex].contents, StringOutMod_ConvertVariableName,
&tokens[arg.nameIndex], &tokens[arg.nameIndex]});
addStringOutput(output.source, tokens[arg.nameIndex].contents,
StringOutMod_ConvertVariableName, &tokens[arg.nameIndex]);
if (!isModuleLocal)
output.header.push_back({tokens[arg.nameIndex].contents,
StringOutMod_ConvertVariableName, &tokens[arg.nameIndex],
&tokens[arg.nameIndex]});
addStringOutput(output.header, tokens[arg.nameIndex].contents,
StringOutMod_ConvertVariableName, &tokens[arg.nameIndex]);
// Array
PushBackAll(output.source, afterNameOutput);
@ -567,15 +557,10 @@ bool DefunGenerator(EvaluatorEnvironment& environment, const EvaluatorContext& c
if (i + 1 < numFunctionArguments)
{
// It's a little weird to have to pick who is responsible for the comma. In this case
// both the name and the type of the next arg are responsible, because there wouldn't be
// a comma if there wasn't a next arg
DefunArgument& nextArg = arguments[i + 1];
output.source.push_back({EmptyString, StringOutMod_ListSeparator,
&tokens[arg.nameIndex], &tokens[nextArg.startTypeIndex]});
addLangTokenOutput(output.source, StringOutMod_ListSeparator, &tokens[arg.nameIndex]);
if (!isModuleLocal)
output.header.push_back({EmptyString, StringOutMod_ListSeparator,
&tokens[arg.nameIndex], &tokens[nextArg.startTypeIndex]});
addLangTokenOutput(output.header, StringOutMod_ListSeparator,
&tokens[arg.nameIndex]);
}
// Argument metadata
@ -590,20 +575,16 @@ bool DefunGenerator(EvaluatorEnvironment& environment, const EvaluatorContext& c
}
}
output.source.push_back(
{EmptyString, StringOutMod_CloseParen, &tokens[endArgsIndex], &tokens[endArgsIndex]});
addLangTokenOutput(output.source, StringOutMod_CloseParen, &tokens[endArgsIndex]);
if (!isModuleLocal)
{
output.header.push_back(
{EmptyString, StringOutMod_CloseParen, &tokens[endArgsIndex], &tokens[endArgsIndex]});
addLangTokenOutput(output.header, StringOutMod_CloseParen, &tokens[endArgsIndex]);
// Forward declarations end with ;
output.header.push_back(
{EmptyString, StringOutMod_EndStatement, &tokens[endArgsIndex], &tokens[endArgsIndex]});
addLangTokenOutput(output.header, StringOutMod_EndStatement, &tokens[endArgsIndex]);
}
int startBodyIndex = endArgsIndex + 1;
output.source.push_back(
{EmptyString, StringOutMod_OpenBlock, &tokens[startBodyIndex], &tokens[startBodyIndex]});
addLangTokenOutput(output.source, StringOutMod_OpenBlock, &tokens[startBodyIndex]);
// Evaluate our body!
EvaluatorContext bodyContext = context;
@ -611,14 +592,13 @@ bool DefunGenerator(EvaluatorEnvironment& environment, const EvaluatorContext& c
bodyContext.definitionName = &nameToken;
// The statements will need to handle their ;
// TODO Remove this, we don't need it any more
StringOutput bodyDelimiterTemplate = {EmptyString, StringOutMod_None, nullptr, nullptr};
StringOutput bodyDelimiterTemplate = {};
int numErrors = EvaluateGenerateAll_Recursive(environment, bodyContext, tokens, startBodyIndex,
bodyDelimiterTemplate, output);
if (numErrors)
return false;
output.source.push_back(
{EmptyString, StringOutMod_CloseBlock, &tokens[endTokenIndex], &tokens[endTokenIndex]});
addLangTokenOutput(output.source, StringOutMod_CloseBlock, &tokens[endTokenIndex]);
output.functions.push_back({nameToken.contents, &tokens[startTokenIndex],
&tokens[endTokenIndex], std::move(argumentsMetadata)});
@ -638,9 +618,9 @@ bool FunctionInvocationGenerator(EvaluatorEnvironment& environment, const Evalua
const Token& funcNameToken = tokens[nameTokenIndex];
int endInvocationIndex = FindCloseParenTokenIndex(tokens, startTokenIndex);
output.source.push_back(
{funcNameToken.contents, StringOutMod_ConvertFunctionName, &funcNameToken, &funcNameToken});
output.source.push_back({EmptyString, StringOutMod_OpenParen, &funcNameToken, &funcNameToken});
addStringOutput(output.source, funcNameToken.contents, StringOutMod_ConvertFunctionName,
&funcNameToken);
addLangTokenOutput(output.source, StringOutMod_OpenParen, &funcNameToken);
// Arguments
int startArgsIndex = nameTokenIndex + 1;
@ -648,19 +628,17 @@ bool FunctionInvocationGenerator(EvaluatorEnvironment& environment, const Evalua
// Function invocations evaluate their arguments
EvaluatorContext functionInvokeContext = context;
functionInvokeContext.scope = EvaluatorScope_ExpressionsOnly;
StringOutput argumentDelimiterTemplate = {EmptyString, StringOutMod_ListSeparator, nullptr,
nullptr};
StringOutput argumentDelimiterTemplate = {};
argumentDelimiterTemplate.modifiers = StringOutMod_ListSeparator;
int numErrors =
EvaluateGenerateAll_Recursive(environment, functionInvokeContext, tokens, startArgsIndex,
argumentDelimiterTemplate, output);
if (numErrors)
return false;
output.source.push_back({EmptyString, StringOutMod_CloseParen, &tokens[endInvocationIndex],
&tokens[endInvocationIndex]});
addLangTokenOutput(output.source, StringOutMod_CloseParen, &tokens[endInvocationIndex]);
if (context.scope != EvaluatorScope_ExpressionsOnly)
output.source.push_back({EmptyString, StringOutMod_EndStatement,
&tokens[endInvocationIndex], &tokens[endInvocationIndex]});
addLangTokenOutput(output.source, StringOutMod_EndStatement, &tokens[endInvocationIndex]);
return true;
}
@ -713,13 +691,11 @@ bool VariableDeclarationGenerator(EvaluatorEnvironment& environment,
addModifierToStringOutput(typeOutput.back(), StringOutMod_SpaceAfter);
if (isGlobal)
output.header.push_back({"extern", StringOutMod_SpaceAfter, &tokens[startTokenIndex],
&tokens[startTokenIndex]});
addStringOutput(output.header, "extern", StringOutMod_SpaceAfter, &tokens[startTokenIndex]);
// else because no variable may be declared extern while also being static
// Automatically make module-declared variables static, reserving "static-var" for functions
else if (isStatic || context.scope == EvaluatorScope_Module)
output.source.push_back({"static", StringOutMod_SpaceAfter, &tokens[startTokenIndex],
&tokens[startTokenIndex]});
addStringOutput(output.source, "static", StringOutMod_SpaceAfter, &tokens[startTokenIndex]);
// Type
PushBackAll(output.source, typeOutput);
@ -727,11 +703,11 @@ bool VariableDeclarationGenerator(EvaluatorEnvironment& environment,
PushBackAll(output.header, typeOutput);
// Name
output.source.push_back({tokens[varNameIndex].contents, StringOutMod_ConvertVariableName,
&tokens[varNameIndex], &tokens[varNameIndex]});
addStringOutput(output.source, tokens[varNameIndex].contents, StringOutMod_ConvertVariableName,
&tokens[varNameIndex]);
if (isGlobal)
output.header.push_back({tokens[varNameIndex].contents, StringOutMod_ConvertVariableName,
&tokens[varNameIndex], &tokens[varNameIndex]});
addStringOutput(output.header, tokens[varNameIndex].contents,
StringOutMod_ConvertVariableName, &tokens[varNameIndex]);
// Array
PushBackAll(output.source, typeAfterNameOutput);
@ -749,10 +725,8 @@ bool VariableDeclarationGenerator(EvaluatorEnvironment& environment,
// Initialized
if (valueIndex < endInvocationIndex)
{
output.source.push_back(
{EmptyString, StringOutMod_SpaceAfter, &tokens[valueIndex], &tokens[valueIndex]});
output.source.push_back(
{"=", StringOutMod_SpaceAfter, &tokens[valueIndex], &tokens[valueIndex]});
addLangTokenOutput(output.source, StringOutMod_SpaceAfter, &tokens[valueIndex]);
addStringOutput(output.source, "=", StringOutMod_SpaceAfter, &tokens[valueIndex]);
EvaluatorContext expressionContext = context;
expressionContext.scope = EvaluatorScope_ExpressionsOnly;
@ -761,11 +735,9 @@ bool VariableDeclarationGenerator(EvaluatorEnvironment& environment,
return false;
}
output.source.push_back({EmptyString, StringOutMod_EndStatement, &tokens[endInvocationIndex],
&tokens[endInvocationIndex]});
addLangTokenOutput(output.source, StringOutMod_EndStatement, &tokens[endInvocationIndex]);
if (isGlobal)
output.header.push_back({EmptyString, StringOutMod_EndStatement,
&tokens[endInvocationIndex], &tokens[endInvocationIndex]});
addLangTokenOutput(output.header, StringOutMod_EndStatement, &tokens[endInvocationIndex]);
return true;
}
@ -827,13 +799,11 @@ bool ArrayAccessGenerator(EvaluatorEnvironment& environment, const EvaluatorCont
for (int offsetTokenIndex : offsetTokenIndices)
{
output.source.push_back(
{"[", StringOutMod_None, &tokens[offsetTokenIndex], &tokens[offsetTokenIndex]});
addStringOutput(output.source, "[", StringOutMod_None, &tokens[offsetTokenIndex]);
if (EvaluateGenerate_Recursive(environment, expressionContext, tokens, offsetTokenIndex,
output) != 0)
return false;
output.source.push_back(
{"]", StringOutMod_None, &tokens[offsetTokenIndex], &tokens[offsetTokenIndex]});
addStringOutput(output.source, "]", StringOutMod_None, &tokens[offsetTokenIndex]);
}
return true;
@ -868,7 +838,7 @@ bool DefMacroGenerator(EvaluatorEnvironment& environment, const EvaluatorContext
ObjectDefinition newMacroDef = {};
newMacroDef.name = &nameToken;
newMacroDef.type = ObjectType_CompileTimeFunction;
newMacroDef.type = ObjectType_CompileTimeMacro;
// Let the reference required propagation step handle this
// TODO: This can also just be a quick lookup to see whether the reference already exists
newMacroDef.isRequired = false;
@ -887,28 +857,26 @@ bool DefMacroGenerator(EvaluatorEnvironment& environment, const EvaluatorContext
// bool isModuleLocal = tokens[startTokenIndex + 1].contents.compare("defmacro-local") == 0;
// Macros must return success or failure
compTimeOutput->source.push_back(
{"bool", StringOutMod_SpaceAfter, &tokens[startTokenIndex], &tokens[startTokenIndex]});
addStringOutput(compTimeOutput->source, "bool", StringOutMod_SpaceAfter,
&tokens[startTokenIndex]);
compTimeOutput->source.push_back(
{nameToken.contents, StringOutMod_ConvertFunctionName, &nameToken, &nameToken});
addStringOutput(compTimeOutput->source, nameToken.contents, StringOutMod_ConvertFunctionName,
&nameToken);
compTimeOutput->source.push_back({EmptyString, StringOutMod_OpenParen, &argsStart, &argsStart});
addLangTokenOutput(compTimeOutput->source, StringOutMod_OpenParen, &argsStart);
// Macros always receive the same arguments
// TODO: Output macro arguments with proper output calls
compTimeOutput->source.push_back(
{"EvaluatorEnvironment& environment, const EvaluatorContext& context, const "
"std::vector<Token>& tokens, int startTokenIndex, std::vector<Token>& output",
StringOutMod_None, &argsStart, &argsStart});
addStringOutput(compTimeOutput->source,
"EvaluatorEnvironment& environment, const EvaluatorContext& context, const "
"std::vector<Token>& tokens, int startTokenIndex, std::vector<Token>& output",
StringOutMod_None, &argsStart);
int endArgsIndex = FindCloseParenTokenIndex(tokens, argsIndex);
compTimeOutput->source.push_back(
{EmptyString, StringOutMod_CloseParen, &tokens[endArgsIndex], &tokens[endArgsIndex]});
addLangTokenOutput(compTimeOutput->source, StringOutMod_CloseParen, &tokens[endArgsIndex]);
int startBodyIndex = endArgsIndex + 1;
compTimeOutput->source.push_back(
{EmptyString, StringOutMod_OpenBlock, &tokens[startBodyIndex], &tokens[startBodyIndex]});
addLangTokenOutput(compTimeOutput->source, StringOutMod_OpenBlock, &tokens[startBodyIndex]);
// Evaluate our body!
EvaluatorContext macroBodyContext = context;
@ -919,7 +887,7 @@ bool DefMacroGenerator(EvaluatorEnvironment& environment, const EvaluatorContext
macroBodyContext.isRequired = false;
macroBodyContext.definitionName = &nameToken;
// TODO Remove this, we don't need it any more
StringOutput bodyDelimiterTemplate = {EmptyString, StringOutMod_None, nullptr, nullptr};
StringOutput bodyDelimiterTemplate = {};
int numErrors =
EvaluateGenerateAll_Recursive(environment, macroBodyContext, tokens, startBodyIndex,
bodyDelimiterTemplate, *compTimeOutput);
@ -929,8 +897,7 @@ bool DefMacroGenerator(EvaluatorEnvironment& environment, const EvaluatorContext
return false;
}
compTimeOutput->source.push_back(
{EmptyString, StringOutMod_CloseBlock, &tokens[endTokenIndex], &tokens[endTokenIndex]});
addLangTokenOutput(compTimeOutput->source, StringOutMod_CloseBlock, &tokens[endTokenIndex]);
// Takes ownership of output
environment.compileTimeFunctions.push_back(newFunction);
@ -987,8 +954,8 @@ bool cStatementOutput(EvaluatorEnvironment& environment, const EvaluatorContext&
switch (operation[i].type)
{
case Keyword:
output.source.push_back({operation[i].keywordOrSymbol, StringOutMod_SpaceAfter,
&nameToken, &nameToken});
addStringOutput(output.source, operation[i].keywordOrSymbol,
StringOutMod_SpaceAfter, &nameToken);
break;
case Splice:
{
@ -1004,8 +971,9 @@ bool cStatementOutput(EvaluatorEnvironment& environment, const EvaluatorContext&
return false;
EvaluatorContext bodyContext = context;
bodyContext.scope = EvaluatorScope_ExpressionsOnly;
StringOutput spliceDelimiterTemplate = {operation[i].keywordOrSymbol,
StringOutMod_SpaceAfter, nullptr, nullptr};
StringOutput spliceDelimiterTemplate = {};
spliceDelimiterTemplate.output = operation[i].keywordOrSymbol;
spliceDelimiterTemplate.modifiers = StringOutMod_SpaceAfter;
int numErrors = EvaluateGenerateAll_Recursive(environment, bodyContext, tokens,
startSpliceListIndex,
spliceDelimiterTemplate, output);
@ -1014,32 +982,25 @@ bool cStatementOutput(EvaluatorEnvironment& environment, const EvaluatorContext&
break;
}
case OpenParen:
output.source.push_back(
{EmptyString, StringOutMod_OpenParen, &nameToken, &nameToken});
addLangTokenOutput(output.source, StringOutMod_OpenParen, &nameToken);
break;
case CloseParen:
output.source.push_back(
{EmptyString, StringOutMod_CloseParen, &nameToken, &nameToken});
addLangTokenOutput(output.source, StringOutMod_CloseParen, &nameToken);
break;
case OpenBlock:
output.source.push_back(
{EmptyString, StringOutMod_OpenBlock, &nameToken, &nameToken});
addLangTokenOutput(output.source, StringOutMod_OpenBlock, &nameToken);
break;
case CloseBlock:
output.source.push_back(
{EmptyString, StringOutMod_CloseBlock, &nameToken, &nameToken});
addLangTokenOutput(output.source, StringOutMod_CloseBlock, &nameToken);
break;
case OpenList:
output.source.push_back(
{EmptyString, StringOutMod_OpenList, &nameToken, &nameToken});
addLangTokenOutput(output.source, StringOutMod_OpenList, &nameToken);
break;
case CloseList:
output.source.push_back(
{EmptyString, StringOutMod_CloseList, &nameToken, &nameToken});
addLangTokenOutput(output.source, StringOutMod_CloseList, &nameToken);
break;
case EndStatement:
output.source.push_back(
{EmptyString, StringOutMod_EndStatement, &nameToken, &nameToken});
addLangTokenOutput(output.source, StringOutMod_EndStatement, &nameToken);
break;
case Expression:
{
@ -1074,8 +1035,9 @@ bool cStatementOutput(EvaluatorEnvironment& environment, const EvaluatorContext&
return false;
EvaluatorContext expressionContext = context;
expressionContext.scope = EvaluatorScope_ExpressionsOnly;
StringOutput listDelimiterTemplate = {EmptyString, StringOutMod_ListSeparator,
nullptr, nullptr};
StringOutput listDelimiterTemplate = {};
listDelimiterTemplate.modifiers = StringOutMod_ListSeparator;
if (EvaluateGenerateAll_Recursive(environment, expressionContext, tokens,
startExpressionIndex, listDelimiterTemplate,
output) != 0)
@ -1097,8 +1059,7 @@ bool cStatementOutput(EvaluatorEnvironment& environment, const EvaluatorContext&
bodyContext.scope = EvaluatorScope_Body;
// The statements will need to handle their ;
// TODO Remove delimiter, we don't need it
StringOutput bodyDelimiterTemplate = {EmptyString, StringOutMod_None, nullptr,
nullptr};
StringOutput bodyDelimiterTemplate = {};
int numErrors =
EvaluateGenerateAll_Recursive(environment, bodyContext, tokens, startBodyIndex,
bodyDelimiterTemplate, output);
@ -1151,9 +1112,9 @@ bool CStatementGenerator(EvaluatorEnvironment& environment, const EvaluatorConte
const CStatementOperation dereference[] = {{Keyword, "*", -1}, {Expression, nullptr, 1}};
const CStatementOperation addressOf[] = {{Keyword, "&", -1}, {Expression, nullptr, 1}};
// Similar to progn, but doesn't necessarily mean things run in order (this doesn't add barriers
// or anything). It's useful both for making arbitrary scopes and for making if blocks with
// multiple statements
// Similar to progn, but doesn't necessarily mean things run in order (this doesn't add
// barriers or anything). It's useful both for making arbitrary scopes and for making if
// blocks with multiple statements
const CStatementOperation blockStatement[] = {
{OpenBlock, nullptr, -1}, {Body, nullptr, 1}, {CloseBlock, nullptr, -1}};
@ -1182,8 +1143,8 @@ bool CStatementGenerator(EvaluatorEnvironment& environment, const EvaluatorConte
const CStatementOperation multiply[] = {{Splice, "*", 1}};
const CStatementOperation divide[] = {{Splice, "/", 1}};
const CStatementOperation modulus[] = {{Splice, "%", 1}};
// Always pre-increment, which matches what you'd expect given the invocation comes before the
// expression. It's also slightly faster, yadda yadda
// Always pre-increment, which matches what you'd expect given the invocation comes before
// the expression. It's also slightly faster, yadda yadda
const CStatementOperation increment[] = {{Keyword, "++", -1}, {Expression, nullptr, 1}};
const CStatementOperation decrement[] = {{Keyword, "--", -1}, {Expression, nullptr, 1}};
@ -1313,8 +1274,9 @@ bool SquareMacro(EvaluatorEnvironment& environment, const EvaluatorContext& cont
//
void importFundamentalGenerators(EvaluatorEnvironment& environment)
{
// I wanted to use c-import, but I encountered problems writing a regex for Jam which can handle
// both include and c-import. Anyways, this way C programmers have one less change to remember
// I wanted to use c-import, but I encountered problems writing a regex for Jam which can
// handle both include and c-import. Anyways, this way C programmers have one less change to
// remember
environment.generators["include"] = CIncludeGenerator;
environment.generators["defun"] = DefunGenerator;


+ 2
- 2
src/Jamfile View File

@ -13,8 +13,8 @@ DynamicLoader.cpp
MakeLocate cakelisp : ../cakelisp ;
Main dependencyTest : TestRefResolver.cpp
Utilities.cpp ;
# Main dependencyTest : TestRefResolver.cpp
# Utilities.cpp ;
# C++ on src/TestRefResolver.o = g++ ;
# Main runProcessTest : RunProcess.cpp ;


+ 17
- 21
src/Main.cpp View File

@ -6,6 +6,7 @@
#include "Converters.hpp"
#include "Evaluator.hpp"
#include "Generators.hpp"
#include "OutputPreambles.hpp"
#include "RunProcess.hpp"
#include "Tokenizer.hpp"
#include "Utilities.hpp"
@ -149,7 +150,8 @@ int main(int argc, char* argv[])
// TODO: Local functions can be left out if not referenced (in fact, they may warn in C if not)
moduleContext.isRequired = true;
GeneratorOutput generatedOutput;
StringOutput bodyDelimiterTemplate = {"", StringOutMod_NewlineAfter, nullptr, nullptr};
StringOutput bodyDelimiterTemplate ={};
bodyDelimiterTemplate.modifiers = StringOutMod_NewlineAfter;
int numErrors = EvaluateGenerateAll_Recursive(environment, moduleContext, *tokens,
/*startTokenIndex=*/0, bodyDelimiterTemplate,
generatedOutput);
@ -162,34 +164,28 @@ int main(int argc, char* argv[])
if (!EvaluateResolveReferences(environment))
{
// TODO Add compile time too
// if (!environment.unknownReferences.empty() ||
// !environment.unknownReferencesForCompileTime.empty())
// {
// for (const UnknownReference& reference : environment.unknownReferences)
// {
// ErrorAtTokenf(*reference.symbolReference, "reference to undefined symbol '%s'",
// reference.symbolReference->contents.c_str());
// }
// for (const UnknownReference& reference : environment.unknownReferencesForCompileTime)
// {
// ErrorAtTokenf(*reference.symbolReference, "reference to undefined symbol '%s'",
// reference.symbolReference->contents.c_str());
// }
environmentDestroyInvalidateTokens(environment);
delete tokens;
return 1;
// }
environmentDestroyInvalidateTokens(environment);
delete tokens;
return 1;
}
// Final output
{
NameStyleSettings nameSettings;
WriterFormatSettings formatSettings;
WriterOutputSettings outputSettings;
outputSettings.sourceCakelispFilename = filename;
char sourceHeadingBuffer[1024] = {0};
// TODO: hpp to h support
// TODO: Strip path from filename
PrintfBuffer(sourceHeadingBuffer, "#include \"%s.hpp\"\n%s", filename,
generatedSourceHeading ? generatedSourceHeading : "");
outputSettings.sourceHeading = sourceHeadingBuffer;
outputSettings.sourceFooter = generatedSourceFooter;
outputSettings.headerHeading = generatedHeaderHeading;
outputSettings.headerFooter = generatedHeaderFooter;
printf("\nResult:\n");
if (!writeGeneratorOutput(generatedOutput, nameSettings, formatSettings, outputSettings))


+ 10
- 1
src/OutputPreambles.cpp View File

@ -1,6 +1,15 @@
#include "OutputPreambles.hpp"
// Must use extern "C" for dynamic symbols, because otherwise name mangling makes things hard
const char* macroSourceHeading = "#include \"Evaluator.hpp\"\n#include \"EvaluatorEnums.hpp\"\nextern \"C\"\n{\n";
const char* macroSourceHeading =
"#include \"Evaluator.hpp\""
"\n#include \"EvaluatorEnums.hpp\""
"\n#include \"Tokenizer.hpp\""
"\nextern \"C\"\n{\n";
// Close extern "C" block
const char* macroSourceFooter = "}\n";
const char* generatedSourceHeading = nullptr;
const char* generatedSourceFooter = nullptr;
const char* generatedHeaderHeading = "#pragma once\n";
const char* generatedHeaderFooter = nullptr;

+ 5
- 0
src/OutputPreambles.hpp View File

@ -3,3 +3,8 @@
// All generated macro source files automatically have this
extern const char* macroSourceHeading;
extern const char* macroSourceFooter;
extern const char* generatedSourceHeading;
extern const char* generatedSourceFooter;
extern const char* generatedHeaderHeading;
extern const char* generatedHeaderFooter;

+ 2
- 4
src/RunProcess.cpp View File

@ -100,7 +100,7 @@ int runProcess(const RunProcessArguments& arguments, int* statusOut)
}
// Only read
close(pipeFileDescriptors[PipeWrite]);
printf("Created child process %d\n", pid);
// printf("Created child process %d\n", pid);
s_subprocesses.push_back({statusOut, pid});
}
@ -112,10 +112,8 @@ int runProcess(const RunProcessArguments& arguments, int* statusOut)
void waitForAllProcessesClosed(SubprocessOnOutputFunc onOutput)
{
if (s_subprocesses.empty())
{
printf("No subprocesses to wait for\n");
return;
}
// TODO: Don't merge all subprocesses to stdin, keep them separate to prevent multi-line errors
// from being split
char processOutputBuffer[1024];


+ 76
- 43
src/Writer.cpp View File

@ -238,6 +238,38 @@ bool moveFile(const char* srcFilename, const char* destFilename)
return true;
}
void writeOutputFollowSplices_Recursive(const NameStyleSettings& nameSettings,
const WriterFormatSettings& formatSettings,
StringOutputState& outputState,
const std::vector<StringOutput>& outputOperations)
{
for (const StringOutput& operation : outputOperations)
{
// Debug print mapping
if (!operation.output.empty() && false)
{
printf("%s \t%d\tline %d\n", operation.output.c_str(), outputState.numCharsOutput + 1,
outputState.currentLine + 1);
}
if (operation.modifiers == StringOutMod_Splice)
{
if (operation.spliceOutput)
{
writeOutputFollowSplices_Recursive(nameSettings, formatSettings, outputState,
operation.spliceOutput->source);
if (!operation.spliceOutput->header.empty())
ErrorAtToken(*operation.startToken,
"splice output contained header output, which is not supported");
}
else
ErrorAtToken(*operation.startToken, "expected splice output");
}
else
writeStringOutput(nameSettings, formatSettings, operation, outputState);
}
}
// TODO This sucks
// TODO Lots of params
bool writeIfContentsNewer(const NameStyleSettings& nameSettings,
@ -264,17 +296,7 @@ bool writeIfContentsNewer(const NameStyleSettings& nameSettings,
}
}
for (const StringOutput& operation : outputOperations)
{
// Debug print mapping
if (!operation.output.empty() && false)
{
printf("%s \t%d\tline %d\n", operation.output.c_str(), outputState.numCharsOutput + 1,
outputState.currentLine + 1);
}
writeStringOutput(nameSettings, formatSettings, operation, outputState);
}
writeOutputFollowSplices_Recursive(nameSettings, formatSettings, outputState, outputOperations);
if (footer)
{
@ -357,50 +379,61 @@ bool writeGeneratorOutput(const GeneratorOutput& generatedOutput,
const WriterFormatSettings& formatSettings,
const WriterOutputSettings& outputSettings)
{
printf("\tTo source file:\n");
if (!generatedOutput.source.empty())
{
char sourceOutputName[MAX_PATH_LENGTH] = {0};
PrintfBuffer(sourceOutputName, "%s.cpp", outputSettings.sourceCakelispFilename);
if (!writeIfContentsNewer(nameSettings, formatSettings, outputSettings,
outputSettings.sourceHeading, outputSettings.sourceFooter,
generatedOutput.source, sourceOutputName))
return false;
printf("\tTo source file:\n");
{
char sourceOutputName[MAX_PATH_LENGTH] = {0};
PrintfBuffer(sourceOutputName, "%s.cpp", outputSettings.sourceCakelispFilename);
if (!writeIfContentsNewer(nameSettings, formatSettings, outputSettings,
outputSettings.sourceHeading, outputSettings.sourceFooter,
generatedOutput.source, sourceOutputName))
return false;
}
}
printf("\n\tTo header file:\n");
if (!generatedOutput.header.empty())