It has been nearly a month since my last post, and there have been some interesting developments.
RSS now supported
This blog now has RSS generated. I wrote the generator in Cakelisp, of course. You should be able to add this blog to your favorite feed reader by entering
https://macoy.me as the URL. Note that HTTP is not supported, so depending on your feed reader's defaults, you will likely need to specify the
https:// rather than just
Comments on Linker-Loader
My post on bringing a dynamic environment to C was on the front page of Hacker News a few weeks ago. The post got a good amount of positive comments, which was rewarding. I also got some private emails from some who expressed their interest and support in the endeavor.
Some commentators astutely pointed out that the Tiny C Compiler already supported something similar. In fact, I had already started working on modifying TCC to work towards my goals before this post arrived. I mentioned this project in my argument for self-modifying applications.
Progress on that front is going very well, and I'm excited to demonstrate my results soon. I plan on recording a video demonstration of it once it is ready. Suffice it to say, I think TCC is the way to go in terms of the enabling of a dynamic environment for C. I plan to write why TCC is the answer and what my modifications to it are in a future article.
Recent Cakelisp changes
The dynamic environment project and the RSS feed generator both required some Cakelisp changes.
Deferred macro execution
I wrote about my XML generator in Writing XML with S-expressions. I used this same generator to write the RSS feed XML. However, due to a subtle execution order issue, the RSS feed generator broke the XML writer.
The XML writer consisted of two Cakelisp macros:
- A syntax definition, which tells Cakelisp which things are XML tags.
- A writer. The writer allows the programmer to freely interweave XML tags and Cakelisp code because the writer can simply write the syntax-defined XML tags and execute everything else as Cakelisp code.
The problem arose when the write macro was executed before the syntax definition macro was. There was no facility in Cakelisp to detect dependencies between macros. Most dependencies were resolved simply by using different compilation phases, which I talk about in the Basics tutorial.
Now, macros can decide to defer their execution when they detect that e.g. a definition macro hasn't been executed yet. Cakelisp will respond to this by simply resolving other macros until there are none left to resolve. If all remaining macros are still trying to defer, that means the definition will never be resolved, and the build will fail. A helpful error indicates this by saying e.g.
"Failed to resolve deferred references to <macro name>" and indicating the invocations that couldn't be resolved.
Essentially, this lets macros "wait and see" if they will ever be able to successfully execute.
This is admittedly strange and a bit complex, but because I had only encountered this construct once in all my Cakelisp code, I considered it acceptable.
My dynamic environment project depends on my modified version of Tiny C Compiler. Before I started this project, Cakelisp transpiled to C++ by default. Needless to say, Tiny C Compiler cannot compile C++, so it was in my best interest to prefer C whenever possible.
I had done some preliminary work to output to C instead. The remaining work I did to make outputting to C the default was:
- C had stricter header inclusion rules. For example,
stdbool.hneeds to be included in C, but not in C++.
- Some declarations and definitions needed to be tweaked to prefer C syntax, which C++ also supports
- I had to eschew the use of C++'s
nullptrand instead prefer
NULL, which is supported in both languages. Luckily, in Cakelisp I already had a special keyword
nullthat meant that no "end user" code changes were required there.
- Various changes needed to be made to GameLib, usually header file inclusions
- A separate C compiler option,
build-time-c-compiler, was added to allow setting a specific compiler for C. The
build-time-compilerspecifies the C++ compiler.
Cakelisp will keep track of which language features are required on a module-by-module basis, which means you can intermix C and C++ in the same Cakelisp project. For example, the instant your module references a namespace, that module will then require C++ and be automatically compiled as a C++ file. If you remove that reference it will become a C file instead. This is a nice feature because it means you can still reference C++ dependencies without having to infect your entire project with C++.
My dynamic environment project should be usable regardless of whether you want to write Cakelisp or prefer C. In fact, any language which produces ELF, PE, or COFF files should work with Tiny C Compiler's linker, though any features which depend on knowing the symbol's underlying type will not function.
I am very excited to be approaching my goal of a fully dynamic environment. I think this will not only open the door to new development processes, but also new forms of end-user applications, a la malleable systems.