Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

After doing some research and with the help of people on this site, I finally came to an understanding of what interpretation actually is.

Essentially, what interpretation means is (correct me if I'm wrong):

  1. Scan a piece of code.
  2. Come up with a piece of code in another language, with the same meaning.
  3. Execute that new piece of code.

So for example, and interpreter written in Java that interprets code in C, and sees this line of code:printf("hello world");

Will come up with this line of code: System.out.println("hello world");

And will execute it (as opposed to a compiler, which will store it).

If all of this is true, then here is my question:

I think, that every execution an interpreter carries out, actually creates a chain of interpretations down to the lowest level. I'd like to know if this is true. Here is what I mean:

When an interpreter executes a line of code (which is the new line it came up with, after scanning a line), it actually tells the underlying platform to interpret it.

For example, as we know, an interpreter written in Java that interprets C code, sees printf("hello") and executes in reaction the new line of code System.out.println("hello").

But what does "executes System.out.println("hello")" actually mean? It means to tell the underlying platform (in this case the JVM) to interpret this line.

And so, another interpretation begins: The (in this example) interpreter in the JVM receives the line System.out.println(), comes up with a new line of code with the same meaning in another language, and executes it.

And when it executes the new line of code, again: It actually tells it's underlying platform to interpret it (in this example, the OS). And then another interpretation begins in the OS, which comes up with a new line of code with the same meaning in a different language, and tells an even lower level to interpret it. This continues down to the lowest level of the CPU.

My question is: Is this true? Is every execution an interpreter carries out, actually a call to the underlying platform to interpret? (Thus creating a chain of interpretations down to the lowest level?)

When people say that an interpreter executes code, do they actually mean that it tells it's underlying platform to interpret that code? (And so on?)

(Note: I might be not accurate about the function of the JVM specifically, I missed some steps there. But my question is more general, Java is just an example).

share|improve this question

3 Answers 3

up vote 8 down vote accepted

Yes, but more than that. All of the interpreter's code is interpreted by a lower level, including the "scan a piece of code" step.

However, there are less levels than you think - more than three levels is rare. Jython is one example of an interpreter running on an interpreter. It runs Python code, and is itself written in Java, which is interpreted by a JVM, which is machine code (that was compiled from C or C++) which is interpreted by the CPU.

JVMs commonly are written in C or C++, and run directly on the CPU, not the OS. Native programs run directly on the CPU, alongside the operating system, rather than being interpreted by the operating system. The difference between the OS and a native program is that the OS has more access to the hardware, so that if the native program wants to access the hardware (e.g. to display stuff on the screen) it still needs to ask the OS.

Also, the "coming up with a line of code in a new language" is Just-In-Time Compilation, not strictly interpretation. Many programs traditionally thought of as interpreters (including nearly all JVMs) do use some form of JIT compilation. A JIT compiler looks something like this:

void execute(instruction instructions_to_execute[])
{
    lower_level_function f;
    if(is_translation_in_cache(instructions_to_execute))
        f = get_translation_from_cache(instructions_to_execute);
    else
    {
        f = translate_to_lower_level_instructions(instructions_to_execute);
        put_translation_in_cache(instructions_to_execute, f);
    }
    f();
}

while an interpreter looks like this:

void execute(instruction instructions_to_execute[])
{
    for(instruction i in instructions_to_execute)
    {
        switch(i.type)
        {
        case PRINT_STRING:
            print(i.string_to_print);
            break;
        case ADD:
            variables[i.result_variable] = variables[i.left_variable] + variables[i.right_variable];
            break;
        case CALL:
            execute(functions[i.function_to_call].instructions);
            break;
        // etc
        }
    }
}

JIT compiling is usually faster for frequently run sections of code, because they can be converted into the lowest level code, and then executed directly. Interpreting is faster for sections of code that are infrequently executed, because the compiler (the translate_to_lower_level_instructions function) is slow.

share|improve this answer
    
Thanks for the detailed answer. I'm planning on writing an interpreter in Java for a simple language I want to make. The programs in this language will run inside the interpreter, and the interpreter (since it's a Java program) will run inside the JVM. And the JVM runs on the CPU. So I think that makes it three interpretations in the chain: Code in my language is interpreted by my interpreter, the interpreter in interpreted by the JVM, and the JVM is interpreted by the CPU. Is this correct or am I missing something? –  NPElover Mar 9 at 9:06
    
I wasn't counting the CPU as an interpreter before - fixed now. Yes, that makes three levels. You can of course get as deep as you want, just be aware that each extra level makes things slower. –  immibis Mar 9 at 9:27
    
In order for a program to create this 'chain of interpretations', it simply needs to execute anything, right? For example, a Java program that has System.out.println(), or that does anything else, will triger interpretation in the JVM. Nothing else is needed, right? –  NPElover Mar 9 at 9:37
1  
Yep. When writing code, you usually don't care how it runs. If you needed to do something special, you would've been doing that from day 1 of using java. (Actually, you do need to do something special, if you're using the command line - run the program with java MainClassName or java -jar jarfilename.jar) –  immibis Mar 9 at 10:05
1  
@NPElover: Yes. Your example is executing a variable declaration instruction, and your VM knows this. –  MSalters Mar 11 at 7:54

Essentially, what interpretation means is (correct me if I'm wrong):

  • Scan a piece of code.
  • Come up with a piece of code in another language, with the same meaning.
  • Execute that new piece of code.

Interpreted execution is a very fuzzy term these days, but for understanding what interpretation means, this is not a good view.

At a fundamental level, a piece of code has a meaning that can be expressed as a series of real-world effects. At the lowest level, a machine instruction is a series of bytes that means, for example, "take the contents of registers A and B, treat them as numbers, add them together, and store the result in register C". Concrete electrical signals in the computer.

Executing a piece of code means performing the real-world effect. Again, at the lowest level (machine code), we call this just "execution". Hardware circuits in the CPU are triggered by the instruction's values to redirect signals so that the effect happens (e.g. they connect the adder circuit's inputs to the registers A and B, and the output to register C - this is of course a massively simplified view of things).

But when your code is in a format that the hardware doesn't directly understand (i.e. everything except machine code, even something as simple as assembly), then you need a different way of executing it. And that is where the fundamental difference between interpretation and compilation lies.

One thing you can do with code is translate it to a different form. Translation means taking code as a series of tiny bits, looking up (in a table, a switch, a series of ifs, whatever) equivalent code in the other language, and stringing the results together.

Classically, if the target language is machine code, this is called compiling.

The other thing you can do is write a program that takes a bit of the code, looks up its meaning (again, table, switch, etc.), and executes a bit of preexisting code (another little function in the program) that generates the intended real-world effect. This is the most pure form of interpreting.

However, pure interpretation in this way is not efficient. This is where things get muddled.

A pure interpreter must re-read code over and over. Unless you work in an extremely memory-sensitive environment, this is stupid. (And even if you do, it's stupid, but in a different way.) A better thing to do is do at least part of the job only one time. High-level languages are complex to understand, and I/O to read the code is costly. So there are things that you can do.

For example, you can read the code in larger chunks and build some representation of it in memory - say, an AST. That saves you from reading it over and over. But ASTs are not very memory-efficient, because they hold too much information about the exact structure of the original code.

Another thing you can do is first translate the code to a more compact form that is still a series of instructions, just simpler. Then a pure interpreter for this simpler form can execute a lot faster, because it's simpler. The CPython interpreter, for example, does this. (.pyc files are cached versions of that simpler form.)

You can also go the full compiler route and translate to machine code and let the CPU execute it, only instead of doing that once and distributing the compiled code, you do it as the program starts. This is called JIT (just in time) compilation.

You can add multiple stages to this, first translating to some simpler form, and then compiling that to machine code. And you can vary when you execute each stage. Before the program is distributed. At program startup. When the actual execution reaches a function. Etc.

Thus, you get a wide spectrum of possible execution strategies, and the simple terms "compiling" and "interpreting" become blurred. Also, there aren't compiled and interpreted languages anymore, because a single language may well have different implementations using different strategies.

But here are some examples.

C and C++ are still typically compiled to machine code ahead of time, and the compiled version is distributed. Of course, the compilers are very complex and will use intermediate formats too.

Java and C# are translated to intermediate forms (Java Bytecode and MSIL, respectively) ahead of time in a process also called compilation (because bytecode is kinda similar to machine code in structure and representation), and the compiled form is distributed. Execution of the intermediate form differs between platforms. Java implementations tend to have a pure interpreter run first, but eventually compile down often-used parts of the code to machine code. .Net, as far as I know, only compiles to machine code, at the granularity of functions (i.e. when execution calls a function for the first, time, the IL gets compiled and stored for subsequent executions).

CPython, the standard python implementation, translates to a bytecode when a file is first read (caching the result in another file), and then does pure interpretation of that bytecode.

JavaScript engines have extremely complex implementation strategies because their performance demands are so high. Mozilla's engine, for example, starts out translating to one intermediate form, and interpreting that. Then it has a JIT compiler from that form to machine code for often-used code. And for code that is used very often (and for specially annotated code), it will translate the intermediate form to a different intermediate form, and use a second JIT compiler to translate that to machine code. (The difference between the two JITs is a different trade-off between time spent compiling and quality (read: speed) of the generated code. And the second intermediate form exists because it's more suitable for the JIT that spends more time trying to optimize.)

There are hardly any pure interpreters for high-level languages anymore. In modern usage, an interpreter is a program that takes in high-level code and produces real-world effects, no matter how many translations are in-between. A compiler is a program that takes in code and produces machine code or some not-human-readable intermediate form. And a VM is a program that takes such an intermediate form and produces real-world effects. Thus you can roughly find three categories of implementations (and languages, if you go by typical implementation):

  • Compiled languages, where you have a compiler that produces machine code.
  • VM-based languages, where you have a compiler that produces intermediate code and a VM that executes it.
  • Interpreted languages, where you have some kind of interpreter.
share|improve this answer
    
Thanks for the detailed explanation :) You said that an interpreter is a program that receives source code and produces real-world effects, and that a VM is a program that receives some sort of IL and produces real world effects. Which means that the only difference, but that definition, between and interpreter and a VM, is that a VM executes IL (compiled source code), and an interpreter executes source code. Do you confirm? –  NPElover Mar 10 at 22:34
    
Yes. A VM (in typical usage) is an interpreter for some IL. –  Sebastian Redl Mar 11 at 10:00

Essentially, what interpretation means is (correct me if I'm wrong):

  • Scan a piece of code.
  • Come up with a piece of code in another language, with the same meaning.
  • Execute that new piece of code.

Again. This is an incorrect definition.

Some interpreters behave this way. But some don't. For example, a typical Java bytecode interpreter (e.g. java -int) doesn't need to do any translation of the bytecodes before interpreting them. (And if it does do some minor rewriting, it is only an optimization to make the interpreter run faster.)

When an interpreter executes a line of code (which is the new line it came up with, after scanning a line), it actually tells the underlying platform to interpret it.

No. That is incorrect. In fact, when an interpreter runs, it is an "execution engine" for the code that it is interpreting. It is the execution engine itself that executes the interpreted code ... in whatever language or intermediate code or bytecode it is expressed at that point.

The underlying platform (e.g. the hardware, in the case of the bytecode interpreter in a a JVM) is executing the interpreter itself. It is not executing the code that the interpreter is running. The Java interpreter is not "telling" the hardware to run / interpret / execute the code. It is doing that task itself.

... as opposed to a compiler, which will store it.

The purpose of a compiler is to translate code from one form to another not to "store" it. For example:

  • javac translates Java source code to bytecodes,
  • the JIT compiler translates bytecodes to native code, and
  • the C compiler gcc translates C code to native code.

Say, would the interpreter I described before, the one that will be used to run programs in a language I'll create, and is run on the JVM - Is it considered a VM?

It could be. It depends on whether the VM is exposed to the outside world. If it is not, then it is moot as to whether you consider it a virtual machine.

It serves as a platform for other programs to run on, which is basically what a VM is. But I don't know if it needs more specifications to be considered a VM.

There is no specification. As I said before. None of these terms have a specification. They only have conventional meanings ... and no single agreed definition.

Why? Because the meanings change over time.

Think of a dictionary. Do the definitions in a dictionary define the word? No. What they actually do is to document the various common and historical meanings (plural!) of the word. And different dictionaries may disagree, and the dictionary definitions change over time to reflect the change in language.


FOLLOWUP

I meant something like this: A code in a language has print "hi". It's interpreted in an interpreter that's written in Java. So the interpreter 'comes up' with the new line 'System.out.println("hi")` - Same meaning, different language - and executes it.

Nope. That's not how an interpreter works. There is no "coming up with" process. In your example, there is no new Java code created do that. Rather, the interpreter (which is written in Java) already includes code to execute the operation, and any other possible operation that can be expressed in the source language ... the language that is being interpreted.

(for example, and interpreter written in Java sees something like print "hi" and executes System.out.println("hi"), which will later be complied to be understood by it's underlying platform - the JVM)

Nope. See above. That is not how an interpreter works.

In fact what you are describing sounds like a source code to source code compiler, not an interpreter at all.

A compiler understand a line of code, and comes up with new code with the same meaning - in the language it translates to - and 'stores' it in a file.

Partly correct, but there is no requirement for the target code to be stored in a file. For instance, the code produced by a JIT compiler is typically written to memory, ready for execution the next time the corresponding method is called.

share|improve this answer
    
Thanks for your answer :) I think I didn't explain myself correctly. By saying that an interpreter 'comes up with new code with the same meaning as the interpreted line of code' - I didn't mean 'translation'. I meant something like this: A code in a language has print "hi". It's interpreted in an interpreter that's written in Java. So the interpreter 'comes up' with the new line 'System.out.println("hi")` - Same meaning, different language - and executes it. (An interpreter in Java executes things with Java operations, just like an interpreter written in C executes things with C operations). –  NPElover Mar 10 at 22:44
    
And by saying that a compiler 'stores' the 'new line of code it came up with' - I meant this: An interpreter understands a line of code in a language, and executes that line of code in a language that it's underlying platform will understand (for example, and interpreter written in Java sees something like print "hi" and executes System.out.println("hi"), which will later be complied to be understood by it's underlying platform - the JVM). A compiler understand a line of code, and comes up with new code with the same meaning - in the language it translates to - and 'stores' it in a file. –  NPElover Mar 10 at 22:49
    
This what I meant, I hope it makes sense –  NPElover Mar 10 at 22:51
    
I think I understand what you are saying, but that doesn't make it correct. What you are describing is NOT how an interpreter works ... not even theoretically. –  Stephen C Mar 11 at 9:31

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.