An interpreter is nothing more than a computer program (usually compiled to machine code) that's designed to execute other computer programs.
Like a compiler, an interpreter contains logic to read source code files, parse it (to understand its structure and syntax), and perform semantic analysis.
The difference is that an interpreter does not convert the program instructions to native machine code. Instead, it executes the program itself. You might think of an interpreter as an emulator for a virtual “computer” specially designed to support the interpreted language. This virtual computer will provide:
- The “memory” in which the interpreted program will store its variables, function call stack, etc. Perhaps this will be a hashtable mapping interpreted-language variable names to objects. Memory can be dynamically allocated and deallocated as needed.
- A means of implementing variable assignments (updating the aforementioned “memory” structure), function calls, or other control flow.
- A means of handling any errors/exceptions that occur within the interpreted program.
- A runtime environment that includes the interpreted language's built-in functions and modules for doing math, I/O, etc.
The core of an interpreter is basically just a giant for
loop that iterates over the interpreted program's instructions and executes them one at a time. (See @Eik Eidt's answer for a simple example.) Non-sequential execution (as with goto
statements, if
...else
statements, or function calls) can be implemented by changing the for
loop's “current instruction” index.
It's common for interpreted languages to use some kind of intermediate representation that's easier for the intepreter to work with than raw source code. This can be a “tokenized” representation of the source code, an abstract syntax tree, or a bytecode format that may closely resemble actual machine code. This intermediate representation may be an internal implementation detail that exists only in memory, or it may be saved as a “compiled” file on its own (as with Java .class
files or Python .pyc
files). The interpreter then executes the immediate representation.
There is also the hybrid approach of just-in-time compilation, which compiles the program (or parts thereof) to machine code at runtime and then runs it using the actual computer hardware. This tends to provide higher performance than normal interpretation.