Cog: Use pieces of Python code as generators in your source files
nedbatchelder.com
nedbatchelder.com
I feel the tool would be more useful if you could process the target language's AST instead. This would result in hygienic macros as well as making the code easier to analyse (and might solve the whitespace problems as well, as a formatter could render the whole tree in the end, after any code generation was already applied)
That’s probably not what the author (you?) had in mind for Cog, but it is effectively a macro system so tying into the AST could be a big advantage. The only nice part about the text version is that it doesn’t presuppose a host language.
[0]:https://stackoverflow.com/questions/267862/what-makes-lisp-m...
It's much simpler for a tool like this to be "dumb" -- leave the correct syntax to the human, since it's a lot easier for a human to deal with on a one-off basis than to have a group of developers writing and maintaining dozens of AST parsers and code generators.
Also, any modern IDE already needs to parse the AST anyway to provide any half-decent inspection features. The grammars/specs of most popular languages are also readily available and well-maintained.
On the contrary, if you add search/replace style macro processing, you actually make the code more difficult to formally analyze because, then not even an IDE could build a meaningful AST without actually expanding the macros. (which in this case would mean executing arbitrary python code)
But do those IDEs actually execute the in-language generators? Which AST are they reporting, the preprocessed or postprocessed?
I've heavily used M4 with C code before and what I liked was the ability to see and run tools against the postprocessed code. Textual replacement can be thought of as a worse is better approach, a principle behind many of the systems that people enjoy and even find to be elegant (at least as long as they don't look too closely).
I like Cog. It is usefull for a set of problems. Eg. writing big enums for cpp with enum-to-string and back functions etc.
I have found Cog to be very helpful while writing Rust code. I love Rust, but I'd prefer to write my macros in a different language (like Python). I also find debugging macro expansions to be much more painful than debugging Cog macros.
I wonder if this could be integrated with Emacs Org-mode?
Love the name :)
I've layered a kind of DSL (more Python in comments with a different marker) on top of cog so multiple files can reference the same metadata (domain model fields in my case) when doing the codegen.
Whereas macros and templates have compiler support to give line numbers inside the macro/template, generated code errors have an extra step of having to look at the C++, find the error in the generator, rinse, repeat.
Lambdas are better, but if you have to repeat yourself in a way that's too syntactically weird for a template or lambda, you can "#define MY_MACRO(...)" and end it with "#undef MY_MACRO" to keep the namespace clean.
Typically, I don't debug any generated code though, because it's only boilerplate that has been tested a million times already. So that's a bit of a non-issue in the real world.
Just don't, instead find a macro system that actually understands your language.
https://github.com/natefinch/gocog
It's directly built off of cog's ideas and mimics much of cog's interface. (I worked with Ned, cog's author back in the day, and really enjoyed having cog to write boilerplate for me).
gocog is some of the first code I wrote in Go, so it's not super pretty code, but it's a very useful little tool for generating boilerplate.
I eventually realized that A) generated code is completely unmaintainable; and B) the reason I thought I needed code generation is because my base language wasn't flexible enough.
Later on I switched to python and haven't yet hit a problem that I need code generation to solve.
* Insert generated C++ and Python boilerplate code * Generate parametrized tests based on an external data files * Copy code from one place to another and keep them up-to-date * Pinning dependencies across multiple projects using a single source of truth
So this tool is immensely useful.
Does it handle line numbers correctly on errors?
I can't remember if I saw Cog first, and wrote a different version that fit my needs better, or if I only found Cog afterwards...
It uses a more PHP-esque syntax for inserting Python code. It has inline expression syntax, and quote functions, which IMO make it nicer than Cog for using as a code preprocessor--it's easy to make e.g. function specializations, or loop over code blocks. It's not very well documented though, and is probably missing some nice features.
Using your emacs-command would defeat that purpose, because you would need to search the region at every run and re-execute it manually again and again and again. And you would need to documentate the command anyway, because nobody can remember all those regions. So why not automate this task then?
It needs to be standalone so that you can make it part of your build process instead of messing around in some text editor that not everybody wants to use.
In Java I solved the problem of whitespace by just running my result through google-java-format, but I see how Python's offside rule would make that totally impossible.
It is similar to cog in that generated code resides alongside the source code.
Some differences/advantages are:
1. Generators can be pure JavaScript or declarative dot templates.
2. The generators can be distributed as npm packages as the generators are resolved through npm's require resolution.
Might be interesting to revisit the idea.
I've also been thinking of building a trivial little library to use jq for configuration files, where jq syntax is more convenient than JSON, and too where you can always write or alter configuration objects using path-based assignments, so you get to choose JSON-style or TOML-style.
Cog looks a little over-the-top for my purposes, but still looks saner than M4 ;)
i've used it for writing books, and interactive checklists, and for generating static html websites.
this is the one i implemented: https://github.com/secretGeek/pre
This way you'll have the same functionality but with standard tooling for every language. This means conventional debugging, static analysis, testing etc.
And no extra dependencies, too.
I like the idea of everything sharing the same file, too, but it does make working with Python part a bit more difficult.
Also, with keeping it separate, the "every language" part comes up. It doesn't have to by Python if it's a separate code generator. Whatever suits you will work. You can generate code for C in C++. Or assembly in Common Lisp. Everything in anything.