Skip to content

Commit d4cc933

Browse files
committed
Added generic comptime functions
They aren't usable yet. I also switched COPYING and LICENSE so COPYING has copyright while license has the GPL full text.
1 parent f22151b commit d4cc933

7 files changed

+812
-736
lines changed

COPYING

+12-670
Large diffs are not rendered by default.

LICENSE

+670-12
Large diffs are not rendered by default.

runtime/TestMain.cake

+3
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@
1919
(printf "error: failed to hot-reload\n")
2020
(return 1)))
2121
(return 0))
22+
23+
(defun-comptime build-text-adventure ()
24+
(printf "Hello, comptime!\n"))

src/Evaluator.cpp

+73-24
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,29 @@ MacroFunc findMacro(EvaluatorEnvironment& environment, const char* functionName)
3939
return nullptr;
4040
}
4141

42+
void* findCompileTimeFunction(EvaluatorEnvironment& environment, const char* functionName)
43+
{
44+
CompileTimeFunctionTableIterator findIt =
45+
environment.compileTimeFunctions.find(std::string(functionName));
46+
if (findIt != environment.compileTimeFunctions.end())
47+
return findIt->second;
48+
return nullptr;
49+
}
50+
4251
bool isCompileTimeCodeLoaded(EvaluatorEnvironment& environment, const ObjectDefinition& definition)
4352
{
44-
if (definition.type == ObjectType_CompileTimeMacro)
45-
return findMacro(environment, definition.name->contents.c_str()) != nullptr;
46-
else
47-
return findGenerator(environment, definition.name->contents.c_str()) != nullptr;
53+
switch (definition.type)
54+
{
55+
case ObjectType_CompileTimeMacro:
56+
return findMacro(environment, definition.name->contents.c_str()) != nullptr;
57+
case ObjectType_CompileTimeGenerator:
58+
return findGenerator(environment, definition.name->contents.c_str()) != nullptr;
59+
case ObjectType_CompileTimeFunction:
60+
return findCompileTimeFunction(environment, definition.name->contents.c_str()) !=
61+
nullptr;
62+
default:
63+
return false;
64+
}
4865
}
4966

5067
bool addObjectDefinition(EvaluatorEnvironment& environment, ObjectDefinition& definition)
@@ -159,7 +176,8 @@ int getNextFreeBuildId(EvaluatorEnvironment& environment)
159176

160177
bool isCompileTimeObject(ObjectType type)
161178
{
162-
return type == ObjectType_CompileTimeMacro || type == ObjectType_CompileTimeGenerator;
179+
return type == ObjectType_CompileTimeMacro || type == ObjectType_CompileTimeGenerator ||
180+
type == ObjectType_CompileTimeFunction;
163181
}
164182

165183
//
@@ -527,6 +545,13 @@ int BuildExecuteCompileTimeFunctions(EvaluatorEnvironment& environment,
527545
if (log.buildProcess)
528546
printf("Build %s\n", definition->name->contents.c_str());
529547

548+
if (!definition->output)
549+
{
550+
ErrorAtToken(*buildObject.definition->name,
551+
"missing compile-time output. Internal code error?");
552+
continue;
553+
}
554+
530555
char convertedNameBuffer[MAX_NAME_LENGTH] = {0};
531556
lispNameStyleToCNameStyle(NameStyleMode_Underscores, definition->name->contents.c_str(),
532557
convertedNameBuffer, sizeof(convertedNameBuffer),
@@ -545,16 +570,28 @@ int BuildExecuteCompileTimeFunctions(EvaluatorEnvironment& environment,
545570
NameStyleSettings nameSettings;
546571
WriterFormatSettings formatSettings;
547572
WriterOutputSettings outputSettings = {};
548-
if (definition->type == ObjectType_CompileTimeGenerator)
549-
{
550-
outputSettings.sourceHeading = generatorSourceHeading;
551-
outputSettings.sourceFooter = generatorSourceFooter;
552-
}
553-
else
573+
574+
switch (definition->type)
554575
{
555-
outputSettings.sourceHeading = macroSourceHeading;
556-
outputSettings.sourceFooter = macroSourceFooter;
576+
case ObjectType_CompileTimeMacro:
577+
outputSettings.sourceHeading = macroSourceHeading;
578+
outputSettings.sourceFooter = macroSourceFooter;
579+
break;
580+
case ObjectType_CompileTimeGenerator:
581+
outputSettings.sourceHeading = generatorSourceHeading;
582+
outputSettings.sourceFooter = generatorSourceFooter;
583+
break;
584+
case ObjectType_CompileTimeFunction:
585+
// TODO Right now this is the same as generators, but I don't know what I really
586+
// want yet. Should each compile-time function have the ability to specify this,
587+
// e.g. &with-build-tools or something? Figure it out automatically?
588+
outputSettings.sourceHeading = generatorSourceHeading;
589+
outputSettings.sourceFooter = generatorSourceFooter;
590+
break;
591+
default:
592+
break;
557593
}
594+
558595
outputSettings.sourceCakelispFilename = fileOutputName;
559596
{
560597
char writerSourceOutputName[MAX_PATH_LENGTH] = {0};
@@ -733,15 +770,25 @@ int BuildExecuteCompileTimeFunctions(EvaluatorEnvironment& environment,
733770
}
734771

735772
// Add to environment
736-
if (buildObject.definition->type == ObjectType_CompileTimeMacro)
737-
{
738-
environment.macros[buildObject.definition->name->contents] =
739-
(MacroFunc)compileTimeFunction;
740-
}
741-
else if (buildObject.definition->type == ObjectType_CompileTimeGenerator)
773+
switch (buildObject.definition->type)
742774
{
743-
environment.generators[buildObject.definition->name->contents] =
744-
(GeneratorFunc)compileTimeFunction;
775+
case ObjectType_CompileTimeMacro:
776+
environment.macros[buildObject.definition->name->contents] =
777+
(MacroFunc)compileTimeFunction;
778+
break;
779+
case ObjectType_CompileTimeGenerator:
780+
environment.generators[buildObject.definition->name->contents] =
781+
(GeneratorFunc)compileTimeFunction;
782+
break;
783+
case ObjectType_CompileTimeFunction:
784+
environment.compileTimeFunctions[buildObject.definition->name->contents] =
785+
(void*)compileTimeFunction;
786+
break;
787+
default:
788+
ErrorAtToken(
789+
*buildObject.definition->name,
790+
"Tried to build definition which is not compile-time object. Code error?");
791+
break;
745792
}
746793

747794
buildObject.stage = BuildStage_ResolvingReferences;
@@ -985,9 +1032,9 @@ bool EvaluateResolveReferences(EvaluatorEnvironment& environment)
9851032
}
9861033
}
9871034

988-
// Keep propagating and evaluating until no more references are resolved.
989-
// This must be done in passes in case evaluation created more definitions. There's probably a
990-
// smarter way, but I'll do it in this brute-force style first
1035+
// Keep propagating and evaluating until no more references are resolved. This must be done in
1036+
// passes in case evaluation created more definitions. There's probably a smarter way, but I'll
1037+
// do it in this brute-force style first
9911038
int numReferencesResolved = 0;
9921039
int numBuildResolveErrors = 0;
9931040
do
@@ -1127,6 +1174,8 @@ const char* objectTypeToString(ObjectType type)
11271174
return "Macro";
11281175
case ObjectType_CompileTimeGenerator:
11291176
return "Generator";
1177+
case ObjectType_CompileTimeFunction:
1178+
return "Compile-time Function";
11301179
default:
11311180
return "Unknown";
11321181
}

src/Evaluator.hpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -170,15 +170,19 @@ typedef std::pair<const std::string, ObjectDefinition> ObjectDefinitionPair;
170170
typedef std::unordered_map<std::string, ObjectReferencePool> ObjectReferencePoolMap;
171171
typedef std::pair<const std::string, ObjectReferencePool> ObjectReferencePoolPair;
172172

173+
typedef std::unordered_map<std::string, void*> CompileTimeFunctionTable;
174+
typedef CompileTimeFunctionTable::iterator CompileTimeFunctionTableIterator;
175+
173176
// Unlike context, which can't be changed, environment can be changed.
174-
// Use care when modifying the environment. Only add things once you know things have succeeded.
175177
// Keep in mind that calling functions which can change the environment may invalidate your pointers
176178
// if things resize.
177179
struct EvaluatorEnvironment
178180
{
179181
// Compile-time-executable functions
180182
MacroTable macros;
181183
GeneratorTable generators;
184+
// Dumping ground for functions without fixed signatures
185+
CompileTimeFunctionTable compileTimeFunctions;
182186

183187
// We need to keep the tokens macros create around so they can be referenced by StringOperations
184188
// Token vectors must not be changed after they are created or pointers to Tokens will become

src/EvaluatorEnums.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,10 @@ enum EvaluatorScope
5959
enum ObjectType
6060
{
6161
ObjectType_Function,
62+
6263
ObjectType_CompileTimeMacro,
6364
ObjectType_CompileTimeGenerator,
65+
ObjectType_CompileTimeFunction,
6466
};
6567

6668
enum ObjectReferenceGuessState

src/Generators.cpp

+47-29
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,8 @@ bool AddDependencyGenerator(EvaluatorEnvironment& environment, const EvaluatorCo
429429
}
430430

431431
bool DefunGenerator(EvaluatorEnvironment& environment, const EvaluatorContext& context,
432-
const std::vector<Token>& tokens, int startTokenIndex, GeneratorOutput& output)
432+
const std::vector<Token>& tokens, int startTokenIndex,
433+
GeneratorOutput& runtimeOutput)
433434
{
434435
if (!ExpectEvaluatorScope("defun", tokens[startTokenIndex], context, EvaluatorScope_Module))
435436
return false;
@@ -452,6 +453,26 @@ bool DefunGenerator(EvaluatorEnvironment& environment, const EvaluatorContext& c
452453
return false;
453454

454455
bool isModuleLocal = tokens[startTokenIndex + 1].contents.compare("defun-local") == 0;
456+
bool isCompileTime = tokens[startTokenIndex + 1].contents.compare("defun-comptime") == 0;
457+
GeneratorOutput* functionOutput = isCompileTime ? new GeneratorOutput : &runtimeOutput;
458+
459+
// Register definition before evaluating body, otherwise references in body will be orphaned
460+
{
461+
ObjectDefinition newFunctionDef = {};
462+
newFunctionDef.name = &nameToken;
463+
newFunctionDef.type = isCompileTime ? ObjectType_CompileTimeFunction : ObjectType_Function;
464+
// Compile-time objects only get built with compile-time references
465+
newFunctionDef.isRequired = isCompileTime ? false : context.isRequired;
466+
if (isCompileTime)
467+
newFunctionDef.output = functionOutput;
468+
if (!addObjectDefinition(environment, newFunctionDef))
469+
{
470+
if (isCompileTime)
471+
delete functionOutput;
472+
return false;
473+
}
474+
// Past this point, compile-time output will be handled by environment destruction
475+
}
455476

456477
int returnTypeStart = -1;
457478
std::vector<FunctionArgumentTokens> arguments;
@@ -460,30 +481,32 @@ bool DefunGenerator(EvaluatorEnvironment& environment, const EvaluatorContext& c
460481

461482
if (environment.enableHotReloading)
462483
{
463-
addStringOutput(isModuleLocal ? output.source : output.header, "extern \"C\"",
464-
StringOutMod_SpaceAfter, &tokens[startTokenIndex]);
484+
addStringOutput(isModuleLocal ? functionOutput->source : functionOutput->header,
485+
"extern \"C\"", StringOutMod_SpaceAfter, &tokens[startTokenIndex]);
465486
}
466487

467488
// TODO: Hot-reloading functions shouldn't be declared static, right?
468489
if (isModuleLocal)
469-
addStringOutput(output.source, "static", StringOutMod_SpaceAfter, &tokens[startTokenIndex]);
490+
addStringOutput(functionOutput->source, "static", StringOutMod_SpaceAfter,
491+
&tokens[startTokenIndex]);
470492

471493
int endArgsIndex = FindCloseParenTokenIndex(tokens, argsIndex);
472-
if (!outputFunctionReturnType(tokens, output, returnTypeStart, startTokenIndex, endArgsIndex,
494+
if (!outputFunctionReturnType(tokens, *functionOutput, returnTypeStart, startTokenIndex,
495+
endArgsIndex,
473496
/*outputSource=*/true, /*outputHeader=*/!isModuleLocal))
474497
return false;
475498

476-
addStringOutput(output.source, nameToken.contents, StringOutMod_ConvertFunctionName,
499+
addStringOutput(functionOutput->source, nameToken.contents, StringOutMod_ConvertFunctionName,
477500
&nameToken);
478501
if (!isModuleLocal)
479-
addStringOutput(output.header, nameToken.contents, StringOutMod_ConvertFunctionName,
480-
&nameToken);
502+
addStringOutput(functionOutput->header, nameToken.contents,
503+
StringOutMod_ConvertFunctionName, &nameToken);
481504

482-
addLangTokenOutput(output.source, StringOutMod_OpenParen, &argsStart);
505+
addLangTokenOutput(functionOutput->source, StringOutMod_OpenParen, &argsStart);
483506
if (!isModuleLocal)
484-
addLangTokenOutput(output.header, StringOutMod_OpenParen, &argsStart);
507+
addLangTokenOutput(functionOutput->header, StringOutMod_OpenParen, &argsStart);
485508

486-
if (!outputFunctionArguments(tokens, output, arguments, /*outputSource=*/true,
509+
if (!outputFunctionArguments(tokens, *functionOutput, arguments, /*outputSource=*/true,
487510
/*outputHeader=*/!isModuleLocal))
488511
return false;
489512

@@ -498,41 +521,35 @@ bool DefunGenerator(EvaluatorEnvironment& environment, const EvaluatorContext& c
498521
argumentsMetadata.push_back({tokens[arg.nameIndex].contents, startTypeToken, endTypeToken});
499522
}
500523

501-
addLangTokenOutput(output.source, StringOutMod_CloseParen, &tokens[endArgsIndex]);
524+
addLangTokenOutput(functionOutput->source, StringOutMod_CloseParen, &tokens[endArgsIndex]);
502525
if (!isModuleLocal)
503526
{
504-
addLangTokenOutput(output.header, StringOutMod_CloseParen, &tokens[endArgsIndex]);
527+
addLangTokenOutput(functionOutput->header, StringOutMod_CloseParen, &tokens[endArgsIndex]);
505528
// Forward declarations end with ;
506-
addLangTokenOutput(output.header, StringOutMod_EndStatement, &tokens[endArgsIndex]);
507-
}
508-
509-
// Register definition before evaluating body, otherwise references in body will be orphaned
510-
{
511-
ObjectDefinition newFunctionDef = {};
512-
newFunctionDef.name = &nameToken;
513-
newFunctionDef.type = ObjectType_Function;
514-
newFunctionDef.isRequired = context.isRequired;
515-
if (!addObjectDefinition(environment, newFunctionDef))
516-
return false;
529+
addLangTokenOutput(functionOutput->header, StringOutMod_EndStatement,
530+
&tokens[endArgsIndex]);
517531
}
518532

519533
int startBodyIndex = endArgsIndex + 1;
520-
addLangTokenOutput(output.source, StringOutMod_OpenBlock, &tokens[startBodyIndex]);
534+
addLangTokenOutput(functionOutput->source, StringOutMod_OpenBlock, &tokens[startBodyIndex]);
521535

522536
// Evaluate our body!
523537
EvaluatorContext bodyContext = context;
524538
bodyContext.scope = EvaluatorScope_Body;
525539
bodyContext.definitionName = &nameToken;
526540
// The statements will need to handle their ;
527541
int numErrors = EvaluateGenerateAll_Recursive(environment, bodyContext, tokens, startBodyIndex,
528-
/*delimiterTemplate=*/nullptr, output);
542+
/*delimiterTemplate=*/nullptr, *functionOutput);
529543
if (numErrors)
544+
{
545+
// Don't delete compile time function output. Evaluate may have caused references to it
530546
return false;
547+
}
531548

532-
addLangTokenOutput(output.source, StringOutMod_CloseBlock, &tokens[endTokenIndex]);
549+
addLangTokenOutput(functionOutput->source, StringOutMod_CloseBlock, &tokens[endTokenIndex]);
533550

534-
output.functions.push_back({nameToken.contents, &tokens[startTokenIndex],
535-
&tokens[endTokenIndex], std::move(argumentsMetadata)});
551+
functionOutput->functions.push_back({nameToken.contents, &tokens[startTokenIndex],
552+
&tokens[endTokenIndex], std::move(argumentsMetadata)});
536553

537554
return true;
538555
}
@@ -1969,6 +1986,7 @@ void importFundamentalGenerators(EvaluatorEnvironment& environment)
19691986

19701987
environment.generators["defun"] = DefunGenerator;
19711988
environment.generators["defun-local"] = DefunGenerator;
1989+
environment.generators["defun-comptime"] = DefunGenerator;
19721990

19731991
environment.generators["def-function-signature"] = DefFunctionSignatureGenerator;
19741992
environment.generators["def-function-signature-local"] = DefFunctionSignatureGenerator;

0 commit comments

Comments
 (0)