Model oriented programming
Model oriented programming is one name among many for a range of techniques that boil down to: specify something formally in a small language that’s just enough to state what you want, then write a program to transform that statement into code in a general purpose programming language.
There is nothing conceptually different about this from defining a programming language and writing a compiler for it. What you actually produce is different because your goals are different.
There are very successful examples of this. The most commercially successful is Simulink from MathWorks. More familiar to web programmers is Svelte. Whirlisp is a pedagogical example of using it to shrink a nontrivial system (in this case, a database).
iMatix has a series on how they use model oriented programming that explains the concepts and practice well. But then also read this comment as a caution about using it casually. Oh, and this kind of system tends to seem messy.
Rascal and Xtext are systems for creating these “little languages” separate from your programs.
You can embed it in the same language. Lisp macros are the classic case. Forth programmers do it, too, such as my old friend’s embedded finite state machine library or embedded FORTRAN subset (docs).
More recent languages like Rust introduce them as well. That community wanted them because they were used to the power of C++’s templates, but didn’t want to persist in that insanity.
Reitit and Malli in Clojure does something interesting. Because Clojure carries its compiler in the runtime, they take a model described as a Lisp macro, but instead of doing simple code generation, they do optimized compilation to JVM bytecode. They also get to impose lots of “compile time” checks with type checking and static analysis that would normally be unavailable in dynamically typed languages like Clojure. A less extreme form is writing in one language and outputting another.