An interpreter for language X is a program (or a machine, or just some kind of mechanism in general) that executes any program p written in language X such that it performs the effects and evaluates the results as prescribed by the specification of X.
CPUs are usually interpreters for their respective instruction sets, although modern high-performance workstation CPUs are actually more complex than that; they may actually have an underlying proprietary private instruction set and either translate (compile) or interpret the externally visible public instruction set.
A compiler from X to Y is a program (or a machine, or just some kind of mechanism in general) that translates any program p from some language X into a semantically equivalent program p′ in some language Y in such a way that the semantics of the program are preserved, i.e. that interpreting p′ with an interpreter for Y will yield the same results and have the same effects as interpreting p with an interpreter for X. (Note that X and Y may be the same language.)
Note that this requires that a JIT compiler from language X to language Y must somehow work together with an interpreter for language Y, otherwise there wouldn’t be any way to run the program. (So, for example, a JIT compiler that compiles JavaScript to x86 machine code doesn’t make sense without an x86 CPU; it compiles the program while it is running, but without the x86 CPU the program wouldn’t be running.)
types:
just-in-time (JIT) compiler - compiles during running
ahead-of-time (AOT) compiler - compiles before running
self-compiler or resident compiler - a compiler runs on a computer and produces the machine codes for the same computer
cross compiler - a compiler runs on a computer and produces the machine codes for another computer
self-assembler/resident-assembler - an assembler that runs on a computer and produces the machine codes for the same computer
cross-assembler - an assembler that runs on a computer and produces the machine codes for another computer
either:
one-pass assembler - an assembler that assigns the memory addresses to the variables and translates the source code into machine code in the first pass simultaneously
two-pass assembler - an assembler that reads the source code twice. In the first pass, it reads all the variables and assigns them memory addresses. In the second pass, it reads the source code and translates the code into object code.
Linker
In some high-level languages, a program may use functions that are defined in external libraries. These libraries containing these functions are usually predefined & precompiled. The Linker links the references to those libraries. If the linker does not find a library of a function then it informs the compiler and then the compiler generates an error. The compiler automatically invokes the linker as the last step in compiling a program.
Loader
A loader is a program that loads the machine codes of a program into the system memory. A loader is part of an operating system that is responsible for loading programs. It is one of the essential stages in the process of starting a program. Because it places programs into memory and prepares them for execution. Loading a program involves reading the contents of an executable file into memory. Once loading is complete, the operating system starts the program by passing control to the loaded program code. All operating systems that support program loading have loaders. In many operating systems, the loader is permanently resident in memory.