You are right when you make a distinction between what language means and how it does what it means.
What does this mean
The first step to compiling a language is to analyze its code to generate an abstract syntax tree. This is a tree that defines what the code you wrote means what it should do. For example, if you have the following code
a = 1 if a: print('not zero')
It will generate a tree that looks more or less like that.
code ___________|______ | | declaration if __|__ ___|____ | | | | a 1 a print | 'not zero'
This means that the code means, but does not report anything about how it executes it.
Edit: Of course, this is far from what Python parsons actually generate, I made a lot of simplification with the goal of readability. Fortunately for us, if you are interested in what is actually created, you can import ast , which provides a Python parser.
import ast code = """ a = 1 if a: print('not zero') """ my_ast = ast.parse(code)
Enjoy checking my_ast .
What is he doing
Once you have AST, you can convert it back to whatever you want. It can be C, it can be machine code, you can even convert it back to Python if you want. The most used Python implementation is CPython, which is written in C.
What happens under the hood is so close to your understanding. Firstly, a language is a set of rules that determine behavior, and only then is there an implementation for these languages that determines how it does it. And yes, of course, you can have different implementations of the same language with slight differences in behavior.