Browse Source

Added variadic functions

* &variable-arguments can be specified in function signatures to
generate "..." functions
* Add some missing newlines
master
Macoy Madson 2 months ago
parent
commit
8e270bd5a6
9 changed files with 100 additions and 26 deletions
  1. +17
    -0
      doc/Cakelisp.org
  2. +9
    -6
      runtime/CHelpers.cake
  3. +38
    -2
      src/GeneratorHelpers.cpp
  4. +3
    -2
      src/GeneratorHelpers.hpp
  5. +6
    -3
      src/Generators.cpp
  6. +2
    -2
      src/ModuleManager.cpp
  7. +12
    -9
      src/ModuleManager.hpp
  8. +12
    -2
      test/Hello.cake
  9. +1
    -0
      test/RunTests.cake

+ 17
- 0
doc/Cakelisp.org View File

@ -149,6 +149,23 @@ The example function's signature will also be added to the header file so that i
Unlike Lisps, function returns must be explicitly specified via ~(return)~, unless the function has no ~&return~ (implicit ~void~ return).
Notice that argument names come first. I chose to swap the order of name and type because it places more emphasis on the name. A well-written program will convey more useful information in the name than in the type, so it makes sense to me to have it come first for the reader. This also applies to ~defstruct~ members, ~type-cast~, ~var~ declarations, etc.
** Variable arguments
The keyword ~&variable-arguments can be used to create a function with variadic arguments:
#+BEGIN_SRC lisp
(c-import "<stdio.h>" "<stdarg.h>")
(defun varargs (num-args int &variable-arguments)
(var list va_list)
(va_start list num-args)
(each-in-range num-args i
(printf "%d\n" (va_arg list int)))
(va_end list))
(defun main (&return int)
(varargs 3 1 2 3)
(return 0))
#+END_SRC
* Variables
The following invocations will declare variables:
- ~var~: Module- or body-scope local. This is the most-used variable type


+ 9
- 6
runtime/CHelpers.cake View File

@ -59,28 +59,31 @@
(defgenerator declare-extern-function (name-token (ref symbol) signature-index (index array))
(quick-token-at signature-token signature-index)
(var return-type-start int -1)
(var is-variadic-index int -1)
(var arguments (<> std::vector FunctionArgumentTokens))
(unless (parseFunctionSignature tokens signature-index arguments return-type-start)
(return false))
(unless (parseFunctionSignature tokens signature-index arguments
return-type-start is-variadic-index)
(return false))
(var end-signature-index int (FindCloseParenTokenIndex tokens signature-index))
(unless (outputFunctionReturnType environment context tokens output return-type-start
startTokenIndex
end-signature-index
true ;; Output to source
end-signature-index
true ;; Output to source
false) ;; Output to header
(return false))
(addStringOutput (path output . source) (field name-token contents)
StringOutMod_ConvertFunctionName
(addr name-token))
(addr name-token))
(addLangTokenOutput (field output source) StringOutMod_OpenParen (addr signature-token))
(unless (outputFunctionArguments environment context tokens output arguments
is-variadic-index
true ;; Output to source
false) ;; Output to header
(return false))
(return false))
(addLangTokenOutput (field output source) StringOutMod_CloseParen (addr signature-token))


+ 38
- 2
src/GeneratorHelpers.cpp View File

@ -465,7 +465,8 @@ void addSpliceOutput(GeneratorOutput& output, GeneratorOutput* spliceOutput,
//
bool parseFunctionSignature(const std::vector<Token>& tokens, int argsIndex,
std::vector<FunctionArgumentTokens>& arguments, int& returnTypeStart)
std::vector<FunctionArgumentTokens>& arguments, int& returnTypeStart,
int& isVariadicIndex)
{
enum DefunState
{
@ -477,6 +478,8 @@ bool parseFunctionSignature(const std::vector<Token>& tokens, int argsIndex,
DefunState state = Name;
FunctionArgumentTokens currentArgument = {};
isVariadicIndex = -1;
int endArgsIndex = FindCloseParenTokenIndex(tokens, argsIndex);
for (int i = argsIndex + 1; i < endArgsIndex; ++i)
{
@ -498,6 +501,19 @@ bool parseFunctionSignature(const std::vector<Token>& tokens, int argsIndex,
// Wait until next token to get type
continue;
}
else if (currentToken.type == TokenType_Symbol &&
currentToken.contents.compare("&variable-arguments") == 0)
{
isVariadicIndex = i;
continue;
}
if (isVariadicIndex != -1)
{
ErrorAtToken(currentToken,
"no additional arguments may be declared after &variable-arguments");
return false;
}
if (!ExpectTokenType("defun", currentToken, TokenType_Symbol))
return false;
@ -609,7 +625,7 @@ bool outputFunctionReturnType(EvaluatorEnvironment& environment, const Evaluator
bool outputFunctionArguments(EvaluatorEnvironment& environment, const EvaluatorContext& context,
const std::vector<Token>& tokens, GeneratorOutput& output,
const std::vector<FunctionArgumentTokens>& arguments,
bool outputSource, bool outputHeader)
int isVariadicIndex, bool outputSource, bool outputHeader)
{
int numFunctionArguments = arguments.size();
for (int i = 0; i < numFunctionArguments; ++i)
@ -656,6 +672,26 @@ bool outputFunctionArguments(EvaluatorEnvironment& environment, const EvaluatorC
}
}
if (isVariadicIndex >= 0)
{
if (numFunctionArguments)
{
if (outputSource)
addLangTokenOutput(output.source, StringOutMod_ListSeparator,
&tokens[numFunctionArguments - 1]);
if (outputHeader)
addLangTokenOutput(output.header, StringOutMod_ListSeparator,
&tokens[numFunctionArguments - 1]);
}
if (outputSource)
addStringOutput(output.source, "...",
StringOutMod_None, &tokens[isVariadicIndex]);
if (outputHeader)
addStringOutput(output.header, "...",
StringOutMod_None, &tokens[isVariadicIndex]);
}
return true;
}


+ 3
- 2
src/GeneratorHelpers.hpp View File

@ -108,7 +108,7 @@ struct FunctionArgumentTokens
};
CAKELISP_API bool parseFunctionSignature(const std::vector<Token>& tokens, int argsIndex,
std::vector<FunctionArgumentTokens>& arguments,
int& returnTypeStart);
int& returnTypeStart, int& isVariadicIndex);
// startInvocationIndex is used for blaming on implicit return type
CAKELISP_API bool outputFunctionReturnType(EvaluatorEnvironment& environment,
const EvaluatorContext& context,
@ -120,7 +120,8 @@ CAKELISP_API bool outputFunctionArguments(EvaluatorEnvironment& environment,
const EvaluatorContext& context,
const std::vector<Token>& tokens, GeneratorOutput& output,
const std::vector<FunctionArgumentTokens>& arguments,
bool outputSource, bool outputHeader);
int isVariadicIndex, bool outputSource,
bool outputHeader);
bool tokenizedCTypeToString_Recursive(EvaluatorEnvironment& environment,
const EvaluatorContext& context,


+ 6
- 3
src/Generators.cpp View File

@ -932,8 +932,9 @@ bool DefunGenerator(EvaluatorEnvironment& environment, const EvaluatorContext& c
}
int returnTypeStart = -1;
int isVariadicIndex = -1;
std::vector<FunctionArgumentTokens> arguments;
if (!parseFunctionSignature(tokens, argsIndex, arguments, returnTypeStart))
if (!parseFunctionSignature(tokens, argsIndex, arguments, returnTypeStart, isVariadicIndex))
return false;
if (isCompileTime && environment.isMsvcCompiler)
@ -974,6 +975,7 @@ bool DefunGenerator(EvaluatorEnvironment& environment, const EvaluatorContext& c
addLangTokenOutput(functionOutput->header, StringOutMod_OpenParen, &argsStart);
if (!outputFunctionArguments(environment, context, tokens, *functionOutput, arguments,
isVariadicIndex,
/*outputSource=*/true,
/*outputHeader=*/!isModuleLocal))
return false;
@ -1064,8 +1066,9 @@ bool DefFunctionSignatureGenerator(EvaluatorEnvironment& environment,
std::vector<StringOutput>& outputDest = isModuleLocal ? output.source : output.header;
int returnTypeStart = -1;
int isVariadicIndex = -1;
std::vector<FunctionArgumentTokens> arguments;
if (!parseFunctionSignature(tokens, argsIndex, arguments, returnTypeStart))
if (!parseFunctionSignature(tokens, argsIndex, arguments, returnTypeStart, isVariadicIndex))
return false;
addStringOutput(outputDest, "typedef", StringOutMod_SpaceAfter, &tokens[startTokenIndex]);
@ -1084,7 +1087,7 @@ bool DefFunctionSignatureGenerator(EvaluatorEnvironment& environment,
addLangTokenOutput(outputDest, StringOutMod_OpenParen, &argsStart);
if (!outputFunctionArguments(environment, context, tokens, output, arguments,
if (!outputFunctionArguments(environment, context, tokens, output, arguments, isVariadicIndex,
/*outputSource=*/isModuleLocal,
/*outputHeader=*/!isModuleLocal))
return false;


+ 2
- 2
src/ModuleManager.cpp View File

@ -418,7 +418,7 @@ bool moduleManagerAddEvaluateFile(ModuleManager& manager, const char* filename,
const char* normalizedProspectiveModuleFilename = makeAbsolutePath_Allocated(".", filename);
if (!normalizedProspectiveModuleFilename)
{
Logf("error: failed to normalize path %s", filename);
Logf("error: failed to normalize path %s\n", filename);
free((void*)normalizedFilename);
return false;
}
@ -427,7 +427,7 @@ bool moduleManagerAddEvaluateFile(ModuleManager& manager, const char* filename,
if (!normalizedModuleFilename)
{
Logf("error: failed to normalize path %s", module->filename);
Logf("error: failed to normalize path %s\n", module->filename);
free((void*)normalizedProspectiveModuleFilename);
free((void*)normalizedFilename);
return false;


+ 12
- 9
src/ModuleManager.hpp View File

@ -5,6 +5,7 @@
#include "Build.hpp"
#include "Evaluator.hpp"
#include "Exporting.hpp"
#include "ModuleManagerEnums.hpp"
#include "RunProcess.hpp"
#include "Tokenizer.hpp"
@ -79,20 +80,22 @@ struct ModuleManager
ArtifactCrcTable newCommandCrcs;
};
void moduleManagerInitialize(ModuleManager& manager);
CAKELISP_API void moduleManagerInitialize(ModuleManager& manager);
// Do not close opened dynamic libraries. Should by called by sub-instances of cakelisp instead of
// moduleManagerDestroy(), otherwise they may segfault
void moduleManagerDestroyKeepDynLibs(ModuleManager& manager);
CAKELISP_API void moduleManagerDestroyKeepDynLibs(ModuleManager& manager);
// Note that this will close all dynamic libraries
void moduleManagerDestroy(ModuleManager& manager);
CAKELISP_API void moduleManagerDestroy(ModuleManager& manager);
bool moduleLoadTokenizeValidate(const char* filename, const std::vector<Token>** tokensOut);
bool moduleManagerAddEvaluateFile(ModuleManager& manager, const char* filename, Module** moduleOut);
bool moduleManagerEvaluateResolveReferences(ModuleManager& manager);
bool moduleManagerWriteGeneratedOutput(ModuleManager& manager);
bool moduleManagerBuildAndLink(ModuleManager& manager, std::vector<std::string>& builtOutputs);
bool moduleManagerExecuteBuiltOutputs(ModuleManager& manager,
const std::vector<std::string>& builtOutputs);
CAKELISP_API bool moduleManagerAddEvaluateFile(ModuleManager& manager, const char* filename,
Module** moduleOut);
CAKELISP_API bool moduleManagerEvaluateResolveReferences(ModuleManager& manager);
CAKELISP_API bool moduleManagerWriteGeneratedOutput(ModuleManager& manager);
CAKELISP_API bool moduleManagerBuildAndLink(ModuleManager& manager,
std::vector<std::string>& builtOutputs);
CAKELISP_API bool moduleManagerExecuteBuiltOutputs(ModuleManager& manager,
const std::vector<std::string>& builtOutputs);
// Initializes a normal environment and outputs all generators available to it
void listBuiltInGenerators();

+ 12
- 2
test/Hello.cake View File

@ -1,5 +1,15 @@
(c-import "<stdio.h>")
(add-cakelisp-search-directory "runtime")
(import &comptime-only "CHelpers.cake")
(c-import "<stdio.h>" "<stdarg.h>")
(defun main(&return int)
(defun varargs (num-args int &variable-arguments)
(var list va_list)
(va_start list num-args)
(each-in-range num-args i
(printf "%d\n" (va_arg list int)))
(va_end list))
(defun main (&return int)
(printf "Hello, world! From Cakelisp!\n")
(varargs 3 1 2 3)
(return 0))

+ 1
- 0
test/RunTests.cake View File

@ -13,6 +13,7 @@
test-file (* (const char)))
(var tests ([] (const cakelisp-test))
(array
(array "Hello" "test/Hello.cake")
(array "Macros" "test/SimpleMacros.cake")
(array "Code modification" "test/CodeModification.cake")
;; (array "Build options" "test/BuildOptions.cake")


Loading…
Cancel
Save