There are no compilers or interpreters. In reality, there are only translators that convert one form into another.
unsigned int gcd (unsigned int a, unsigned int b) { if (a == 0 &&b == 0) b = 1; else if (b == 0) b = a; else if (a != 0) while (a != b) if (a <b) b -= a; else a -= b; return b; }
And this another language, called the "WATCOM C/C++ v10.0a assembler" that is generated by a "C" to "assembler" translator:
gcd: mov ebx,eax mov eax,edx test ebx,ebx jne L1 test edx,edx jne L1 mov eax,1 ret L1: test eax,eax jne L2 mov eax,ebx ret L2: test ebx,ebx je L5 L3; cmp ebx,eax je L5 jae L4 sub eax,ebx jmp L3 L4: sub ebx,eax jmp L3 L5: ret
Every translation is ready by an interpreter. Some interperators are actual silicon hardware. If we translate down to this form, our binary code it sloshes around, and gets translated into (say) changes to pixels on the screen or changes to the contents of a file.
We call silicon interpreters actual machines and the all the other interpreters that read all the other non-binary translations virtual machines (VM). And any computation is really a sequence of translations down a hierachy of virtual machines; e.g. example:
requirements document # translated by "human" into .. deisgn notation # e.g. UML, translated to "C++" by "CAD tool" "c++" # older versions of "c++" translated to "c" "c" # and so it goes on assembler machine code binary # and finally... ------- silicon
The advantages list of pros and cons of virtual machines is based on some excellent article by Wellie Chao.
Every layer of translation slows down the computer. This is less of a problem these days (faster CPUs, better optimizers) but it is something to bear in mind.
I'm going to let you run any old program inside a VM on my hardware? And that program could (say) launch a denial of service of attack on China without me knowing about it? And, if that program breaks through the walls around the virtual machine, it could take all the data on my machine?
Yeah, right, that'll work.
Now, my business critical apps are running on someone else's machines. I do not control my own infrastructure. Can I live with that?
To run N languages on P platforms, just port take a VM that runs the N languges, then port it once for each platform.
Heck, some one has probably done this already if it is a common language (like JAVA) or a poplular plaform (like Windows, Linux, Mac). Which means that, to run your code on many platforms, just make it compatiable with a VM that runs on those platforms.
Most of the virtual machine software on the market today stores a whole disk in the guest environment as a single file in the host environment. Snapshot and rollback capabilities are implemented by storing the change in state in a separate file in the host information.
Running everything on one machine would be great if it all worked, but many times it results in undesirable interactions or even outright conflicts. The cause often is software problems or business requirements, such as the need for isolated security. Virtual machines allow you to isolate each application (or group of applications) in its own sandbox environment. The virtual machines can run on the same physical machine (simplifying IT hardware management), yet appear as independent machines to the software you are running.
If one virtual machine goes down due to application or operating system error, the others continue running, providing services your business needs to function smoothly.
Once you can run N copies on P platforms, why keep P small? Why not, instead, keep hopping your applciations around to any space CPU, whereever it appears?
VMs make it very easy for someone to sell your their spare CPU. They don' have to deal with the details of your application (which can be quirky++). Instead, they ust have to advertise that they handle a small number of virtual machines.
Virtual machines let you test scenarios easily. Most virtual machine software today provides snapshot and rollback capabilities. This means you can stop a virtual machine, create a snapshot, perform more operations in the virtual machine, and then roll back again and again until you have finished your testing.
Sample LISP program:
(define fib (lambda (n) (if (< n 2) 1 (+ (fib (- n 1)) (fib (- n 2)))))) (fib 20)
Note that define is setting the vale "fib" to be a list whose first item is "lambda". This is the internal representation of LISP code.
Lisp evaluates expressions, which can be simple (atoms) or compound (lists).
An atom is a string of characters, which can be letters, digits, and most punctuation; the characters may -not- include spaces, quotes, parentheses, brackets, '.', '#', or ';' (the comment character). In this Lisp, case is significant ( X is different from x ).
A list is a '(', followed by zero or more objects (each of which is an atom or a list), followed by a ')'.
The special object nil is both an atom and the empty list. That is, nil = (). A non-nil list is called a -pair-, because it is represented by a pair of pointers, one to the first element of the list (its -car-), and one to the rest of the list (its -cdr-). For example, the car of ((a list) of stuff) is (a list), and the cdr is (of stuff). For another example, the list (42 69 613) looks like this:
(Geek detail: It's also possible to have a pair whose cdr is not a list; the pair with car A and cdr B is printed as (A . B).)
That's the syntax of programs and data. Now let's consider their meaning. You can use Lisp like a calculator: type in an expression, and Lisp prints its value. If you type 25, it prints 25. If you type (+ 2 2), it prints 4. In general, Lisp the LISP VM translates a particular expression in a particular environment (set of variable bindings) by the following algorithm (in the following, some of the details of the operator names are specific to a particular Lisp interpreter called awklisp- but their operations are the same in all Lisps):
We still need the rules for special forms. They are:
It's possible to define other special forms. Common special forms are:
(let ((<var> <expr>)...) <body>...)Bind each <var> to its corresponding <expr> (evaluated in the current environment), and evaluate <body> in the resulting environment.
(cond (<test-expr> <result-expr>...)... (else <result-expr>...))where the final else clause is optional. Evaluate each <test-expr> in turn, and for the first non-nil result, evaluate its <result-expr>. If none are non-nil, and there's no else clause, return nil.
(and <expr>...)Evaluate each <expr> in order, until one returns nil; then return nil. If none are nil, return the value of the last <expr>.
(or <expr>...)Evaluate each <expr> in order, until one returns non-nil; return that value. If all are nil, return nil.
That's it. Any program that conforms to the above VM can run on any platform that runs a LISP VM.
Now, who wants to buy shares in a computer hardware company?