RedDragon is an experiment in building a compiler pipeline that analyses code (malformed / without dependencies / unknown language) across ~15 languages through a single 27-opcode IR, with LLM fallbacks.
The design question: where exactly can LLMs enter a compiler pipeline?
RedDragon has three specific insertion points:
- LLM as an alternative compiler frontend. For languages without a built-in parser, the LLM receives a formal IR spec (all 27 opcodes, lowering templates, worked examples) and translates source to IR directly. No language-specific code needed. This works for Haskell, Elixir, Perl — anything with parseable source.
- LLM for syntax repair. When the parser hits a parse error in malformed source, an LLM fixes the broken spans and the system re-parses. The repair is constrained to syntactic fixes; the LLM doesn't change what the code does.
- LLM as runtime resolver. When the VM hits a call to a function that doesn't exist in the IR (e.g., requests.get()), an LLM can produce plausible return values and side effects, so that execution continues through incomplete code.
All three are optional. When code is complete and well-formed, the pipeline makes zero LLM calls. When an LLM fails at any point, the system falls back to symbolic placeholders and keeps going.
It's not a crazy idea. For example, Amazon's BluAge offering does automatic translation. However, frequently, forward engineering teams do not want a 1:1 translation of the code, because that might end up reproducing the same system organisation of the original Cobol base (in a modern language), and engineers/architects usually want to work on a new design (while maintaining the original domain logic).
So far, this library does not step into the forward engineering territory, and tries to merely provide useful information/artifacts, which could help the reverse engineering teams move (hopefully) faster, but obviously there is a lot of experimentation / inference that can be done on top of the extracted data.
Depends - sometimes/often the goal is just to bring it into a modern language to improve maintainability (i.e. its an easier hire) rather than to do a ground-up rewrite.
Especially as ground-up rewrites are often risky, and bringing it into a modern language might make it easier to incrementally improve/refactor over time.
When people say "bring this cobol code to a new language to improve maintainability", they don't just mean the syntax. Any C developer can learn cobol syntax in 10 minutes. They mean things like use functions instead of gotos, don't use just global variables, don't depend on lots of weird tooling from the parallel world of IBM mainframes, with most of your logic hidden in weird batch scripts with names like ABC@@DEF.
I could write a cobol to c translator in a weekend. Nobody would buy it. Source: I've spent the last year and a half consulting on a huge project to rewrite a cobol codebase.
So basically the real work is not syntax translation (which a machine could do, albeit probably poorly) but semantics translation (which requires a deep understanding of both languages as well as what the code's intent is), so that you can do things like replace goto's with function calls without breaking expected behavior given inputs.
A similar problem to translating procedural code in a language with mutable variables to functional code in a language with immutable variables. A lot of old functions in C etc. were expected to modify their passed-in arguments, for example, which would be a no-no today (note: not in the C space, I'm currently an Elixir dev, but I'm hoping that's now frowned upon!)
I've noticed that LLM's are still not very good at this, too.
I'm unfamiliar with COBOL but I'm currently looking for work; not sure if you'd be up for a conversation just to discuss your work since (for some odd reason) I enjoy refactoring (as well as software preservation and validation); at the very least I'd probably be a decent rubber-duck if you got stuck on something lol
To get the username you'd need to be familiar with at least the old Hebrew translation of Lord of the Rings and my mother :)
I'm enjoying tech-leading this project very much. I get to spend the time needed to write unusually high quality code, and there's a wide funnel that collects interesting debugging challenges and gets the best ones to my desk.
Working with a bank is... not for everyone. Not for me ten years ago, for example.
There are a hundred approaches for as many big banks with mainframe cores. The problem of modernizing a bank is much, much bigger than what language its core logic is written in.
We're talking about a custom software stack grown from a first version written fifty years ago, before the word "coupling" meant anything to programmers.
Yes, there's a lot of it kicking around. This evolved from a personal testbed to iterate on ideas to apply on actual legacy code modernisation work I've been involved in.
The design question: where exactly can LLMs enter a compiler pipeline?
RedDragon has three specific insertion points:
- LLM as an alternative compiler frontend. For languages without a built-in parser, the LLM receives a formal IR spec (all 27 opcodes, lowering templates, worked examples) and translates source to IR directly. No language-specific code needed. This works for Haskell, Elixir, Perl — anything with parseable source.
- LLM for syntax repair. When the parser hits a parse error in malformed source, an LLM fixes the broken spans and the system re-parses. The repair is constrained to syntactic fixes; the LLM doesn't change what the code does.
- LLM as runtime resolver. When the VM hits a call to a function that doesn't exist in the IR (e.g., requests.get()), an LLM can produce plausible return values and side effects, so that execution continues through incomplete code.
All three are optional. When code is complete and well-formed, the pipeline makes zero LLM calls. When an LLM fails at any point, the system falls back to symbolic placeholders and keeps going.
This is a work in progress.