0000000: 01111111 01000101 01001100
01000110 00000010 00000001 .ELF..
0000006: 00000001 00000000 00000000
00000000 00000000 00000000 ......
000000c: 00000000 00000000 00000000
00000000 00000010 00000000 ......
We can give more human-language-like names to instructions
We can give logical names to memory locations
More advanced assemblers allow provide powerful facilities to do computations on these logically named memory locations
More advanced assemblers allow macros in-house (of course, you can also use external macro rewriters like m4) and allow for larger sets of directives (think of them as highly useful pragmas)
0x004000b0 4d31d2 xor r10, r10
0x004000b3 488b5c2408 mov rbx, [rsp+0x8]
0x004000b8 488b542408 mov rdx, [rsp+0x8]
0x004000bd 8a0c250f076. mov cl, [0x60070f]
0x004000c4 443813 cmp [rbx], r10b
0x004000c7 7417 jz 0x4000e0
Knuth's arguments for MIX/MMIX (TAOCP, 3rd edition, p. ix):
Knuth's arguments for MIX/MMIX continued
See also MMIX
Harder to maintain (look at how long it is taking to migrate from MIX to MMIX!)
Processor instructions go through generations, both inside and outside processor families
Inherently imperative coding style
Human languages are one of the most importants of communication we possess
Programming languages share many of the same characteristics that we desire from human languages
There are lots of human languages, and lots of computer languages
Why we change things: we learn better ways and evolve
Specialization in a particular problem space:
Personal preference
Expressiveness!
Ease of re-implementation: if it can be ported quickly, it has big advantages (example BLISS (very hard to impossible to port) versus Pascal (very easy to port via pcode).
Standardization: widely accepted languages generally have official standards and/or canonical implementations.
Open source
Economics, patronage, inertia, (and luck!)
Declarative languages, continued
Declarative languages, continued
Dataflow languages: So far, these type of languages have not proven to be popular. However, they do have some interesting and attractive properties.
Logic programming languages: the classic example, as mentioned previously, is Prolog. However, there are more modern languages, such as the hybrid functional logic programming languages Curry and Mercury.
Imperative languages: these are "Do What I Say" (maybe aspiring to "Do What I Mean", as, for instance, Perl does) languages.
Imperative languages, continued
Imperative languages, continued
It's fun! It's interesting! It's even practical.
The obvious answer is that it is easier to make informed decisions about what language to use for what purpose. "When you only have a hammer, everything looks like a nail" is very true in computer science; learning to use a full suite of appropriate languages can make your life as computer scientist more fulfilling.
Make better use of the lower level bits.
Simulate useful features in languages that lack them. Older languages can benefit from newer concepts and techniques, and sometimes to the easiest way to that benefit is to just simulate the feature.
Techniques developed for parsing programming languages can also be used for other arenas, such as parsing configuration files. I have written more Bison parsers for configuration files than I ever have for my programming language experiments.
More of a spectrum than a Manichaean duality.
In the large, compilers transform your source code into a new form directly executable by a processor, generally called something like "binary form", or "executable form."
In the large, interpreters execute your source code. There are a variety of ways of doing this, some coming quite close to compilation.
Implementations generally are considered as "compilers" if the translator does a lot of work, and "interpreters" if the translator is less taxed.
An amusing example from page 19 of the text: some Basic interpreters actually recommended removing comments to improve performance. Remember, a Basic interpreter has to re-read and then discard those comments each and every time it runs, and lots of comments are lots of unused bytes that must be dealt with each execution of the code.
The initial compilation phase for C is a preprocessing one, with macros much like an advanced assembler like yasm would provide. and conditionals, which let you literally remove inapplicable code (ifdef and its variants.) You can stop the gcc C compiler at this phase with "gcc -E"
The next phase in C compilation is to turn out assembly language code ("gcc -S"); the final phases don't involve the compiler: 1) have the assembler turn this into machine language and 2) have the linker turn all of this into a final binary format for direct execution.
Some natively interpretive languages also allow for compilation; these languages often have late binding properties that will need the equivalent of interpretation even for "compiled" code.
Your text on page 23 labels TeX as a compiler, which is largely true, but pure TeX only goes as far as a file format called DVI, which is not a format understood by any modern printer. DVI can be converted to PDF or PostScript by programs such as dvips and dvipdf. (It is true that there are versions of TeX such as pdftex/pdflatex which can turn out PDF directly.)
Compilation is one of the great triumphs of computer science.
Typical phases of compilation:
Generally a parser will build a parse tree; during this build, various semantic actions can be triggered by semantic action rules embedded in the parser. Other semantics can be done later, perhaps by rewalking the tree and massaging it into more refined (and perhaps more optimized) forms, or later, as dynamic semantic routines invoked at execution.
Target code generation: a compiler can generate assembly code (and many do), or it might generate an intermediate form like LLVM intermediate representation.