Notes on the M4 Macro Language (2008)
mbreen.com
mbreen.com
M4 was easy to use, and unless you do something crazy, fairly easy to read. I ended up moving most of the actual envar reading/processing logic to a single base m4 file, and so the actual templates would import all necessary variables from the base m4 file and largely keep the original structure.
I wish there was a more "modern" (read: ergonomic) descendant of M4, but M4 is easy enough to understand, flexible, and extensively (if not well) documented enough that it's easy to use, stable, and fast enough for most of my use cases.
Of course, if you operate within a language ecosystem, you can probably find a nice templating system there. Python, for instance, has Jinja2. Or, if your needs are simple, you can use something more basic, like sed(1).
Not really crazy about Jinja2 either, but at this point it seems like it has a lot more mindshare and maintenance.
You have loops and conditionals, and functions that take parameters to modularize it (e.g. a function for each table row).
Sequential composition "concatenates" stdout. At the end you redirect it all like this, which is inconvenient in other languages:
top-level-function > out.html
----For example, I generate this page with shell:
http://www.oilshell.org/releases.html
And it's separated into 3-4 functions here:
https://github.com/oilshell/oil/blob/master/devtools/release...
----
Shell falls down when you have to generate complex logic as with tables [1], but m4 also falls down there.
I actually experimented with many different methods in order to help me design the Oil language.
This is generated with awk, and it has some advantages over shell:
https://www.oilshell.org/release/0.8.pre3/test/spec.wwz/osh....
These two are generated with Python:
https://www.oilshell.org/release/0.8.pre3/test/spec.wwz/alia...
https://www.oilshell.org/release/0.8.pre3/test/wild.wwz/
So I hope Oil will do better than shell, awk, and Python.
-----
Shell needs to be better for escaping. Template languages like Jinja do better there. I put m4 in the "70's-style macro processing bucket" which is generally sloppy about escaping.
I use Python for escaping here, but I do it with O(1) processes and not one process for every tiny fragment that needs to be escaped!
Git Log in HTML: A Harder Problem and A Safe Solution http://www.oilshell.org/blog/2017/09/29.html
If you try using awk or sed, you will need to figure out a magical way to escape whatever arbitrary value you are trying to replace, so you don't break sed's regex or awk's syntax.
M4 was one of the last things I tried, and it was an utter disaster. If any of M4's magical characters collides with the syntax of the file you're trying to template: tough luck. And there is no way to escape your file for M4 either, it's syntax doesn't support that.
I eventually discovered that Bash 4.4+ has some built-in functions for "dumb" string substitution[1], which is exactly what's missing from the default POSIX tools.
Since then I wrote this little wrapper around that functionality, as a simple, single-file, dependency-free Bash script. If you find something like that useful, it's on the link below:
[0] https://github.com/luizberti/templ
[1] https://www.tldp.org/LDP/abs/html/string-manipulation.html
As for perl, it does come "with the system" more often than Ruby, but also not as much as bash.
Even if it did, I reckon bash is something that nowadays people are more accustomed to than perl, and would consider it more of a no brainer.
https://www.gnu.org/software/gettext/manual/html_node/envsub...
The templating language looks similar to capability to that provided by templ.
it's part of `gettext`: https://www.gnu.org/software/gettext/manual/html_node/envsub...
Back when I never cared about m4, Today, I find it interesting to understand what problem its trying to solve. I dont feel like its practical anymore but I think it is important to not discard use of old tools and understand them.
I see it as a more powerful version of CPP. It might be somewhat based on it. CPP seems to have been developed in 1972-3[1], while M4 was developed in 1977.
Today, I think we're using template systems like Mustache, Handlebars, ERB, Jinja, etc. where otherwise macro systems would be used. The difference between macro systems / preprocessors and templates is that the latter comes with syntax (like <%= %> for ERB) that always makes it clear what's handled by the template system. With M4 and CPP, it's implicit and instead you have syntax (`' or []) to exclude stuff from being handled by the macro system.
I think that change is because, before, macro systems were meant to be used mainly with programming languages to avoid repetition. Then, programming languages became concise and more flexible, so they didn't need to be handled by another language like C has CPP. Our need changed to having to avoid repetition with data files like HTML.
Perhaps having implicit expansion when used with an underlying programming language is not so much an issue, or at least it could be handled with a convention like how CPP variables and functions are normally capitalized. With data files, I suppose being able to differentiate between what's HTML and what's ERB was probably more important.
[1] https://softwareengineering.stackexchange.com/questions/3093...
$ m4 << EOF
changequote(],[)dnl
define(foo,bar)dnl
]Lorem ipsum
foo
[foo]
Lorem ipsum[
EOF
Lorem ipsum
foo
bar
Lorem ipsumhttps://github.com/kevinpt/opbasm/blob/master/opbasm/picobla...
Michael Breen's notes are indispensable documentation on how to make effective use of the language.
.. (just a snip follows)
`define(`_'''$`'1```_LENGTH',strlenc(''$`'2``))'dnl
`; "''$`'2``"'
`ifdef(`PB3', ''$`'1```: calltable($4, $1, estr('''$`'2```))dnl
return',dnl
`table '''$`'1```#, [dec2pbhex(cstr('''$`'2```))]
'''$`'1```: loadaddr($2, $3, _'''$`'1```_STR)
jump __`'$5`'_handler
_'''$`'1```_STR: load&return $1, '''$`'1```#')'
)')'
I have to ask if this is right, or whether a parser would have been a whole lot less work. Credit for your skillz, but that looks like so much pain.FWIW I didn't intend for things to go this far. I just started with some simple macros and it snowballed.
I'm told the person who took over that codebase was equal parts perplexed and horrified by it. They ripped out the m4 code generation and just put the generated files directly in the source tree. As the codebase had stabilized, the purpose that m4 served was basically gone.
So, not still in use, but if I found myself in the same situation today, I'd explore other options and consider doing the same thing. Interface default methods might cover some of the need at this point, but it's so far back, I'm hazy on the details.
The nice thing about doing this sort of code generation with m4 rather than the C preprocessor is that m4 preserves formatting (like line breaks) a lot better. Even if you're not using any of the other functionality of m4 (and there's a lot), that alone makes figuring out where things went wrong easier.
After tying everything together with make, I could include plots for any parameter as I was writing the report and have the relevant data generated as necessary. All without re-running simulations that had already produced results.
Thankfully I never used it in context of sendmail or autotools, so no reason to hate it for me, I guess.
Examples: - italics in a live markdown editor usually hides the asterisks after you've closed the pair, but can get them back if you backspace with your cursor at the 's'. - [[]] style links in similar live editors hide/show content as the syntax has been designed to do.
These examples do it for me, the author, not just in publication.
Other examples include CAD software array-style duplication as a single example. Draw one thing, give a command like "copy 10 times" and then move the piece and see 10 copies laid out.
I can imagine using something like that `define` example in m4 and wanting the instruction to propagate throughout my text file, yet retain the command as a sort of undo/toggle option. Again, all for me the author as I think through my work in this editor, publication isn't really the point in these thoughts.
The workflow for applying formulas to org mode tables is fantastic. I'm still an absolute novice, but I can keep an array of TBLFM formulas[1] below my table which I write and execute at will (C-c C-c or ,,) and can retain as a sort of history of the table state. There seem to be an endless variety of hooks I can tap into for these kinds of table editing sessions.
I'm thinking about this in the context of the process of doing research and writing. When my thoughts are not concrete, I do not have a formal document I'm writing but I'm learning and thinking and connecting information. I am in the process of auditing a whole load of these tools and I want a tool that is the superset of all functionality I see, not sacrificing the awesome transclusion of TiddlyWiki or internal links in org mode for the drawing abilities of OneNote.
I think what I want is an exceptionally powerful "viewport" for lack of a better term. I take great inspiration from ZUIs and wonder how to apply the fantastic text based tools I know to a semantically zooming canvas. I don't like directories, I do like juxtaposition of disparate thoughts and projects. Can I have an AutoCAD/OneNote/orgmode? I want one. m4 seems inspires me towards this goal, even though I realize that's not exactly it's intention.
[1]: https://orgmode.org/manual/Durations-and-time-values.html#Du...
- Dropbox Paper specific items like +LinkToDocument, @Person, #tag
- Digital Paper functionality like stylized tables /note red, creates a red box and puts your cursor inside it
- literally just two commandline tools /date, /time
These are all much more convenient than clicking GUI buttons to get these formatting options yet incredibly limited. There are on the order of tens of these capabilities in any similar application. Notion, Slack, TiddlyWiki, etc.
I can think up hundreds of keyboard-text -> computer-things. Sure they are basic and understood processes, but unless they are built by a developer for a walled garden application you can't really have them. In general, this is one of the reasons I primarily use vim/emacs and linux, so I can build my own interface for my computer. But I'm interested in bringing that textgrid customization into a canvas application that supports more media types than text. vim + repl + powerpoint. Keyboard driven, mouse driven, stylus driven and as powerful as a computer can be.
- I can highlight 3 paragraphs and type /column to make a three column layout.
- I can type /tabs to create tabs in my document ala tiddlywiki tabs.
- I can link statusbar like information into a document with /uptime and document how much time this document has been open.
I would like these capabilities in a shared document system to be able to work collaboratively on a higher level than text, and with more programmatic options than the tens that these applications provide.
I think I can't express this well and I'm really at the beginning of thinking about it, thanks for taking the time to read it. Maybe there's nothing here and this is just a manifestation of my frustration of using a computer.
[1]: things I can type on my ~80 or ~100 key keyboard.
Searching the text of pdfs just failed in 3/3 tests I tried going through my old notes to find a document.
As I said I used it in my full class load for a month then entirely gave up on it. Too many papers and too many large documents of notes, I have much more reusability and ease of access and searching my pile of md documents next to a pile of PDFs (I write `zathura path/to/paper`) at the top of each section in my markdown doc that I edit in vim, then when I want it open I put my cursor on it and hit `gx`. This beat out any OneNote workflow I could figure out in a month of daily use.
http://lua-users.org/wiki/StringsTutorial
I considered using longstrings to make a "better CSV" once but then I realized that quoting is only one of the many problems with CSV.
My script is here if you wanna check it out https://github.com/luizberti/templ
PRO: "I've become a huge fan of M4 ... M4 was easy to use, and unless you do something crazy, fairly easy to read. "
CON: "M4 is so impotent and crippling difficult to use, that it led me to create my own dumb templating engine[0]"