There’s a spectrum.
-
Pure Interpreter: parses the code and stores the abstract syntax tree (AST). When executed a program called the “interpreter” carries out the tasks implied by the AST in terms of modifying the state of the machine in whatever way. (Example the Bash shell)
-
Bytecode Interpreter: parses the code, takes the AST and produces a sequence of byte-codes for an abstract machine, when executed a program called an interpreter carries out the tasks implied by the bytecode. (example CPython, prolog with WAM, erlang + BEAM, early Java)
-
JIT compiled: parses the code, takes the AST and (usually) generates a bytecode sequence, then after some number of executions with a bytecode interpreter (possibly zero) converts the bytecode sequence to machine code and execution from then on is carried out by telling the CPU to jump to the machine code instructions. (example Java, maybe things like C#, a lot of modern javascript)
-
Just Ahead of Time Compiled: parses the code, takes the AST, generates an intermediate IR representation, at first execution, it passes the IR to an optimizing IR to machine code compiler, stores the machine code, and the first time and every time after executes the machine code by having the CPU jump to the machine code instructions. (Julia, SBCL)
-
Ahead of Time Compiled: parses the code, takes the AST and usually generates an intermediate representation, then passes the representation to an optimizing machine code compiler, writes out a file with all of the generated machine code creating an “executable binary” which carries out ONLY the one task. Usually no further compilation occurs during runtime. (C, C++, Rust)
So the “interpreter vs compiler” is orthogonal to “interactive vs non-interactive” in that basically everything but 5 can be interactive. If you collapse 1-4 into “not AOT compiled” and call that “interpreted” it’s a controversial statement because for different people interpreted may mean 1 or both 1 & 2, most people don’t consider 3,4 as interpreted.