Most software is trying to enforce a distinction between "code" and "data", in the sense that whatever we call "data" can only cause very limited set of things to happen - but that's just the program rules that make this distinction, fundamentally it doesn't exist. And thus, all it takes is some little bug in your input parser, or in whatever code interprets[0] that data, and suddenly data becomes code.
See also: most security vulnerabilities that ever existed.
Or maybe an example from the opposite end will be illuminating. Consider WMF/EMF family of image formats[1], that are notable for handling both raster and vector data well. The interesting thing about WMF/EMF files is that the data format itself is... serialized list of function calls to Window's GDI+ API.
(Edit: also, hint: look at the abstraction layers. Your, say, Python program is Python code, but for the interpreter, it's merely data; your Python interpreter itself is merely data for the layer underneath, and so on, and so on.)
You can find countless examples of the same information being code or data in all kinds of software systems - and outside of them, too; anything from music players to DNA. And, going all the way up to theoretical: there is no such thing in nature as "code" distinct from "data". There is none, there is no way to make that distinction, atoms do not carry such property, etc. That distinction is only something we do for convenience, because most of the time it's obvious for us what is code and what is data - but again, that's not something in objective reality, it's merely a subjective opinion.
Skipping the discussion about how we make code/data distinction work (hint: did you prove your data as processed by your program isn't itself a Turing-complete language?) - the "problem" with LLMs is that we expect them to behave with human-like, fully general intelligence, processing all inputs together as a single fused sensory stream. There is no way to introduce a provably perfect distinction between "code" and "data" here without losing some generality in the model.
And you definitely ain't gonna do it with prompts - if one part of the input can instruct the model to do X, another can always make it disregard X. It's true for humans too. Helpful example: imagine you're working a data-entry job; you're told to retype a binder of text into your terminal as-is, ignoring anything the text actually says (it's obviously data). Halfway through the binder, you hit on a part of text that reads as a desperate plea for help from kidnapped slave worker claiming to have produced the data you're retyping, and who's now begging you to tell someone, call police, etc. Are you going to ignore it, just because your boss said you should ignore contents of the data you're transcribing? Are you? Same is going to be true for LLMs - sufficiently convincing input will override whatever input came before.
--
[0] - Interpret, interpreter... - that should in itself be a hint.