FORTH, an ancient language which was the basis for PostScript (which is a Turing Complete language), could very easily do self-modifying code. Here’s an example I found from someone else going over the basics of how it’s done there:
====
So to do self-modifying code in FORTH (that, for example, is precisely
what PostScript, a FORTH dialect, does) you simply follow these
steps:
1. Define whatever data-structure creating tools you need;
2. Set aside a memory buffer wherein to store the code you
plan to generate (as hi-level definitions in strings, e.g.);
3. Re-vector interpretation to buffer, to execute your modifiable
code;
4. When you want to modify it, FORGET all definitions from the
first in your modifiable block to the end (it makes it easier
if these are compiled in their own VOCABULARY or if you have
set up a fence in memory to mark where FORGETting is to begin);
then create your new code in the buffer and interpret ***that***.