When is “good enough” enough?
What’s the cost-benefit, the ROI, of deep-diving and solving a problem thoroughly?
Case study
Primary goal: to transpile Nils Holm’s Prolog in Scheme to Python. I’ve already done this kind of thing by transpiling Nils Holm’s Prolog to Javascript.
Approach: I wrote a grammar, in OhmJS, to inhale Nils Holm’s Scheme code. I then began to write a .rwr t2t transpiler from that code to Python. Surprisingly, it’s not as easy as doing the same kind of thing for Javascript. Python has many more restrictions than does Javascript, making Python less useful as an “assembler”, although it must be said that Python is a higher level language than is Javascript. In essence, “higher level” means restricting what can be done - it ain’t the assembler wild west, you can’t do “everything”. Is this a good thing? Yes, if you want human readability and error-catching, no, if you want a free-for-all assembler (a “toolbox” language, as in some of my previous blogs).
Conclusion: transpilation is easy going in one direction, but, is much harder going in the opposite direction.
HLL -> assembler is easy
HLL -> Javascript is easy
HLL -> Lisp is easy
Scheme -> Javascript is easy
Python -> Lisp is easy
Scheme -> Python is hard.
Going from a low level language, like Scheme (a Lisp) is hard because the code is written in terms of low-level operations and you have to reverse-engineer the design intent (DI) of why certain things are being done.
I have to read the Scheme code and reverse-engineer what was intended (DI). The ideal is to create a higher-than-high-level language (HHLL) and to capture my manual reverse-engineering in the HHLL, then have the HHLL emit Python, etc.
So ...
Approach:
I manually wrote a script in .scn (higher than high level language) for metaprolog. I did this by keeping Nils Holm’s code open in one window, then transcribing it in a new notation. I invented the new notation as I went. I guess that I’ve built up enough experience to intuit what can be compiled by a machine and what can’t. That intuition guided me during the transcription process.
I wrote a grammar in OhmJS for this meta-notation.
I tested the grammar in Ohm-editor against progressively larger cases until I got the whole new meta-language to parse correctly. Along the way, I adjusted my ideas of what should be included in the meta-language and adjusted the meta-prolog code accordingly.
Next, I want to build a t2t transpiler that converts the meta-prolog into a bunch of existing programming languages, like Prolog and Common Lisp and ... In essence, I want to treat those languages as “assemblers”.
To do this, I need to write a .rwr specification (or, worse, a whack of JavaScript) that goes along with the OhmJS parser.
To write a .rwr specification, I need to clone the grammar, then tweak it (heavily) to fit .rwr requirements.
Tweaking the cloned grammar looks like a daunting task, that even emacs can’t help me with, much. I need a “structured” parse-editor to do “structured” copy/paste with.
So, I began a small sub-project. I wrote a grammar for Ohm grammars and then wrote a .rwr specification that converts the Ohm grammars into .rwr specifications.
It turns out that there are some hoary edge-cases in this sub-task.
I struggled with automating the edge cases for about 1 day, then, I just gave up. What I had was “good enough” to accomplish the bulk of what I need - the beginnings of an .rwr specification for the meta-language. Solving the problem of making .rwr from Ohm grammars is an interesting problem, but, will need more than 1 day of work. Solving this problem, is just a sub-problem of the main issue. What I have is “good enough”. There are obvious warts in the generated file, but, I can fix them manually using a text editor (in my case, emacs). Fixing by hand will take much less time than solving the rwrmaker problem in full generality.
So, I will run the rwrmaker tool and save its output in a file. Then, I will hand-edit the file - aided by error messages from the OhmJS+rwr workflow - until it compiles cleanly. Then, I will clone it and make a version that emits Common Lisp, and, a version that emits Python. Other versions, like WASM, I will leave to others (if you want to volunteer, contact me).
See Also
References: https://guitarvydas.github.io/2024/01/06/References.html
Blog: https://guitarvydas.github.io/
Videos: https://www.youtube.com/@programmingsimplicity2980
Discord: https://discord.gg/Jjx62ypR
Leanpub: https://leanpub.com/u/paul-tarvydas
Gumroad:
https://tarvydas.gumroad.com
Twitter: @paul_tarvydas