Browse Source

Breaking change to type system, deprecated more

The following types are now changed:
- * is now addr
- & is now ref
- [] is now array
- <> is now template
- && is deprecated

I did this because I believe symbol-heavy languages end up harder to
read and understand. Now, we can do things that make a lot of sense:
(var my-int-ptr (addr int) (addr an-int))
It also means less utilization of the shift key, which is nice
ergonomics-wise. The tradeoff of course is more typing, but I consider
pressing a single key quite a bit easier than shift and a key.

The following generators are now deprecated:
++
--
%
block

The first three are in the same spirit as the type name
change. Symbols are harder to search and type than words.

These are now renamed:
bit->> becomes bit-shift->>
bit-<< becomes bit-shift-<<

This is an obnoxious change, but I figure because of low utilization
by people other than myself, now is as good a time as any.
c-port
Macoy Madson 5 months ago
parent
commit
a1e022e6d5
  1. 63
      doc/Cakelisp.org
  2. 2
      doc/Roadmap.org
  3. 38
      doc/Tutorial_Basics.org
  4. 20
      runtime/BuildTools.cake
  5. 72
      runtime/CHelpers.cake
  6. 14
      runtime/Cakelisp.cake
  7. 40
      runtime/ComptimeHelpers.cake
  8. 8
      runtime/CppHelpers.cake
  9. 40
      runtime/DynamicLoader.cake
  10. 48
      runtime/FileUtilities.cake
  11. 2
      runtime/HotLoader.cake
  12. 46
      runtime/HotReloading.cake
  13. 94
      runtime/HotReloadingCodeModifier.cake
  14. 6
      runtime/RunProcess.cake
  15. 16
      runtime/TextAdventure.cake
  16. 6
      src/Evaluator.cpp
  17. 154
      src/GeneratorHelpers.cpp
  18. 39
      src/Generators.cpp
  19. 40
      src/Metadata.cpp
  20. 2
      src/ModuleManager.cpp
  21. 4
      test/BuildHelpers.cake
  22. 18
      test/CodeModification.cake
  23. 14
      test/CommandLineOptions.cake
  24. 8
      test/CompileTimeHooks.cake
  25. 4
      test/Defer.cake
  26. 10
      test/Defines.cake
  27. 2
      test/Metadata.cake
  28. 20
      test/RunTests.cake
  29. 2
      test/SimpleMacros.cake
  30. 18
      test/TestPaths.cake
  31. 18
      test/Tutorial_Basics.cake
  32. 2
      tools/cakelisp.el

63
doc/Cakelisp.org

@ -51,7 +51,7 @@ This also means that adding Cakelisp to an existing C/C++ project should be virt
Here are some example imports:
#+BEGIN_SRC lisp
(c-import "<vector>") ;; now just e.g. (var my-vec (<> std::vector int) (array 1 2 3))
(c-import "<vector>") ;; now just e.g. (var my-vec (template (in std vector) int) (array 1 2 3))
(c-import "<cstdio.h>") ;; (fprintf stderr "Hello %s!\n" "Cakelisp")
(c-import "MyHeader.hpp") ;; (call-on updateState myGlobalVar 0.016)
@ -73,21 +73,20 @@ By default, ~&with-defs~ is specified.
You shouldn't expect Cakelisp features to work with external C/C++ code. Features like hot-reloading or introspection aren't available to external code because Cakelisp does not parse any C/C++ headers. This doesn't mean you cannot call C/C++ code from a hot-reloaded Cakelisp function, it just means you cannot magically hot-reload the C/C++ code you're calling.
* Types
Types are identical to types in C, but specified in an S-expression notation. Here are some example C++ types and their corresponding Cakelisp:
| C/C++ | Cakelisp |
|------------------------------+-------------------------------------|
| ~int~ | ~int~ |
| ~int*~ | ~(* int)~ |
| ~const int*~ | ~(* (const int))~ |
| ~const int* const~ | ~(const (* (const int)))~ |
| ~int x[]~ | ~([] int)~ |
| ~int x[5]~ | ~([] 5 int)~ |
| ~int x[4][4]~ | ~([] 4 ([] 4 int))~ |
| ~int x[][4]~ | ~([] ([] 4 int))~ |
| ~std::vector<int>~ | ~(<> std::vector int)~ |
| ~std::map<std::string, int>~ | ~(<> std::map (in std string) int)~ |
| ~int&~ | ~(& int)~ |
| ~int&&~ | ~(&& int)~ |
| ~int&&~ | ~(rval-ref-to int)~ |
| C/C++ | Cakelisp |
|------------------------------+-----------------------------------------------|
| ~int~ | ~int~ |
| ~int*~ | ~(addr int)~ |
| ~const int*~ | ~(addr (const int))~ |
| ~const int* const~ | ~(const (addr (const int)))~ |
| ~int x[]~ | ~(array int)~ |
| ~int x[5]~ | ~(array 5 int)~ |
| ~int x[4][4]~ | ~(array 4 (array 4 int))~ |
| ~int x[][4]~ | ~(array (array 4 int))~ |
| ~std::vector<int>~ | ~(template (in std vector) int)~ |
| ~std::map<std::string, int>~ | ~(template (in std map) (in std string) int)~ |
| ~int&~ | ~(ref int)~ |
| ~int&&~ | ~(rval-ref-to int)~ |
Note that C++ scope resolution operator can be used or ~in~ can be used. The latter is preferable.
@ -95,7 +94,7 @@ While this is more verbose than C types, they are much more easily parsed and co
To read C types properly, you must [[http://unixwiz.net/techtips/reading-cdecl.html][work backwards from the name]] and apply several heuristics. The parentheses do add more typing, but they're more clear, machine-parseable, and can be read naturally (e.g. read left to right "pointer to constant character" vs. C's "constant character pointer", which seems worse in my mind).
This form also handles arrays as part of the type: ~(var my-array ([] 5 int))~ rather than ~int myArray[5];~, another way it is more consistent, readable, and parsable.
This form also handles arrays as part of the type: ~(var my-array (array 5 int))~ rather than ~int myArray[5];~, another way it is more consistent, readable, and parsable.
You can use any C/C++ keywords like ~volatile~, ~unsigned~, ~struct~, etc. in the same way that ~const~ is demonstrated above.
* Functions
@ -153,20 +152,20 @@ Use ~set~ to modify variables:
Arrays have the same syntactic sugar as C, e.g.:
#+BEGIN_SRC C
(var my-numbers ([] int) (array 1 2 3))
(var my-numbers (array int) (array 1 2 3))
#+END_SRC
...is a better way than
#+BEGIN_SRC C
(var my-numbers ([] 3 int) (array 1 2 3))
(var my-numbers (array 3 int) (array 1 2 3))
#+END_SRC
...because the compiler will automatically determine the size.
* Type aliases
Aliases can be created for types. Internally, this uses ~typedef~. For example:
#+BEGIN_SRC lisp
;; This will save us a lot of typing!
(def-type-alias FunctionReferenceArray (<> std::vector (* (* void))))
(def-type-alias FunctionReferenceArray (template (in std vector) (addr (addr void))))
;; Build on it!
(def-type-alias FunctionReferenceMap (<> std::unordered_map std::string FunctionReferenceArray))
(def-type-alias FunctionReferenceMap (template (in std unordered_map) (in std string) FunctionReferenceArray))
;; Declare a variable using our typedef
(var registered-functions FunctionReferenceMap)
#+END_SRC
@ -181,7 +180,7 @@ The syntax for function pointers is shown in [[file:../runtime/HotLoader.cake][H
(var hot-reload-entry-point-func reload-entry-point-signature null)
;; An example of a function which takes any type of function pointer, hence the cast
(register-function-pointer (type-cast (addr hot-reload-entry-point-func) (* (* void)))
(register-function-pointer (type-cast (addr hot-reload-entry-point-func) (addr (addr void)))
"reloadableEntryPoint")
#+END_SRC
@ -193,7 +192,7 @@ The syntax for function pointers is shown in [[file:../runtime/HotLoader.cake][H
If you wanted to define a function pointer which could point to ~int main(int numArguments, char* arguments[])~, for example:
#+BEGIN_SRC lisp
(def-function-signature main-signature (num-arguments int
arguments ([] (* char))
arguments (array (addr char))
&return int))
(var main-pointer main-signature (addr main))
#+END_SRC
@ -213,22 +212,22 @@ Use the argument ~--list-built-ins~ to see an up-to-date list of all possible ex
- ~cond~
- ~when~:
- ~unless~:
- ~array~: Used for initializer lists, e.g. ~(var my-array ([] int) (array 1 2 3))~. Without arguments, equals the default initializer, e.g. ~(array)~ becomes ~{}~ in generated code
- ~array~: Used for initializer lists, e.g. ~(var my-array (array int) (array 1 2 3))~. Without arguments, equals the default initializer, e.g. ~(array)~ becomes ~{}~ in generated code
- ~set~: Sets a variable to a value, e.g. ~(set my-var 5)~ sets ~(var my-var int)~ to ~5~
- ~block~: Defines a scope, where variables declared within it are limited to that scope
- ~scope~: Alias of block, in case you want to be explicit. For example, creating a scope to reduce scope of variables vs. creating a block to have more than one statement in an ~(if)~ body
- ~?~: Ternary operator. For example, the expression ~(? true 1 2)~ will return 1, whereas ~(? false 1 2)~ returns 2. Handy for when you don't want to use a full ~if~ statement, for example
** Pointers, members
- ~new~: Calls C++ ~new~ with the given type, e.g. ~(new (* char))~ will allocate memory for a pointer to a character
- ~new~: Calls C++ ~new~ with the given type, e.g. ~(new (addr char))~ will allocate memory for a pointer to a character
- ~deref~: Return the value at the pointer's address
- ~addr~: Take the address of a variable/member
- ~field~: Access a struct/class member. For example, with struct ~(defstruct my-struct num int)~, and variable ~(var my-thing my-struct)~, access ~num~: ~(field my-thing num)~
- ~path~: Access fields from struct addresses. For example, ~(path my-struct-ptr > num)~. You can nest these and access non-pointer fields as well, e.g. ~(path my-struct . ptr-field > another-struct . field)~. Path removes the need to do e.g. ~(field (deref my-struct-ptr) num)~, which can become hard to read with deep accessing.
- ~call-on~: Call a member function. For example, if I have a variable ~my-bar~ of type ~Bar~ with member function ~do-thing~, I can call it like so: ~(call-on do-thing my-bar arg-1 arg-2)~
- ~call-on-ptr~: Like ~call-on~, only it works on pointers, e.g. ~(var my-pointer-to-bar (* Bar) (addr a-bar))~, call its member: ~(call-on-ptr do-thing my-pointer-to-bar arg-1 arg-2)~. These can be nested as necessary
- ~call-on-ptr~: Like ~call-on~, only it works on pointers, e.g. ~(var my-pointer-to-bar (addr Bar) (addr a-bar))~, call its member: ~(call-on-ptr do-thing my-pointer-to-bar arg-1 arg-2)~. These can be nested as necessary
- ~call~: Call the first argument as a function. This is necessary when you can't type the function's name directly, e.g. it is a function pointer. For example, to call a static member function: ~(call (in my-class do-static-thing) arg-1 arg-2)~
- ~in~: Scope resolution operator (~::~). Used for both namespaces and static member access. For e.g. ~(in SuperSpace SubSpace Thing)~ would generate ~SuperSpace::SubSpace::Thing~. ~in~ may be used within type signatures
- ~type-cast~: Cast the variable to given type, e.g. ~(var actually-int (* void) (get-stored-var-pointer "my-int"))~ could become an int via ~(type-cast actually-int (* int))~
- ~type-cast~: Cast the variable to given type, e.g. ~(var actually-int (addr void) (get-stored-var-pointer "my-int"))~ could become an int via ~(type-cast actually-int (addr int))~
- ~type~: Parse the first argument as a type. Types are a domain-specific language, so the evaluator needs to know when it should use that special evaluation mode
** Logical expressions
- ~not~: Inverts the boolean result of the argument. ~(not true)~ equals ~false~
@ -323,7 +322,7 @@ The binding would result like so:
We could output a variable declaration like so:
#+BEGIN_SRC lisp
(var (<> std::vector Token) initializer)
(var (template (in std vector) Token) initializer)
(when (!= -1 initializer-index)
(tokenize-push initializer (token-splice-addr (at initializer-index tokens))))
(tokenize-push output
@ -345,7 +344,7 @@ Macros must return ~true~ or ~false~ to denote whether the expansion was success
*** ~tokenize-push~
~tokenize-push~ is the main "quoting" function. The first argument is the output variable. ~output~ is passed in to ~defmacro~ automatically, but you can define other token arrays like so:
#+BEGIN_SRC lisp
(var my-tokens (<> std::vector Token))
(var my-tokens (template (in std vector) Token))
#+END_SRC
~tokenize-push~ copies all source tokens directly to the output until it reaches one of the ~token*~ functions. These functions tell the tokenizer to unpack and insert the tokens in the variables rather than the symbol which is the variable name. Unless otherwise specified, these take any number of arguments:
@ -484,9 +483,9 @@ This configuration label ensures Cakelisp itself doesn't get affected by your ru
There may be cases when you need to do complex logic or modifications of the link command. We use a ~hook~ to give us a chance to do so.
#+BEGIN_SRC lisp
(defun-comptime cakelisp-link-hook (manager (& ModuleManager)
linkCommand (& ProcessCommand)
linkTimeInputs (* ProcessCommandInput) numLinkTimeInputs int
(defun-comptime cakelisp-link-hook (manager (ref ModuleManager)
linkCommand (ref ProcessCommand)
linkTimeInputs (addr ProcessCommandInput) numLinkTimeInputs int
&return bool)
(Log "Cakelisp: Adding link arguments\n")
;; Dynamic loading

2
doc/Roadmap.org

@ -112,7 +112,7 @@ In GameLib, almost all loops would be fine with a number range, e.g. here are so
;; Pointer types, end as an expression
;; This is a little bit too clunky for this construct
(each-range (:iter c (* char) buffer (- (+ buffer (array-size buffer)) 1))
(each-range (:iter c (addr char) buffer (- (+ buffer (array-size buffer)) 1))
(fprintf stderr "Char is '%c'\n" (deref c)))
#+END_SRC

38
doc/Tutorial_Basics.org

@ -123,7 +123,7 @@ Modify our ~main~ function to take command-line arguments:
#+BEGIN_SRC lisp
(defun main (num-arguments int
arguments ([] (* char))
arguments (array (addr char))
&return int)
(unless (= 2 num-arguments)
(fprintf stderr "Expected command argument\n")
@ -187,7 +187,7 @@ Invoke the macro in ~main~:
#+BEGIN_SRC lisp
(defun main (num-arguments int
arguments ([] (* char))
arguments (array (addr char))
&return int)
(unless (= 2 num-arguments)
(fprintf stderr "Expected command argument\n")
@ -302,7 +302,7 @@ Let's create our lookup list. We'll use a C++ ~std::vector~, as it is common in
#+BEGIN_SRC lisp
(defmacro defcommand (command-name symbol arguments array &rest body any)
(get-or-create-comptime-var command-table (<> (in std vector) (* (const Token))))
(get-or-create-comptime-var command-table (template (in std vector) (addr (const Token))))
(call-on-ptr push_back command-table command-name)
(tokenize-push output
@ -343,7 +343,7 @@ We attach the compile-time function to compile-time hooks, or call from macros o
It's time to create a compile-time function which will create our runtime command look-up table.
#+BEGIN_SRC lisp
(defun-comptime create-command-lookup-table (environment (& EvaluatorEnvironment) &return bool)
(defun-comptime create-command-lookup-table (environment (ref EvaluatorEnvironment) &return bool)
(return true))
(add-compile-time-hook post-references-resolved
@ -355,7 +355,7 @@ Each hook has a pre-defined signature, which is what the ~environment~ and other
From our previous note on ~post-references-resolved~ we learned that our hook can be invoked multiple times. Let's store a comptime var to prevent it from being called more than once:
#+BEGIN_SRC lisp
(defun-comptime create-command-lookup-table (environment (& EvaluatorEnvironment) &return bool)
(defun-comptime create-command-lookup-table (environment (ref EvaluatorEnvironment) &return bool)
(get-or-create-comptime-var command-table-already-created bool false)
(when (deref command-table-already-created)
(return true))
@ -370,14 +370,14 @@ Our compile-time function is now hooked up and running when all references are r
Let's get our command table and make a loop to iterate over it, printing each command:
#+BEGIN_SRC lisp
(defun-comptime create-command-lookup-table (environment (& EvaluatorEnvironment) &return bool)
(defun-comptime create-command-lookup-table (environment (ref EvaluatorEnvironment) &return bool)
(get-or-create-comptime-var command-table-already-created bool false)
(when (deref command-table-already-created)
(return true))
(set (deref command-table-already-created) true)
(get-or-create-comptime-var command-table (<> (in std vector) (* (const Token))))
(for-in command-name (* (const Token)) (deref command-table)
(get-or-create-comptime-var command-table (template (in std vector) (addr (const Token))))
(for-in command-name (addr (const Token)) (deref command-table)
(printFormattedToken stderr (deref command-name))
(fprintf stderr "\n"))
(return true))
@ -404,7 +404,7 @@ To do this, we will generate a new array of Tokens and tell Cakelisp to evaluate
We need to create the Token array such that it can always be referred back to in case there are errors. We do this by making sure to allocate it on the heap so that it does not go away on function return or scope exit:
#+BEGIN_SRC lisp
(var command-data (* (<> std::vector Token)) (new (<> std::vector Token)))
(var command-data (addr (template (in std vector) Token)) (new (template (in std vector) Token)))
(call-on push_back (field environment comptimeTokens) command-data)
#+END_SRC
@ -439,18 +439,18 @@ We use ~token-splice-addr~ because ~command-name-string~ is a ~Token~, not a /po
Let's output the generated command data to the console to make sure it's good. Here's the full ~create-command-lookup-table~ so far:
#+BEGIN_SRC lisp
(defun-comptime create-command-lookup-table (environment (& EvaluatorEnvironment) &return bool)
(defun-comptime create-command-lookup-table (environment (ref EvaluatorEnvironment) &return bool)
(get-or-create-comptime-var command-table-already-created bool false)
(when (deref command-table-already-created)
(return true))
(set (deref command-table-already-created) true)
(get-or-create-comptime-var command-table (<> (in std vector) (* (const Token))))
(get-or-create-comptime-var command-table (template (in std vector) (addr (const Token))))
(var command-data (* (<> std::vector Token)) (new (<> std::vector Token)))
(var command-data (addr (template (in std vector) Token)) (new (template (in std vector) Token)))
(call-on push_back (field environment comptimeTokens) command-data)
(for-in command-name (* (const Token)) (deref command-table)
(for-in command-name (addr (const Token)) (deref command-table)
(printFormattedToken stderr (deref command-name))
(fprintf stderr "\n")
@ -485,18 +485,18 @@ Add this before ~main~:
(def-function-signature command-function ())
(defstruct-local command-metadata
name (* (const char))
name (addr (const char))
command command-function)
#+END_SRC
Now the runtime knows what the layout of the data is. In ~create-command-lookup-table~, let's generate another array of tokens to hold the runtime lookup table variable:
#+BEGIN_SRC lisp
(var command-table-tokens (* (<> std::vector Token)) (new (<> std::vector Token)))
(var command-table-tokens (addr (template (in std vector) Token)) (new (template (in std vector) Token)))
(call-on push_back (field environment comptimeTokens) command-table-tokens)
(tokenize-push (deref command-table-tokens)
(var command-table ([] command-metadata)
(var command-table (array command-metadata)
(array (token-splice-array (deref command-data)))))
(prettyPrintTokens (deref command-table-tokens))
@ -511,7 +511,7 @@ We now get:
#+BEGIN_SRC output
say-your-name
(array "say-your-name" say-your-name)
(var command-table ([] command-metadata)
(var command-table (array command-metadata)
(array (array "say-your-name" say-your-name)))
Successfully built and linked a.out
Hello, Cakelisp!
@ -559,7 +559,7 @@ And check the output:
#+BEGIN_SRC output
say-your-name
(array "say-your-name" say-your-name)
(var command-table ([] command-metadata)
(var command-table (array command-metadata)
(array (array "say-your-name" say-your-name)))
Successfully built and linked a.out
Available commands:
@ -601,7 +601,7 @@ Building only:
> ./bin/cakelisp test/Tutorial_Basics.cake
say-your-name
(array "say-your-name" say-your-name)
(var command-table ([] command-metadata)
(var command-table (array command-metadata)
(array (array "say-your-name" say-your-name)))
Successfully built and linked a.out
#+END_SRC

20
runtime/BuildTools.cake

@ -27,7 +27,7 @@
(return true))
;; Returns exit code (0 = success)
(defun-comptime run-process-wait-for-completion-comptime (run-arguments (* RunProcessArguments)
(defun-comptime run-process-wait-for-completion-comptime (run-arguments (addr RunProcessArguments)
&return int)
(run-process-wait-for-completion-body))
@ -38,13 +38,13 @@
should-resolve symbol ;; 'resolve or 'no-resolve
executable-name any
&optional &rest arguments any)
(var specifier-tokens (<> std::vector Token))
(var command-arguments (<> std::vector Token))
(var specifier-tokens (template (in std vector) Token))
(var command-arguments (template (in std vector) Token))
(when arguments
(var current-token (* (const Token)) arguments)
(var current-token (addr (const Token)) arguments)
(var end-paren-index int (FindCloseParenTokenIndex tokens startTokenIndex))
(var end-token (* (const Token)) (addr (at end-paren-index tokens)))
(var end-token (addr (const Token)) (addr (at end-paren-index tokens)))
(while (< current-token end-token)
(cond
;; Special symbols to add optional specifications
@ -52,7 +52,7 @@
(isSpecialSymbol (deref current-token)))
(cond
((= 0 (call-on compare (path current-token > contents) ":in-directory"))
(var next-token (* (const Token)) (+ 1 current-token))
(var next-token (addr (const Token)) (+ 1 current-token))
(unless (< next-token end-token)
(ErrorAtToken (deref next-token) "expected expression for working directory")
(return false))
@ -65,13 +65,13 @@
;; I thought I needed to make it absolute, but at least on Linux, chdir works with relative
;; TODO: Remove this if Windows is fine with it as well
;; (scope ;; Make the path absolute if necessary
;; (var working-dir-alloc (* (const char)) (makeAbsolutePath_Allocated null (token-splice next-token)))
;; (var working-dir-alloc (addr (const char)) (makeAbsolutePath_Allocated null (token-splice next-token)))
;; (unless working-dir-alloc
;; (Logf "error: could not find expected directory %s" (token-splice next-token))
;; (return false))
;; ;; Copy it so we don't need to worry about freeing if something goes wrong
;; (set (token-splice-addr working-dir-str-var) working-dir-alloc)
;; (free (type-cast working-dir-alloc (* void))))
;; (free (type-cast working-dir-alloc (addr void))))
(set (field (token-splice arguments-out-name) working-directory)
(call-on c_str (token-splice-addr working-dir-str-var))))
@ -92,7 +92,7 @@
(? arguments (deref arguments) (deref executable-name)))
(tokenize-push output
(var (token-splice-addr resolved-executable-var) ([] 1024 char) (array 0))
(var (token-splice-addr resolved-executable-var) (array 1024 char) (array 0))
(var (token-splice arguments-out-name) RunProcessArguments (array 0)))
(cond
@ -117,7 +117,7 @@
(set (field (token-splice arguments-out-name) fileToExecute)
(token-splice-addr resolved-executable-var))
(token-splice-array specifier-tokens)
(var (token-splice-addr command-array-var) ([] (* (const char)))
(var (token-splice-addr command-array-var) (array (addr (const char)))
(array (token-splice-addr resolved-executable-var)
(token-splice-array command-arguments) null))
(set (field (token-splice arguments-out-name) arguments)

72
runtime/CHelpers.cake

@ -15,7 +15,7 @@
;; Necessary to create e.g. in C PREFIX "_my_thing"
(defgenerator static-string-combine (string-A (arg-index any) string-B (arg-index any))
(var statement (const ([] CStatementOperation))
(var statement (const (array CStatementOperation))
(array
(array Expression null string-A)
(array Keyword " " -1)
@ -24,7 +24,7 @@
;; e.g. (negate 1) outputs (-1)
(defgenerator negate (to-negate (arg-index any))
(var negate-statement (const ([] CStatementOperation))
(var negate-statement (const (array CStatementOperation))
(array
(array OpenParen null -1)
(array Keyword "-" -1)
@ -36,15 +36,15 @@
;; A global and module-local variable "defer"
(defmacro before-exit (work-body array)
(get-or-create-comptime-var before-exit-work (<> (in std vector) (* (const Token))))
(get-or-create-comptime-var before-exit-work (template (in std vector) (addr (const Token))))
(call-on push_back (deref before-exit-work) work-body)
(return true))
;; Run the deferred code
(defmacro run-before-exit-work ()
(get-or-create-comptime-var before-exit-work (<> (in std vector) (* (const Token))))
(get-or-create-comptime-var before-exit-work (template (in std vector) (addr (const Token))))
;; TODO: Should we sort it eventually?
(for-in start-work-token (* (const Token)) (deref before-exit-work)
(for-in start-work-token (addr (const Token)) (deref before-exit-work)
(tokenize-push output
(token-splice start-work-token)))
(return true))
@ -54,9 +54,9 @@
;;
;; Especially useful for casting from malloc:
;; (var my-thing (* thing) (type-cast (* thing) (malloc (sizeof thing))))
;; (var my-thing (addr thing) (type-cast (addr thing) (malloc (sizeof thing))))
;; vs.
;; (var-cast-to my-thing (* thing) (malloc (sizeof thing)))
;; (var-cast-to my-thing (addr thing) (malloc (sizeof thing)))
(defmacro var-cast-to (var-name symbol type any expression-to-cast any)
(tokenize-push output
(var (token-splice var-name) (token-splice type)
@ -73,7 +73,7 @@
(quick-token-at signature-token signature-index)
(var return-type-start int -1)
(var is-variadic-index int -1)
(var arguments (<> std::vector FunctionArgumentTokens))
(var arguments (template (in std vector) FunctionArgumentTokens))
(unless (parseFunctionSignature tokens signature-index arguments
return-type-start is-variadic-index)
(return false))
@ -110,23 +110,23 @@
(defgenerator forward-declare (&rest start-body-index (index any))
;; TODO: Support global vs local?
(var is-global bool true)
(var output-dest (& (<> std::vector StringOutput))
(var output-dest (ref (template (in std vector) StringOutput))
(? is-global (field output header) (field output source)))
(var end-invocation-index int (FindCloseParenTokenIndex tokens startTokenIndex))
(var current-index int start-body-index)
(var namespace-stack (<> std::vector int))
(var namespace-stack (template (in std vector) int))
(while (< current-index end-invocation-index)
(var current-token (& (const Token)) (at current-index tokens))
(var current-token (ref (const Token)) (at current-index tokens))
;; Invocations
(when (= TokenType_OpenParen (field current-token type))
(var invocation-token (& (const Token)) (at (+ 1 current-index) tokens))
(var invocation-token (ref (const Token)) (at (+ 1 current-index) tokens))
(cond
((= 0 (call-on compare (field invocation-token contents) "namespace"))
(unless (< (+ 3 current-index) end-invocation-index)
(ErrorAtToken invocation-token "missing name or body arguments")
(return false))
(var namespace-name-token (& (const Token)) (at (+ 2 current-index) tokens))
(var namespace-name-token (ref (const Token)) (at (+ 2 current-index) tokens))
(addStringOutput output-dest "namespace"
StringOutMod_SpaceAfter (addr invocation-token))
(addStringOutput output-dest (field namespace-name-token contents)
@ -139,7 +139,7 @@
(unless (< (+ 2 current-index) end-invocation-index)
(ErrorAtToken invocation-token "missing name argument")
(return false))
(var type-name-token (& (const Token)) (at (+ 2 current-index) tokens))
(var type-name-token (ref (const Token)) (at (+ 2 current-index) tokens))
(unless (ExpectTokenType "forward-declare" type-name-token TokenType_Symbol)
(return false))
(addStringOutput output-dest (field invocation-token contents)
@ -163,7 +163,7 @@
(return true))
(defgenerator declare-external (statement-token (arg-index array))
(var statement (const ([] CStatementOperation))
(var statement (const (array CStatementOperation))
(array
(array Keyword "extern" -1)
(array Statement null statement-token)))
@ -172,16 +172,16 @@
(return true))
;; TODO: Better way to handle global vs. local
(defun-comptime defenum-internal (environment (& EvaluatorEnvironment)
context (& (const EvaluatorContext))
tokens (& (const (<> (in std vector) Token)))
(defun-comptime defenum-internal (environment (ref EvaluatorEnvironment)
context (ref (const EvaluatorContext))
tokens (ref (const (template (in std vector) Token)))
startTokenIndex int
output (& GeneratorOutput)
output (ref GeneratorOutput)
is-global bool
name (* (const Token))
name (addr (const Token))
enum-values int
&return bool)
(var output-dest (& (<> std::vector StringOutput))
(var output-dest (ref (template (in std vector) StringOutput))
(? is-global (field output header) (field output source)))
(addStringOutput output-dest "typedef enum" StringOutMod_SpaceAfter name)
@ -190,7 +190,7 @@
(var end-invocation-index int (FindCloseParenTokenIndex tokens startTokenIndex))
(each-token-argument-in tokens enum-values end-invocation-index current-index
(var current-token (* (const Token)) (addr (at current-index tokens)))
(var current-token (addr (const Token)) (addr (at current-index tokens)))
(unless (ExpectTokenType "defenum" (deref current-token) TokenType_Symbol)
(return false))
(addStringOutput output-dest (path current-token > contents)
@ -238,7 +238,7 @@
update (arg-index any)
;; Cannot be optional due to CStatementOutput limitation
&rest body (arg-index any))
(var statement (const ([] CStatementOperation))
(var statement (array (const CStatementOperation))
(array
(array Keyword "for" -1)
(array OpenParen null -1)
@ -301,7 +301,7 @@
(defmacro each-char-in-string (start-char any iterator-name symbol &rest body any)
(tokenize-push output
(c-for (var (token-splice iterator-name) (* char) (token-splice start-char))
(c-for (var (token-splice iterator-name) (addr char) (token-splice start-char))
(deref (token-splice iterator-name))
(incr (token-splice iterator-name))
(token-splice-rest body tokens)))
@ -309,7 +309,7 @@
(defmacro each-char-in-string-const (start-char any iterator-name symbol &rest body any)
(tokenize-push output
(c-for (var (token-splice iterator-name) (* (const char)) (token-splice start-char))
(c-for (var (token-splice iterator-name) (addr (const char)) (token-splice start-char))
(deref (token-splice iterator-name))
(incr (token-splice iterator-name))
(token-splice-rest body tokens)))
@ -331,7 +331,7 @@
;;
(defgenerator c-preprocessor-define-constant (define-name (arg-index symbol) value (arg-index any))
(var define-statement (const ([] CStatementOperation))
(var define-statement (const (array CStatementOperation))
(array
(array Keyword "#define" -1)
(array Expression null define-name)
@ -341,7 +341,7 @@
(return (c-statement-out define-statement)))
(defgenerator c-preprocessor-undefine (define-name symbol)
(var define-statement (const ([] CStatementOperation))
(var define-statement (const (array CStatementOperation))
(array
(array Keyword "#undef" -1)
(array Expression null 1)
@ -353,7 +353,7 @@
&optional false-block (arg-index any))
(if (!= -1 false-block)
(scope
(var statement (const ([] CStatementOperation))
(var statement (const (array CStatementOperation))
(array
(array Keyword "#ifdef" -1)
(array Expression null preprocessor-symbol)
@ -366,7 +366,7 @@
(array KeywordNoSpace "\n" -1)))
(return (c-statement-out statement)))
(scope
(var statement (const ([] CStatementOperation))
(var statement (const (array CStatementOperation))
(array
(array Keyword "#ifdef" -1)
(array Expression null preprocessor-symbol)
@ -384,13 +384,13 @@
;; Note that this circumvents the reference system in order to reduce compile-time cost of using
;; aliased functions. This will probably have to be fixed eventually
(defgenerator output-aliased-c-function-invocation (&optional &rest arguments any)
(var invocation-name (& (const std::string)) (field (at (+ 1 startTokenIndex) tokens) contents))
(var invocation-name (ref (const (in std string))) (field (at (+ 1 startTokenIndex) tokens) contents))
;; TODO Hack: If I was referenced directly, don't do anything, because it's only for dependencies
(when (= 0 (call-on compare invocation-name
"output-aliased-c-function-invocation"))
(return true))
(get-or-create-comptime-var c-function-aliases (<> std::unordered_map std::string std::string))
(def-type-alias FunctionAliasMap (<> std::unordered_map std::string std::string))
(get-or-create-comptime-var c-function-aliases (template (in std unordered_map) (in std string) (in std string)))
(def-type-alias FunctionAliasMap (template (in std unordered_map) (in std string) (in std string)))
(var alias-func-pair (in FunctionAliasMap iterator)
(call-on-ptr find c-function-aliases invocation-name))
@ -400,7 +400,7 @@
"gotten this far")
(return false))
(var underlying-func-name (& (const std::string)) (path alias-func-pair > second))
(var underlying-func-name (ref (const (in std string))) (path alias-func-pair > second))
;; (NoteAtTokenf (at (+ 1 startTokenIndex) tokens) "found %s, outputting %s. Output is %p"
;; (call-on c_str invocation-name) (call-on c_str underlying-func-name)
@ -408,7 +408,7 @@
(if arguments
(scope
(var invocation-statement (const ([] CStatementOperation))
(var invocation-statement (const (array CStatementOperation))
(array
(array KeywordNoSpace (call-on c_str underlying-func-name) -1)
(array OpenParen null -1)
@ -419,7 +419,7 @@
invocation-statement (array-size invocation-statement)
output)))
(scope
(var invocation-statement (const ([] CStatementOperation))
(var invocation-statement (const (array CStatementOperation))
(array
(array KeywordNoSpace (call-on c_str underlying-func-name) -1)
(array OpenParen null -1)
@ -436,7 +436,7 @@
;; alias, we can set the generators table to it
(output-aliased-c-function-invocation)
(get-or-create-comptime-var c-function-aliases (<> std::unordered_map std::string std::string))
(get-or-create-comptime-var c-function-aliases (template (in std unordered_map) (in std string) (in std string)))
(set (at (field alias contents) (deref c-function-aliases)) (field underlying-func-name contents))
;; (Logf "aliasing %s to %s\n" (call-on c_str (field underlying-func-name contents))

14
runtime/Cakelisp.cake

@ -9,9 +9,9 @@
(return true))
(defun-comptime cakelisp-evaluate-build-files-internal
(module-manager (& ModuleManager)
files (* (* (const char))) num-files int
build-outputs (& (<> (in std vector) (in std string)))
(module-manager (ref ModuleManager)
files (addr (addr (const char))) num-files int
build-outputs (ref (template (in std vector) (in std string)))
&return bool)
(each-in-range num-files i
(unless (moduleManagerAddEvaluateFile module-manager (at i files) null)
@ -26,23 +26,23 @@
(return false))
(return true))
(defun-comptime cakelisp-evaluate-build-files (files (* (* (const char))) num-files int
(defun-comptime cakelisp-evaluate-build-files (files (addr (addr (const char))) num-files int
&return bool)
;; (Log "--------------------- { Open Cakelisp sub-instance\n");
(var module-manager ModuleManager (array))
(moduleManagerInitialize module-manager)
(var build-outputs (<> (in std vector) (in std string)))
(var build-outputs (template (in std vector) (in std string)))
(unless (cakelisp-evaluate-build-files-internal module-manager files num-files build-outputs)
(cakelisp-manager-destroy-and module-manager (return false)))
(cakelisp-manager-destroy-and module-manager (return true)))
(defun-comptime cakelisp-evaluate-build-execute-files (files (* (* (const char))) num-files int
(defun-comptime cakelisp-evaluate-build-execute-files (files (addr (addr (const char))) num-files int
&return bool)
;; (Log "--------------------- { Open Cakelisp sub-instance\n");
(var module-manager ModuleManager (array))
(moduleManagerInitialize module-manager)
(var build-outputs (<> (in std vector) (in std string)))
(var build-outputs (template (in std vector) (in std string)))
(unless (cakelisp-evaluate-build-files-internal module-manager files num-files build-outputs)
(cakelisp-manager-destroy-and module-manager (return false)))

40
runtime/ComptimeHelpers.cake

@ -19,18 +19,18 @@
;; Convert type to parseable string as well as unique (to the type) function name
(scope
;; For parsing
(var type-to-string-buffer ([] 256 char) (array 0))
(var type-string-write-head (* char) type-to-string-buffer)
(var type-to-string-buffer (array 256 char) (array 0))
(var type-string-write-head (addr char) type-to-string-buffer)
;; For function name
(var type-to-name-string-buffer ([] 256 char) (array 0))
(var type-name-string-write-head (* char) type-to-name-string-buffer)
(var type-to-name-string-buffer (array 256 char) (array 0))
(var type-name-string-write-head (addr char) type-to-name-string-buffer)
(unless (writeStringToBufferErrorToken "destroy-"
(addr type-name-string-write-head) type-to-name-string-buffer
(sizeof type-to-name-string-buffer) var-type)
(return false))
(var current-type-token (* (const Token)) (addr var-type))
(var end-type-token (* (const Token)) (FindTokenExpressionEnd current-type-token))
(var current-type-token (addr (const Token)) (addr var-type))
(var end-type-token (addr (const Token)) (FindTokenExpressionEnd current-type-token))
(while (<= current-type-token end-type-token)
(unless (appendTokenToString (deref current-type-token) (addr type-string-write-head)
type-to-string-buffer (sizeof type-to-string-buffer))
@ -72,7 +72,7 @@
(incr current-type-token))
(set (field var-type-str contents) type-to-string-buffer)
(var current-char (* char) type-to-name-string-buffer)
(var current-char (addr char) type-to-name-string-buffer)
(while (!= (deref current-char) 0)
(when (= (deref current-char) ':')
(set (deref current-char) '-'))
@ -85,18 +85,18 @@
(var destroy-var-func-name-symbol Token destroy-var-func-name-str)
(set (field destroy-var-func-name-symbol type) TokenType_Symbol)
(var destroy-func-name (* (const char))
(var destroy-func-name (addr (const char))
(call-on c_str (field destroy-var-func-name-str contents)))
;; Define the destructor if one for this type isn't already defined
(unless (or (findCompileTimeFunction environment destroy-func-name)
(findObjectDefinition environment destroy-func-name))
(var destruction-func-def (* (<> std::vector Token)) (new (<> std::vector Token)))
(var destruction-func-def (addr (template (in std vector) Token)) (new (template (in std vector) Token)))
;; Need to have the environment delete this once it's safe
(call-on push_back (field environment comptimeTokens) destruction-func-def)
(tokenize-push (deref destruction-func-def)
(defun-comptime (token-splice-addr destroy-var-func-name-symbol) (data (* void))
(delete (type-cast data (* (token-splice-addr var-type))))))
(defun-comptime (token-splice-addr destroy-var-func-name-symbol) (data (addr void))
(delete (type-cast data (addr (token-splice-addr var-type))))))
(var destruction-func-context EvaluatorContext context)
;; This doesn't cause the required to propagate because comptime functions are lazily required,
@ -108,7 +108,7 @@
;; We are only outputting a compile-time function, which uses definition's output storage to be
;; built. This throwaway will essentially only have a splice to that output, so we don't really
;; need to keep track of it, except to destroy it once everything is done
(var throwaway-output (* GeneratorOutput) (new GeneratorOutput))
(var throwaway-output (addr GeneratorOutput) (new GeneratorOutput))
(call-on push_back (field environment orphanedOutputs) throwaway-output)
(unless (= 0 (EvaluateGenerate_Recursive environment
destruction-func-context
@ -116,7 +116,7 @@
(deref throwaway-output)))
(return false)))
(var initializer (<> std::vector Token))
(var initializer (template (in std vector) Token))
(when (!= initializer-index -1)
(tokenize-push initializer (set (deref (token-splice-addr bound-var-name))
(token-splice-addr (at initializer-index tokens)))))
@ -125,17 +125,17 @@
;; TODO: Any way to make this less code for each ref? There's a lot here.
;; Yes: Auto-generate construction function and call it instead of copy-pasting
(tokenize-push output
(var (token-splice-addr bound-var-name) (* (token-splice-addr var-type)) null)
(var (token-splice-addr bound-var-name) (addr (token-splice-addr var-type)) null)
(scope
(unless (GetCompileTimeVariable environment
(token-splice-addr var-name) (token-splice-addr var-type-str)
(type-cast (addr (token-splice-addr bound-var-name)) (* (* void))))
(type-cast (addr (token-splice-addr bound-var-name)) (addr (addr void))))
(set (token-splice-addr bound-var-name) (new (token-splice-addr var-type)))
(token-splice-array initializer)
(var destroy-func-name (* (const char)) (token-splice-addr destroy-var-func-name-str))
(var destroy-func-name (addr (const char)) (token-splice-addr destroy-var-func-name-str))
(unless (CreateCompileTimeVariable environment
(token-splice-addr var-name) (token-splice-addr var-type-str)
(type-cast (token-splice-addr bound-var-name) (* void))
(type-cast (token-splice-addr bound-var-name) (addr void))
destroy-func-name)
(delete (token-splice-addr bound-var-name))
(return false)))))
@ -153,7 +153,7 @@
;; ;; Invocation is 0, so skip it
;; (var num-destructured-args int 1)
;; (while (< current-arg-index end-invocation-index)
;; (var current-arg (* (const Token)) (addr (at current-arg-index tokens)))
;; (var current-arg (addr (const Token)) (addr (at current-arg-index tokens)))
;; (var num-destructured-args-token Token (array TokenType_Symbol (std::to_string num-destructured-args)
;; "test/ComptimeHelpers.cake" 1 1 1))
;; (unless (ExpectTokenType "destructure-arguments" (at current-arg-index tokens) TokenType_Symbol)
@ -176,7 +176,7 @@
;; Assumes tokens is the array of tokens
(defmacro quick-token-at (name symbol index any)
(tokenize-push output (var (token-splice name) (& (const Token))
(tokenize-push output (var (token-splice name) (ref (const Token))
(at (token-splice index) tokens)))
(return true))
@ -237,7 +237,7 @@
(defmacro token-contents-snprintf (token any format string &rest arguments any)
(tokenize-push output
(scope
(var token-contents-printf-buffer ([] 256 char) (array 0))
(var token-contents-printf-buffer (array 256 char) (array 0))
(var num-printed size_t
(snprintf token-contents-printf-buffer (sizeof token-contents-printf-buffer)
(token-splice format)

8
runtime/CppHelpers.cake

@ -16,7 +16,7 @@
(findObjectDefinition environment (call-on c_str (path context . definitionName > contents)))
RequiredFeature_CppInDefinition name)
(var constructor-var-statement (const ([] CStatementOperation))
(var constructor-var-statement (const (array CStatementOperation))
(array
(array TypeNoArray null type)
(array Keyword " " -1)
@ -42,7 +42,7 @@
RequiredFeature_Cpp name)
;; Class declaration
(var class-declaration-output (& (<> (in std vector) StringOutput))
(var class-declaration-output (ref (template (in std vector) StringOutput))
(? is-local (field output source) (field output header)))
(addStringOutput class-declaration-output "class" StringOutMod_SpaceAfter name)
(addStringOutput class-declaration-output (path name > contents)
@ -52,9 +52,9 @@
;; Body of class
(var end-invocation-index int (FindCloseParenTokenIndex tokens startTokenIndex))
(var current-index int class-body)
;; (var member-variables (<> (in std vector) Token) )
;; (var member-variables (template (in std vector) Token) )
(while (< current-index end-invocation-index)
(var current-token (& (const Token)) (at current-index tokens))
(var current-token (ref (const Token)) (at current-index tokens))
(cond
;; Symbols in class bodies always denote a data member declaration
((= TokenType_Symbol (field current-token type))

40
runtime/DynamicLoader.cake

@ -17,22 +17,22 @@
"This module requires platform-specific code. Please define your platform before importing" \
" this module, e.g.: (comptime-define-symbol 'Unix). Supported platforms: 'Unix, 'Windows")))
(def-type-alias-global DynamicLibHandle (* void))
(def-type-alias-global DynamicLibHandle (addr void))
(c-preprocessor-define MAX_PATH_LENGTH 256)
(defstruct DynamicLibrary
handle DynamicLibHandle)
(def-type-alias DynamicLibraryMap (<> std::unordered_map std::string DynamicLibrary))
(def-type-alias DynamicLibraryMap (template (in std unordered_map) (in std string) DynamicLibrary))
(var dynamicLibraries DynamicLibraryMap)
;; allow-global-linking = Allow subsequently loaded libraries to resolve from this library You do
;; NOT want this if you intend to reload the library, because it may resolve to the old version
(defun dynamic-library-load (libraryPath (* (const char))
(defun dynamic-library-load (libraryPath (addr (const char))
allow-global-linking bool
&return DynamicLibHandle)
(var libHandle (* void) null)
(var libHandle (addr void) null)
(comptime-cond
('Unix
@ -47,16 +47,16 @@
(set libHandle (dlopen libraryPath (bit-or RTLD_LAZY RTLD_GLOBAL)))
(set libHandle (dlopen libraryPath (bit-or RTLD_LAZY RTLD_LOCAL))))
(var error (* (const char)) (dlerror))
(var error (addr (const char)) (dlerror))
(when (or (not libHandle) error)
(fprintf stderr "DynamicLoader Error:\n%s\n" error)
(return null)))
('Windows
;; TODO Clean this up! Only the cakelispBin is necessary I think (need to double check that)
;; TODO Clear added dirs after? (RemoveDllDirectory())
(var absoluteLibPath (* char)
(var absoluteLibPath (addr char)
(make-absolute-path-allocated null libraryPath))
(var dllDirectory ([] MAX_PATH_LENGTH char) (array 0))
(var dllDirectory (array MAX_PATH_LENGTH char) (array 0))
(get-directory-from-path absoluteLibPath dllDirectory (sizeof dllDirectory))
(path-convert-to-backward-slashes absoluteLibPath)
(var dll-directory-length size_t (strlen dllDirectory))
@ -66,7 +66,7 @@
(path-convert-to-backward-slashes dllDirectory)
(scope ;; DLL directory
(var wchars_num int (MultiByteToWideChar CP_UTF8 0 dllDirectory -1 null 0))
(var wstrDllDirectory (* wchar_t) (new-array wchars_num wchar_t))
(var wstrDllDirectory (addr wchar_t) (new-array wchars_num wchar_t))
(MultiByteToWideChar CP_UTF8 0 dllDirectory -1 wstrDllDirectory wchars_num)
(AddDllDirectory wstrDllDirectory)
(delete-array wstrDllDirectory))
@ -74,13 +74,13 @@
;; When loading cakelisp.lib, it will actually need to find cakelisp.exe for the symbols
;; This is only necessary for Cakelisp itself; left here for reference
;; (scope ;; Cakelisp directory
;; (var cakelispBinDirectory (* (const char))
;; (var cakelispBinDirectory (addr (const char))
;; (makeAbsolutePath_Allocated null "bin"))
;; (var wchars_num int (MultiByteToWideChar CP_UTF8 0 cakelispBinDirectory -1 null 0))
;; (var wstrDllDirectory (* wchar_t) (new-array wchars_num wchar_t))
;; (var wstrDllDirectory (addr wchar_t) (new-array wchars_num wchar_t))
;; (MultiByteToWideChar CP_UTF8 0 cakelispBinDirectory -1 wstrDllDirectory wchars_num)
;; (AddDllDirectory wstrDllDirectory)
;; (free (type-cast cakelispBinDirectory (* void)))
;; (free (type-cast cakelispBinDirectory (addr void)))
;; (delete-array wstrDllDirectory))
(set libHandle (LoadLibraryEx absoluteLibPath null
@ -89,16 +89,16 @@
(unless libHandle
(fprintf stderr "DynamicLoader Error: Failed to load %s with code %d\n" absoluteLibPath
(GetLastError))
(free (type-cast absoluteLibPath (* void)))
(free (type-cast absoluteLibPath (addr void)))
(return null))
(free (type-cast absoluteLibPath (* void)))))
(free (type-cast absoluteLibPath (addr void)))))
(set (at libraryPath dynamicLibraries) (array libHandle))
(return libHandle))
(defun dynamic-library-get-symbol (library DynamicLibHandle
symbolName (* (const char))
&return (* void))
symbolName (addr (const char))
&return (addr void))
(unless library
(fprintf stderr "DynamicLoader Error: Received empty library handle\n")
(return null))
@ -106,12 +106,12 @@
(comptime-cond
('Unix
;; Clear any existing error before running dlsym
(var error (* char) (dlerror))
(var error (addr char) (dlerror))
(when error
(fprintf stderr "DynamicLoader Error:\n%s\n" error)
(return null))
(var symbol (* void) (dlsym library symbolName))
(var symbol (addr void) (dlsym library symbolName))
(set error (dlerror))
(when error
@ -120,9 +120,9 @@
(return symbol))
('Windows
(var procedure (* void)
(var procedure (addr void)
(type-cast
(GetProcAddress (type-cast library HINSTANCE) symbolName) (* void)))
(GetProcAddress (type-cast library HINSTANCE) symbolName) (addr void)))
(unless procedure
(fprintf stderr "DynamicLoader Error:\n%d\n" (GetLastError))
(return null))
@ -131,7 +131,7 @@
(return null))))
(defun dynamic-library-close-all ()
(for-in libraryPair (& (<> std::pair (const std::string) DynamicLibrary)) dynamicLibraries
(for-in libraryPair (ref (template (in std pair) (const (in std string)) DynamicLibrary)) dynamicLibraries
(comptime-cond
('Unix
(dlclose (field libraryPair second handle)))

48
runtime/FileUtilities.cake

@ -15,36 +15,36 @@
"This module requires platform-specific code. Please define your platform before importing" \
" this module, e.g.: (comptime-define-symbol 'Unix). Supported platforms: 'Unix, 'Windows")))
(defun path-convert-to-forward-slashes (path-str (* char))
(defun path-convert-to-forward-slashes (path-str (addr char))
(each-char-in-string path-str current-char
(when (= (deref current-char) '\\')
(set (deref current-char) '/'))))
(defun path-convert-to-backward-slashes (path-str (* char))
(defun path-convert-to-backward-slashes (path-str (addr char))
(each-char-in-string path-str current-char
(when (= (deref current-char) '/')
(set (deref current-char) '\\'))))
;; Converts to forward slashes!
(defun make-absolute-path-allocated (fromDirectory (* (const char)) filePath (* (const char))
&return (* char))
(defun make-absolute-path-allocated (fromDirectory (addr (const char)) filePath (addr (const char))
&return (addr char))
(comptime-cond
('Unix
;; Second condition allows for absolute paths
(if (and fromDirectory (!= (at 0 filePath) '/'))
(scope
(var relativePath ([] MAX_PATH_LENGTH char) (array 0))
(var relativePath (array MAX_PATH_LENGTH char) (array 0))
(safe-string-print relativePath (sizeof relativePath) "%s/%s" fromDirectory filePath)
(return (realpath relativePath null)))
(scope
;; The path will be relative to the binary's working directory
(return (realpath filePath null)))))
('Windows
(var absolutePath (* char) (type-cast (calloc MAX_PATH_LENGTH (sizeof char)) (* char)))
(var absolutePath (addr char) (type-cast (calloc MAX_PATH_LENGTH (sizeof char)) (addr char)))
(var isValid bool false)
(if fromDirectory
(scope
(var relativePath ([] MAX_PATH_LENGTH char) (array 0))
(var relativePath (array MAX_PATH_LENGTH char) (array 0))
(safe-string-print relativePath (sizeof relativePath) "%s/%s" fromDirectory filePath)
(set isValid (_fullpath absolutePath relativePath MAX_PATH_LENGTH)))
(set isValid (_fullpath absolutePath filePath MAX_PATH_LENGTH)))
@ -62,23 +62,23 @@
(return null))))
;; Expects: forward slash path. Returns: forward slash path
(defun get-directory-from-path (path (* (const char)) bufferOut (* char) bufferSize int)
(defun get-directory-from-path (path (addr (const char)) bufferOut (addr char) bufferSize int)
(comptime-cond
('Unix
(var pathCopy (* char) (string-duplicate path))
(var dirName (* (const char)) (dirname pathCopy))
(var pathCopy (addr char) (string-duplicate path))
(var dirName (addr (const char)) (dirname pathCopy))
(safe-string-print bufferOut bufferSize "%s" dirName)
(free pathCopy))
('Windows
(var converted-path ([] MAX_PATH_LENGTH char) (array 0))
(var converted-path (array MAX_PATH_LENGTH char) (array 0))
(var num-printed size_t
(snprintf converted-path (sizeof converted-path) "%s" path))
(when (= (at (- num-printed 1) converted-path) '/')
(set (at (- num-printed 1) converted-path) 0))
(path-convert-to-backward-slashes converted-path)
(var drive ([] _MAX_DRIVE char))
(var dir ([] _MAX_DIR char))
(var drive (array _MAX_DRIVE char))
(var dir (array _MAX_DIR char))
;; char fname[_MAX_FNAME];
;; char ext[_MAX_EXT];
(_splitpath_s converted-path drive (sizeof drive) dir (sizeof dir)
@ -121,7 +121,7 @@
('Unix
(c-import "<unistd.h>")))
(defun file-exists (filename (* (const char)) &return bool)
(defun file-exists (filename (addr (const char)) &return bool)
(comptime-cond
('Unix
(return (!= -1 (access filename F_OK))))
@ -129,36 +129,36 @@
(return (!= (GetFileAttributes filename) INVALID_FILE_ATTRIBUTES))))
(return false))
(defun read-file-into-memory-ex (in-file (* FILE)
(defun read-file-into-memory-ex (in-file (addr FILE)
;; If file is larger than this, quit early
;; This allows the program to decide to handle large files differently
;; Pass 0 for no max
maximum-size size_t
size-out (* size_t)
&return (* char))
size-out (addr size_t)
&return (addr char))
(fseek in-file 0 SEEK_END)
(set (deref size-out) (ftell in-file))
(rewind in-file)
(when (and maximum-size (> (deref size-out) maximum-size))
(return null))
(var-cast-to out-buffer (* char) (malloc (+ 1 (deref size-out))))
(var-cast-to out-buffer (addr char) (malloc (+ 1 (deref size-out))))
(fread out-buffer (deref size-out) 1 in-file)
(set (at (deref size-out) out-buffer) 0)
(return out-buffer))
;; TODO: Windows CreateFile version of this
(defun read-file-into-memory (in-file (* FILE) &return (* char))
(defun read-file-into-memory (in-file (addr FILE) &return (addr char))
(fseek in-file 0 SEEK_END)
(var file-size size_t)
(return (read-file-into-memory-ex in-file 0 (addr file-size))))
(defun write-string (out-file (* FILE) out-string (* (const char)))
(defun write-string (out-file (addr FILE) out-string (addr (const char)))
(var string-length size_t (strlen out-string))
;; (fprintf stderr "%s has length %d\n" out-string (type-cast string-length int))
(fwrite (addr string-length) (sizeof string-length) 1 out-file)
(fwrite out-string string-length 1 out-file))
(defun read-string (in-file (* FILE) out-string-buffer (* char) out-string-buffer-length size_t)
(defun read-string (in-file (addr FILE) out-string-buffer (addr char) out-string-buffer-length size_t)
(var string-length size_t 0)
(fread (addr string-length) (sizeof string-length) 1 in-file)
;; (fprintf stderr "Next string has length %d\n" (type-cast string-length int))
@ -179,16 +179,16 @@
(ignore ;; For reference
(defun-local test--load-save ()
(scope
(var test-file (* FILE) (fopen "Test.bin" "wb"))
(var test-file (addr FILE) (fopen "Test.bin" "wb"))
(unless test-file
(return 1))
(write-string test-file "This is a test string")
(fclose test-file)
(var read-file (* FILE) (fopen "Test.bin" "rb"))
(var read-file (addr FILE) (fopen "Test.bin" "rb"))
(unless read-file
(return 1))
(var read-buffer ([] 256 char) (array 0))
(var read-buffer (array 256 char) (array 0))
(read-string read-file read-buffer (sizeof read-buffer))
(fprintf stderr "Got \"%s\"\n" read-buffer)
(fclose read-file))))

2
runtime/HotLoader.cake

@ -6,7 +6,7 @@
(def-function-signature reload-entry-point-signature (&return bool))
(var hot-reload-entry-point-func reload-entry-point-signature null)
(register-function-pointer (type-cast (addr hot-reload-entry-point-func) (* (* void)))
(register-function-pointer (type-cast (addr hot-reload-entry-point-func) (addr (addr void)))
;; TODO Support name conversion at runtime? (conversion requires tokens)
"reloadableEntryPoint")

46
runtime/HotReloading.cake

@ -12,29 +12,29 @@
;; Function management
;;
(def-type-alias FunctionReferenceArray (<> std::vector (* (* void))))
(def-type-alias FunctionReferenceMap (<> std::unordered_map std::string FunctionReferenceArray))
(def-type-alias FunctionReferenceArray (template (in std vector) (addr (addr void))))
(def-type-alias FunctionReferenceMap (template (in std unordered_map) (in std string) FunctionReferenceArray))
(def-type-alias FunctionReferenceMapIterator (in FunctionReferenceMap iterator))
(def-type-alias FunctionReferenceMapPair (<> std::pair (const std::string) FunctionReferenceArray))
(def-type-alias FunctionReferenceMapPair (template (in std pair) (const (in std string)) FunctionReferenceArray))
(var registered-functions FunctionReferenceMap)
(var current-lib DynamicLibHandle null)
(comptime-cond
('Unix
(var hot-reload-lib-path (* (const char)) "libGeneratedCakelisp.so")
(var hot-reload-lib-path (addr (const char)) "libGeneratedCakelisp.so")
;; Not used on Unix due to more advanced versioning required to avoid caching issues
(var hot-reload-active-lib-path (* (const char)) "libGeneratedCakelisp_Active.so"))
(var hot-reload-active-lib-path (addr (const char)) "libGeneratedCakelisp_Active.so"))
('Windows
(var hot-reload-lib-path (* (const char)) "libGeneratedCakelisp.dll")
(var hot-reload-lib-path (addr (const char)) "libGeneratedCakelisp.dll")
;; Need to copy it so the programmer can modify the other file while we're running this one
(var hot-reload-active-lib-path (* (const char)) "libGeneratedCakelisp_Active.dll")))
(var hot-reload-active-lib-path (addr (const char)) "libGeneratedCakelisp_Active.dll")))
;; This is incremented each time a reload occurs, to avoid caching problems on Linux
(var hot-reload-lib-version-number int 0)
(defun register-function-pointer (function-pointer (* (* void))
function-name (* (const char)))
(defun register-function-pointer (function-pointer (addr (addr void))
function-name (addr (const char)))
(var findIt FunctionReferenceMapIterator
(call-on find registered-functions function-name))
(if (= findIt (call-on end registered-functions))
@ -47,17 +47,17 @@
(call (in std move) new-function-pointer-array)))
(call-on push_back (path findIt > second) function-pointer)))
(defun-local copy-binary-file-to (srcFilename (* (const char))
destFilename (* (const char)) &return bool)
(defun-local copy-binary-file-to (srcFilename (addr (const char))
destFilename (addr (const char)) &return bool)
;; Note: man 3 fopen says "b" is unnecessary on Linux, but I'll keep it anyways
(var srcFile (* FILE) (fopen srcFilename "rb"))
(var destFile (* FILE) (fopen destFilename "wb"))
(var srcFile (addr FILE) (fopen srcFilename "rb"))
(var destFile (addr FILE) (fopen destFilename "wb"))
(when (or (not srcFile) (not destFile))
(perror "fopen: ")
(fprintf stderr "error: failed to copy %s to %s\n" srcFilename destFilename)
(return false))
(var buffer ([] 4096 char))
(var buffer (array 4096 char))
(var totalCopied size_t 0)
(var numRead size_t (fread buffer (sizeof (at 0 buffer)) (array-size buffer) srcFile))
(while numRead
@ -82,7 +82,7 @@
;; We need to use a new temporary file each time we do a reload, otherwise mmap/cache issues segfault
;; See https://bugzilla.redhat.com/show_bug.cgi?id=1327623
(scope ;; Clean up the old version, so they don't stack up and eat all the disk space
(var prev-library-name ([] 256 char) (array 0))
(var prev-library-name (array 256 char) (array 0))
(snprintf prev-library-name (sizeof prev-library-name) "libHotReloadingTemp_%d.so"
hot-reload-lib-version-number)
(when (!= -1 (access prev-library-name F_OK))
@ -90,7 +90,7 @@
;; Must be a unique filename, else the caching will bite us
(incr hot-reload-lib-version-number)
(var temp-library-name ([] 256 char) (array 0))
(var temp-library-name (array 256 char) (array 0))
(snprintf temp-library-name (sizeof temp-library-name) "libHotReloadingTemp_%d.so"
hot-reload-lib-version-number)
(unless (copy-binary-file-to hot-reload-lib-path temp-library-name)
@ -108,7 +108,7 @@
(return false))
;; Intialize variables
(var global-initializer (* void)
(var global-initializer (addr void)
(dynamic-library-get-symbol current-lib "hotReloadInitializeState"))
(if global-initializer
(scope
@ -116,14 +116,14 @@
(call (type-cast global-initializer global-initializer-signature)))
(fprintf stderr "warning: global initializer 'hotReloadInitializeState' not found!"))
(for-in function-referent-it (& FunctionReferenceMapPair) registered-functions
(var loaded-symbol (* void)
(for-in function-referent-it (ref FunctionReferenceMapPair) registered-functions
(var loaded-symbol (addr void)
(dynamic-library-get-symbol current-lib
(call-on c_str (path function-referent-it . first))))
(unless loaded-symbol
(return false))
;; TODO: What will happen once modules are unloaded? We can't store pointers to their static memory
(for-in function-pointer (* (* void)) (path function-referent-it . second)
(for-in function-pointer (addr (addr void)) (path function-referent-it . second)
(set (deref function-pointer) loaded-symbol)))
(return true))
@ -134,13 +134,13 @@
;; Data/state management
;;
(def-type-alias StateVariableMap (<> std::unordered_map std::string (* void)))
(def-type-alias StateVariableMap (template (in std unordered_map) (in std string) (addr void)))
(def-type-alias StateVariableMapIterator (in StateVariableMap iterator))
(var registered-state-variables StateVariableMap)
(var verbose-variables bool false)
(defun hot-reload-find-variable (name (* (const char)) variable-address-out (* (* void)) &return bool)
(defun hot-reload-find-variable (name (addr (const char)) variable-address-out (addr (addr void)) &return bool)
(var find-it StateVariableMapIterator (call-on find registered-state-variables name))
(unless (!= find-it (call-on end registered-state-variables))
(set variable-address-out nullptr)
@ -153,7 +153,7 @@
(return true))
;; TODO: Free variables. They'll need generated destructors for C++ types (see compile-time vars)
(defun hot-reload-register-variable (name (* (const char)) variable-address (* void))
(defun hot-reload-register-variable (name (addr (const char)) variable-address (addr void))
(set (at name registered-state-variables) variable-address))
;;

94
runtime/HotReloadingCodeModifier.cake

@ -55,7 +55,7 @@
;; TODO: Potential bug if vars miss modified-vars stage. Need to store list of modified vars instead
;; (would also need to keep track of modified references...yikes)
;; TODO: Need to take scope into account before changing a symbol (was it actually a module-private var)
(defun-comptime make-code-hot-reloadable (environment (& EvaluatorEnvironment)
(defun-comptime make-code-hot-reloadable (environment (ref EvaluatorEnvironment)
&return bool)
(var verbose bool false)
@ -67,29 +67,29 @@
(fprintf stderr "HotReloading: Modifying code for hot-reloading.\n")
(fprintf stderr "Subsequent modifications will not be hot-reloading safe\n")
(get-or-create-comptime-var modules-with-import (<> std::unordered_map std::string int))
(get-or-create-comptime-var modules-with-import (template (in std unordered_map) (in std string) int))
(defstruct modify-definition
name (in std string)
expanded-definition (<> std::vector Token)
module (* Module))