High-level Language
Due to their limitations, we often refer to machine and assembly languages as low-level programming languages. High-level programming languages overcome these limitations of low-level programming languages. Those limitations are:
1. They are machine dependent. We cannot execute a machine/assembly language program on any computer other than the one for which it was written (High-level; machine independent, program written in a high-level language can be executed on any computer having the translator software for the high-level language).
2. They require programmers to have a good knowledge of the internal structure of the computer used (High-level; no need to have knowledge of the internal structure of the computer on which a high-level language program written is executed).
3. It is difficult, error prone, and time consuming to write programs in machine/assembly language because they deal with machine-level coding and require programmers to write one instruction for each machine-level operation (High-level; high-level coding, instead of machine-level coding, enabling the programmers to write instructions using English words and familiar mathematical symbols and expressions).
Compiler
A compiler is a translator program, more sophisticated than an assembler, that translates a high-level language program into its equivalent machine language program. It is so called because it compiles a set of machine language instructions for every program instruction of a high-level language. Compiler design has come a long way since the first compiler was developed in the 1950s. From early mainframe computers to modern cross-platform development, compilers have played a crucial role in making it easier for developers to write and execute programs. The advancements in compiler design, such as optimizing compilers, interactive compilers, compilers for object-oriented programming, platform-independent compilers, and JIT compilers, have made it possible for developers to write more complex and efficient programs. The era of modern compilers is characterized by open-source compilers and a focus on performance and efficiency. A compiler can translate only those source programs that are written in the language which the compiler can translate. Therefore, a computer requires a separate compiler for each high-level language that it supports. Also, an object program for one computer may not be the same as the object program for another computer. Hence, each computer must have its own compiler for a language; separate compilers are required for the same language on different computers.
In addition to translating high-level language instructions into machine language instructions, compilers also detect and indicate certain types of errors in source programs - syntax errors. If a compiler detects one or more errors in a source program, it will not compile it into an object program. In this case, the compiler generates a list of coded error messages indicating the type of errors committed. The programmer uses this error list to re-edit the source program for removing the errors, and creates a modified source program for recompilation.
Linker
A single programmer can write a small program and can store it in a single source code file. However, large size software often consist of several thousands, even several millions, of lines of program code. In modular approach of software development, software consists of multiple source program files. We can modify and compile each source program file independent of other source program files to create a corresponding object program file. In this case, we use a program called linker to combine all object program files (modules) of the software, and to convert them into a final executable program, which is sometimes called load module. Hence, a linker is software that takes multiple object program files (modules) of some software and fits them together to assemble them into the software's final executable form.
Interpreter
Interpreter is another type of translator used to translate a high-level language program into its equivalent machine language program. It takes one statement of the high-level language program, translates it into machine language instructions, and executes the resulting machine language instructions immediately, before taking up the next statement.
With an interpreter, user cannot save the object program for future use, repeated interpretation - translation plus execution - of a program is necessary for its repeated execution. Since an interpreter translates and executes a high-level language program statement-by-statement, it reinterprets (translates and executes) a program statement every time it encounters that statement during program execution.
The main advantage of interpreters over compilers is that an interpreter flags a syntax error in a program statement to a programmer as soon as it interprets the program statement. This allows the programmer to make corrections during interactive program development. Therefore, interpreters make it easier and faster to correct programs.
The main disadvantage of interpreters over compilers is that they are slower than compilers when running a finished program. This is because an interpreter translates each statement from the source program every time it executes it. A compiler translates each statement only once and saves it in the object program.
In some cases a programmer first uses the interpreter to develop and debug his/her program. Then, after reaching a bug-free state, he/she compiles the program to obtain its object code and uses the object code for routine processing subsequently.
Binary system + Electrical circuits
We have seen already that computers work in a binary system of 0s and 1s. Second, computers are electronic devices built upon electrical circuits. Now we are going to bring those two aspects of computing together, implementing digital circuits.
So, digital circuits deal with signals that represent a limited number of states; here we will do with binary which means that 0 and 1 are the only two states to consider. We typically use voltage to represent a 0 or 1 in a digital circuit, where 0 is a low voltage and 1 is a high voltage. Usually low means 0V, and high tends to be 5V, 3.3V, or 1.8V, depending on the design of the circuit. In reality, digital circuits don’t need a precise voltage to register as high or low. Instead, usually a range of voltages registers as high or low. For example, in a nominal 5-volt digital circuit, an input voltage of anywhere between 2V and 5V registers as high, and anywhere between 0V and 0.8V is considered low. Any other voltage level results in undefined behavior in the circuit and should be avoided.
In analog circuits - voltage, current, and resistance could vary over a wide range of values, our world is naturally analog! However, computers work in the digital realm; digital signal is a type of signal that has two discrete levels, either HIGH (1) or LOW (0) and the concept of the Binary number system is the accurate representation of digital signals, based on Boolean algebra.
Usually ground is the lowest voltage in a digital circuit, and all other voltages in that circuit are positive compared to ground. If a digital circuit is battery powered, we consider the negative terminal of the battery to be ground. The same goes for other kinds of DC power supplies; the negative terminal or wire is considered ground.
Modern digital systems are built from millions or billions of transistors. No human being could understand these systems by writing equations describing the movement of electrons in each transistor and solving all of the equations simultaneously. The critical technique for managing complexity is abstraction: hiding details when they are not important. A system can be viewed from many different levels of abstraction.