Compiler Construction
Compiler Construction
PDF generated using the open source mwlib toolkit. See http://code.pediapress.com/ for more information.
PDF generated at: Tue, 20 Sep 2011 17:42:02 UTC
Contents
Articles
Introduction 1
Compiler construction 1
Compiler 2
Interpreter 10
History of compiler writing 13
Lexical analysis 21
Lexical analysis 21
Regular expression 25
Regular expression examples 36
Finite-state machine 40
Preprocessor 50
Syntactic analysis 53
Parsing 53
Lookahead 57
Symbol table 60
Abstract syntax 62
Abstract syntax tree 63
Context-free grammar 64
Terminal and nonterminal symbols 76
Left recursion 78
Backus–Naur Form 82
Extended Backus–Naur Form 85
TBNF 90
Top-down parsing 90
Recursive descent parser 92
Tail recursive parser 97
Parsing expression grammar 99
LL parser 105
LR parser 113
Parsing table 122
Simple LR parser 124
Canonical LR parser 125
GLR parser 127
LALR parser 128
Recursive ascent parser 130
Parser combinator 138
Bottom-up parsing 140
Chomsky normal form 146
CYK algorithm 148
Simple precedence grammar 151
Simple precedence parser 152
Operator-precedence grammar 154
Operator-precedence parser 157
Shunting-yard algorithm 161
Chart parser 171
Earley parser 172
The lexer hack 176
Scannerless parsing 177
Tools 398
Compiler-compiler 398
PQCC 400
Compiler Description Language 401
Comparison of regular expression engines 403
Comparison of parser generators 409
Lex 420
flex lexical analyser 423
Quex 430
JLex 433
Ragel 434
yacc 435
Berkeley Yacc 436
ANTLR 437
GNU bison 439
Coco/R 449
GOLD 451
JavaCC 456
JetPAG 457
Lemon Parser Generator 460
ROSE compiler framework 461
SableCC 463
Scannerless Boolean Parser 464
Spirit Parser Framework 465
S/SL programming language 467
SYNTAX 468
Syntax Definition Formalism 469
TREE-META 471
Frameworks supporting the polyhedral model 473
Literature 497
Compilers: Principles, Techniques, and Tools 497
Principles of Compiler Design 499
The Design of an Optimizing Compiler 499
References
Article Sources and Contributors 500
Image Sources, Licenses and Contributors 504
Article Licenses
License 505
1
Introduction
Compiler construction
Compiler construction is an area of computer science that deals with the theory and practice of developing
programming languages and their associated compilers.
The theoretical portion is primarily concerned with syntax, grammar and semantics of programming languages. One
could say that this gives this particular area of computer science a strong tie with linguistics. Some courses on
compiler construction will include a simplified grammar of a spoken language that can be used to form a valid
sentence for the purposes of providing students with an analogy to help them understand how grammar works for
programming languages.
The practical portion covers actual implementation of compilers for languages. Students will typically end up writing
the front end of a compiler for a simplistic teaching language, such as Micro.
Subfields
• Parsing
• Program analysis
• Program transformation
• Compiler or program optimization
• Code generation
Further reading
• Alfred V. Aho, Monica S. Lam, Ravi Sethi, Jeffrey D. Ullman. Compilers: Principles, Techniques, and Tools.
• Michael Wolfe. High-Performance Compilers for Parallel Computing. ISBN 978-0805327304
External links
• The ACE CoSy compiler development suite [1], an industry leading C/C++ compiler development suite.
• The LLVM Compiler Infrastructure project [2], The leading open source compiler tools project.
• Let's Build a Compiler, by Jack Crenshaw [3], An interesting tutorial on compiler construction.
• Compiler Construction at the University of New England [4]
References
[1] http:/ / www. ace. nl/ compiler/ cosy. html
[2] http:/ / llvm. org/
[3] http:/ / compilers. iecc. com/ crenshaw/
[4] http:/ / mcs. une. edu. au/ ~comp319/
Compiler 2
Compiler
A compiler is a computer program (or
set of programs) that transforms source
code written in a programming
language (the source language) into
another computer language (the target
language, often having a binary form
known as object code). The most
common reason for wanting to
transform source code is to create an
executable program.
A compiler is likely to perform many or all of the following operations: lexical analysis, preprocessing, parsing,
semantic analysis (Syntax-directed translation), code generation, and code optimization.
Program faults caused by incorrect compiler behavior can be very difficult to track down and work around; therefore,
compiler implementors invest a lot of time ensuring the correctness of their software.
The term compiler-compiler is sometimes used to refer to a parser generator, a tool often used to help create the lexer
and parser.
Compiler 3
History
Software for early computers was primarily written in assembly language for many years. Higher level programming
languages were not invented until the benefits of being able to reuse software on different kinds of CPUs started to
become significantly greater than the cost of writing a compiler. The very limited memory capacity of early
computers also created many technical problems when implementing a compiler.
Towards the end of the 1950s, machine-independent programming languages were first proposed. Subsequently,
several experimental compilers were developed. The first compiler was written by Grace Hopper, in 1952, for the
A-0 programming language. The FORTRAN team led by John Backus at IBM is generally credited as having
introduced the first complete compiler in 1957. COBOL was an early language to be compiled on multiple
architectures, in 1960.[1]
In many application domains the idea of using a higher level language quickly caught on. Because of the expanding
functionality supported by newer programming languages and the increasing complexity of computer architectures,
compilers have become more and more complex.
Early compilers were written in assembly language. The first self-hosting compiler — capable of compiling its own
source code in a high-level language — was created for Lisp by Tim Hart and Mike Levin at MIT in 1962.[2] Since
the 1970s it has become common practice to implement a compiler in the language it compiles, although both Pascal
and C have been popular choices for implementation language. Building a self-hosting compiler is a bootstrapping
problem—the first such compiler for a language must be compiled either by a compiler written in a different
language, or (as in Hart and Levin's Lisp compiler) compiled by running the compiler in an interpreter.
Compilers in education
Compiler construction and compiler optimization are taught at universities and schools as part of the computer
science curriculum. Such courses are usually supplemented with the implementation of a compiler for an educational
programming language. A well-documented example is Niklaus Wirth's PL/0 compiler, which Wirth used to teach
compiler construction in the 1970s.[3] In spite of its simplicity, the PL/0 compiler introduced several influential
concepts to the field:
1. Program development by stepwise refinement (also the title of a 1971 paper by Wirth)[4]
2. The use of a recursive descent parser
3. The use of EBNF to specify the syntax of a language
4. A code generator producing portable P-code
5. The use of T-diagrams[5] in the formal description of the bootstrapping problem
Compilation
Compilers enabled the development of programs that are machine-independent. Before the development of
FORTRAN (FORmula TRANslator), the first higher-level language, in the 1950s, machine-dependent assembly
language was widely used. While assembly language produces more reusable and relocatable programs than machine
code on the same architecture, it has to be modified or rewritten if the program is to be executed on different
hardware architecture.
With the advance of high-level programming languages soon followed after FORTRAN, such as COBOL, C,
BASIC, programmers can write machine-independent source programs. A compiler translates the high-level source
programs into target programs in machine languages for the specific hardwares. Once the target program is
generated, the user can execute the program.
Compiler 4
Compiler output
One classification of compilers is by the platform on which their generated code executes. This is known as the
target platform.
A native or hosted compiler is one which output is intended to directly run on the same type of computer and
operating system that the compiler itself runs on. The output of a cross compiler is designed to run on a different
platform. Cross compilers are often used when developing software for embedded systems that are not intended to
support a software development environment.
The output of a compiler that produces code for a virtual machine (VM) may or may not be executed on the same
platform as the compiler that produced it. For this reason such compilers are not usually classified as native or cross
compilers.
The lower level language that is target of a compiler may itself be a high-level programming language. C, often
viewed as some sort of portable assembler, can also be the target language of a compiler. E.g.: Cfront, the original
compiler for C++ used C as target language.
arbitrary source code at runtime with regular string operations, and then execute that code by passing it to a special
evaluation function. To implement these features in a compiled language, programs must usually be shipped with a
runtime library that includes a version of the compiler itself.
Hardware compilation
The output of some compilers may target hardware at a very low level, for example a Field Programmable Gate
Array (FPGA) or structured Application-specific integrated circuit (ASIC). Such compilers are said to be hardware
compilers or synthesis tools because the source code they compile effectively controls the final configuration of the
hardware and how it operates; the output of the compilation are not instructions that are executed in sequence - only
an interconnection of transistors or lookup tables. For example, XST is the Xilinx Synthesis Tool used for
configuring FPGAs. Similar tools are available from Altera, Synplicity, Synopsys and other vendors.
Compiler construction
In the early days, the approach taken to compiler design used to be directly affected by the complexity of the
processing, the experience of the person(s) designing it, and the resources available.
A compiler for a relatively simple language written by one person might be a single, monolithic piece of software.
When the source language is large and complex, and high quality output is required, the design may be split into a
number of relatively independent phases. Having separate phases means development can be parceled up into small
parts and given to different people. It also becomes much easier to replace a single phase by an improved one, or to
insert new phases later (e.g., additional optimizations).
The division of the compilation processes into phases was championed by the Production Quality
Compiler-Compiler Project (PQCC) at Carnegie Mellon University. This project introduced the terms front end,
middle end, and back end.
All but the smallest of compilers have more than two phases. However, these phases are usually regarded as being
part of the front end or the back end. The point at which these two ends meet is open to debate. The front end is
generally considered to be where syntactic and semantic processing takes place, along with translation to a lower
level of representation (than source code).
The middle end is usually designed to perform optimizations on a form other than the source code or machine code.
This source code/machine code independence is intended to enable generic optimizations to be shared between
versions of the compiler supporting different languages and target processors.
The back end takes the output from the middle. It may perform more analysis, transformations and optimizations that
are for a particular computer. Then, it generates code for a particular processor and OS.
This front-end/middle/back-end approach makes it possible to combine front ends for different languages with back
ends for different CPUs. Practical examples of this approach are the GNU Compiler Collection, LLVM, and the
Amsterdam Compiler Kit, which have multiple front-ends, shared analysis and multiple back-ends.
Compiler 6
Front end
The front end analyzes the source code to build an internal representation of the program, called the intermediate
representation or IR. It also manages the symbol table, a data structure mapping each symbol in the source code to
associated information such as location, type and scope. This is done over several phases, which includes some of
the following:
1. Line reconstruction. Languages which strop their keywords or allow arbitrary spaces within identifiers require a
phase before parsing, which converts the input character sequence to a canonical form ready for the parser. The
top-down, recursive-descent, table-driven parsers used in the 1960s typically read the source one character at a
time and did not require a separate tokenizing phase. Atlas Autocode, and Imp (and some implementations of
ALGOL and Coral 66) are examples of stropped languages which compilers would have a Line Reconstruction
phase.
2. Lexical analysis breaks the source code text into small pieces called tokens. Each token is a single atomic unit of
the language, for instance a keyword, identifier or symbol name. The token syntax is typically a regular language,
Compiler 7
so a finite state automaton constructed from a regular expression can be used to recognize it. This phase is also
called lexing or scanning, and the software doing lexical analysis is called a lexical analyzer or scanner.
3. Preprocessing. Some languages, e.g., C, require a preprocessing phase which supports macro substitution and
conditional compilation. Typically the preprocessing phase occurs before syntactic or semantic analysis; e.g. in
the case of C, the preprocessor manipulates lexical tokens rather than syntactic forms. However, some languages
such as Scheme support macro substitutions based on syntactic forms.
4. Syntax analysis involves parsing the token sequence to identify the syntactic structure of the program. This phase
typically builds a parse tree, which replaces the linear sequence of tokens with a tree structure built according to
the rules of a formal grammar which define the language's syntax. The parse tree is often analyzed, augmented,
and transformed by later phases in the compiler.
5. Semantic analysis is the phase in which the compiler adds semantic information to the parse tree and builds the
symbol table. This phase performs semantic checks such as type checking (checking for type errors), or object
binding (associating variable and function references with their definitions), or definite assignment (requiring all
local variables to be initialized before use), rejecting incorrect programs or issuing warnings. Semantic analysis
usually requires a complete parse tree, meaning that this phase logically follows the parsing phase, and logically
precedes the code generation phase, though it is often possible to fold multiple phases into one pass over the code
in a compiler implementation.
Back end
The term back end is sometimes confused with code generator because of the overlapped functionality of generating
assembly code. Some literature uses middle end to distinguish the generic analysis and optimization phases in the
back end from the machine-dependent code generators.
The main phases of the back end include the following:
1. Analysis: This is the gathering of program information from the intermediate representation derived from the
input. Typical analyses are data flow analysis to build use-define chains, dependence analysis, alias analysis,
pointer analysis, escape analysis etc. Accurate analysis is the basis for any compiler optimization. The call graph
and control flow graph are usually also built during the analysis phase.
2. Optimization: the intermediate language representation is transformed into functionally equivalent but faster (or
smaller) forms. Popular optimizations are inline expansion, dead code elimination, constant propagation, loop
transformation, register allocation and even automatic parallelization.
3. Code generation: the transformed intermediate language is translated into the output language, usually the native
machine language of the system. This involves resource and storage decisions, such as deciding which variables
to fit into registers and memory and the selection and scheduling of appropriate machine instructions along with
their associated addressing modes (see also Sethi-Ullman algorithm).
Compiler analysis is the prerequisite for any compiler optimization, and they tightly work together. For example,
dependence analysis is crucial for loop transformation.
In addition, the scope of compiler analysis and optimizations vary greatly, from as small as a basic block to the
procedure/function level, or even over the whole program (interprocedural optimization). Obviously, a compiler can
potentially do a better job using a broader view. But that broad view is not free: large scope analysis and
optimizations are very costly in terms of compilation time and memory space; this is especially true for
interprocedural analysis and optimizations.
Interprocedural analysis and optimizations are common in modern commercial compilers from HP, IBM, SGI, Intel,
Microsoft, and Sun Microsystems. The open source GCC was criticized for a long time for lacking powerful
interprocedural optimizations, but it is changing in this respect. Another open source compiler with full analysis and
optimization infrastructure is Open64, which is used by many organizations for research and commercial purposes.
Compiler 8
Due to the extra time and space needed for compiler analysis and optimizations, some compilers skip them by
default. Users have to use compilation options to explicitly tell the compiler which optimizations should be enabled.
Compiler correctness
Compiler correctness is the branch of software engineering that deals with trying to show that a compiler behaves
according to its language specification. Techniques include developing the compiler using formal methods and using
rigorous testing (often called compiler validation) on an existing compiler.
Related techniques
Assembly language is a type of low-level language and a program that compiles it is more commonly known as an
assembler, with the inverse program known as a disassembler.
A program that translates from a low level language to a higher level one is a decompiler.
A program that translates between high-level languages is usually called a language translator, source to source
translator, language converter, or language rewriter. The last term is usually applied to translations that do not
involve a change of language.
Notes
[1] "IP: The World's First COBOL Compilers" (http:/ / www. interesting-people. org/ archives/ interesting-people/ 199706/ msg00011. html).
interesting-people.org. 12 June 1997. .
[2] T. Hart and M. Levin. "The New Compiler, AIM-39 - CSAIL Digital Archive - Artificial Intelligence Laboratory Series" (ftp:/ / publications.
ai. mit. edu/ ai-publications/ pdf/ AIM-039. pdf). publications.ai.mit.edu. .
[3] "The PL/0 compiler/interpreter" (http:/ / www. 246. dk/ pl0. html). .
[4] "The ACM Digital Library" (http:/ / www. acm. org/ classics/ dec95/ ). .
[5] T diagrams were first introduced for describing bootstrapping and cross-compiling compilers in McKeeman et al. A Compiler Generator
(1971). Conway described the broader concept before that with his UNCOL in 1958, to which Bratman added in 1961: H. Bratman, “An
alternate form of the ´UNCOL diagram´“, Comm. ACM 4 (March 1961) 3, p. 142. Later on, others, including P.D. Terry, gave an explanation
and usage of T-diagrams in their textbooks on the topic of compiler construction. Cf. Terry, 1997, Chapter 3 (http:/ / scifac. ru. ac. za/
compilers/ cha03g. htm). T-diagrams are also now used to describe client-server interconnectivity on the World Wide Web: cf. Patrick
Closhen, et al. 1997: T-Diagrams as Visual Language to Illustrate WWW Technology (http:/ / pu. rbg. informatik. tu-darmstadt. de/ docs/
HJH-19990217-etal-T-diagrams. doc), Darmstadt University of Technology, Darmstadt, Germany
[6] ETAPS (http:/ / www. etaps. org/ ) - European Joint Conferences on Theory and Practice of Software. Cf. "CC" (Compiler Construction)
subsection.
References
• Compiler textbook references (http://www.informatik.uni-trier.de/~ley/db/books/compiler/index.html) A
collection of references to mainstream Compiler Construction Textbooks
• Aho, Alfred V.; Sethi, Ravi; and Ullman, Jeffrey D., Compilers: Principles, Techniques and Tools (ISBN
0-201-10088-6) link to publisher (http://www.aw.com/catalog/academic/product/0,4096,0201100886,00.
html). Also known as “The Dragon Book.”
• Allen, Frances E., "A History of Language Processor Technology in IBM" (http://www.research.ibm.com/
journal/rd/255/ibmrd2505Q.pdf), IBM Journal of Research and Development, v.25, no.5, September 1981.
Compiler 9
• Allen, Randy; and Kennedy, Ken, Optimizing Compilers for Modern Architectures, Morgan Kaufmann
Publishers, 2001. ISBN 1-55860-286-0
• Appel, Andrew Wilson
• Modern Compiler Implementation in Java, 2nd edition. Cambridge University Press, 2002. ISBN
0-521-82060-X
• Modern Compiler Implementation in ML (http://books.google.com/books?id=8APOYafUt-oC&
printsec=frontcover), Cambridge University Press, 1998. ISBN 0-521-58274-1
• Bornat, Richard, Understanding and Writing Compilers: A Do It Yourself Guide (http://www.cs.mdx.ac.uk/
staffpages/r_bornat/books/compiling.pdf), Macmillan Publishing, 1979. ISBN 0-333-21732-2
• Cooper, Keith D., and Torczon, Linda, Engineering a Compiler, Morgan Kaufmann, 2004, ISBN 1-55860-699-8.
• Leverett; Cattel; Hobbs; Newcomer; Reiner; Schatz; Wulf, An Overview of the Production Quality
Compiler-Compiler Project, in Computer 13(8):38-49 (August 1980)
• McKeeman, William Marshall; Horning, James J.; Wortman, David B., A Compiler Generator (http://www.cs.
toronto.edu/XPL/), Englewood Cliffs, N.J. : Prentice-Hall, 1970. ISBN 0-13-155077-2
• Muchnick, Steven, Advanced Compiler Design and Implementation (http://books.google.com/
books?id=Pq7pHwG1_OkC&printsec=frontcover&source=gbs_summary_r&cad=0), Morgan Kaufmann
Publishers, 1997. ISBN 1-55860-320-4
• Scott, Michael Lee, Programming Language Pragmatics (http://books.google.com/
books?id=4LMtA2wOsPcC&printsec=frontcover&dq=Programming+Language+Pragmatics), Morgan
Kaufmann, 2005, 2nd edition, 912 pages. ISBN 0-12-633951-1 ( The author's site on this book (http://www.cs.
rochester.edu/~scott/pragmatics/)).
• Srikant, Y. N.; Shankar, Priti, The Compiler Design Handbook: Optimizations and Machine Code Generation
(http://books.google.com/books?id=0K_jIsgyNpoC&printsec=frontcover), CRC Press, 2003. ISBN
0-8493-1240-X
• Terry, Patrick D., Compilers and Compiler Generators: An Introduction with C++ (http://scifac.ru.ac.za/
compilers/conts.htm), International Thomson Computer Press, 1997. ISBN 1-85032-298-8,
• Wirth, Niklaus, Construction (http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.70.3774&
rep=rep1&type=pdf''Compiler) (ISBN 0-201-40353-6), Addison-Wesley, 1996, 176 pages. Revised November
2005.
External links
• The comp.compilers newsgroup and RSS feed (http://compilers.iecc.com/)
• Hardware compilation mailing list (http://www.jiscmail.ac.uk/lists/hwcomp.html)
• Practical introduction to compiler construction using flex and yacc (http://www.onyxbits.de/content/blog/
patrick/introduction-compiler-construction-using-flex-and-yacc)
• Book " Basics of Compiler Design (http://www.diku.dk/hjemmesider/ansatte/torbenm/Basics/)" by Torben
Ægidius Mogensen
Interpreter 10
Interpreter
In computer science, an interpreter normally means a computer program that executes, i.e. performs, instructions
written in a programming language. An interpreter may be a program that either
1. executes the source code directly
2. translates source code into some efficient intermediate representation (code) and immediately executes this
3. explicitly executes stored precompiled code[1] made by a compiler which is part of the interpreter system
Perl, Python, MATLAB, and Ruby are examples of type 2, while UCSD Pascal is type 3: Source programs are
compiled ahead of time and stored as machine independent code, which is then linked at run-time and executed by
an interpreter and/or compiler (for JIT systems). Some systems, such as Smalltalk, BASIC, Java and others, may also
combine 2 and 3.
While interpreting and compiling are the two main means by which programming languages are implemented, these
are not fully distinct categories, one of the reasons being that most interpreting systems also perform some
translation work, just like compilers. The terms "interpreted language" or "compiled language" merely mean that the
canonical implementation of that language is an interpreter or a compiler; a high level language is basically an
abstraction which is (ideally) independent of particular implementations.
Bytecode interpreters
There is a spectrum of possibilities between interpreting and compiling, depending on the amount of analysis
performed before the program is executed. For example, Emacs Lisp is compiled to bytecode, which is a highly
compressed and optimized representation of the Lisp source, but is not machine code (and therefore not tied to any
particular hardware). This "compiled" code is then interpreted by a bytecode interpreter (itself written in C). The
compiled code in this case is machine code for a virtual machine, which is implemented not in hardware, but in the
bytecode interpreter. The same approach is used with the Forth code used in Open Firmware systems: the source
language is compiled into "F code" (a bytecode), which is then interpreted by a virtual machine.
Control tables - that do not necessarily ever need to pass through a compiling phase - dictate appropriate algorithmic
control flow via customized interpreters in similar fashion to bytecode interpreters.
Efficiency
The main disadvantage of interpreters is that when a program is interpreted, it typically runs more slowly than if it
had been compiled. The difference in speeds could be tiny or great; often an order of magnitude and sometimes
more. It generally takes longer to run a program under an interpreter than to run the compiled code but it can take
less time to interpret it than the total time required to compile and run it. This is especially important when
prototyping and testing code when an edit-interpret-debug cycle can often be much shorter than an
edit-compile-run-debug cycle.
Interpreting code is slower than running the compiled code because the interpreter must analyze each statement in
the program each time it is executed and then perform the desired action, whereas the compiled code just performs
the action within a fixed context determined by the compilation. This run-time analysis is known as "interpretive
overhead". Access to variables is also slower in an interpreter because the mapping of identifiers to storage locations
must be done repeatedly at run-time rather than at compile time.
There are various compromises between the development speed when using an interpreter and the execution speed
when using a compiler. Some systems (such as some Lisps) allow interpreted and compiled code to call each other
and to share variables. This means that once a routine has been tested and debugged under the interpreter it can be
compiled and thus benefit from faster execution while other routines are being developed. Many interpreters do not
Interpreter 11
execute the source code as it stands but convert it into some more compact internal form. Many BASIC interpreters
replace keywords with single byte tokens which can be used to find the instruction in a jump table. Others achieve
even higher levels of program compaction by using a bit-orientated rather than a byte-orientated program memory
structure, where commands tokens may occupy perhaps 5 bits of a 16 bit word, leaving 11 bits for their label or
address operands.
An interpreter might well use the same lexical analyzer and parser as the compiler and then interpret the resulting
abstract syntax tree.
Development cycle
During program development, the programmer makes frequent changes to source code. With a compiler, each time
the programmer makes a change to the source code, s/he must wait for the compiler to make a compilation of the
altered source files, and link all of the binary code files together before the program can be executed. The larger the
program, the longer the programmer must wait to see the results of the change. By contrast, a programmer using an
interpreter does a lot less waiting, as the interpreter usually just needs to translate the code being worked on to an
intermediate representation (or not translate it at all), thus requiring much less time before the changes can be tested.
Distribution
A compiler converts source code into binary instruction for a specific processor's architecture, thus making it less
portable. This conversion is made just once, on the developer's environment, and after that the same binary can be
distributed to the user's machines where it can be executed without further translation. A cross compiler can generate
binary code for the user machine even if it has a different processor than the machine where the code is compiled.
An interpreted program can be distributed as source code. It needs to be translated in each final machine, which takes
more time but makes the program distribution independent to the machine's architecture.
Execution environment
An interpreter makes source translations during runtime, meaning that every line of code must be converted to
machine --- code each time the program runs. This process slows down the program's execution, something that does
not occur for compiled programs. This slowness puts interpreters at a major disadvantage when compared with
compilers. Another key disadvantage of an interpreter is that it must be present on the user's machine, (as additional
software) to run the program.
However, for interpreters, an AST causes more overhead than a bytecode interpreter, because of nodes related to
syntax performing no useful work, of a less sequential representation (requiring traversal of more pointers) and of
overhead visiting the tree.[4]
Just-in-time compilation
Further blurring the distinction between interpreters, byte-code interpreters and compilation is just-in-time
compilation (or JIT), a technique in which the intermediate representation is compiled to native machine code at
runtime. This confers the efficiency of running native code, at the cost of startup time and increased memory use
when the bytecode or AST is first compiled. Adaptive optimization is a complementary technique in which the
interpreter profiles the running program and compiles its most frequently-executed parts into native code. Both
techniques are a few decades old, appearing in languages such as Smalltalk in the 1980s.[5]
Just-in-time compilation has gained mainstream attention amongst language implementers in recent years, with Java,
the .NET Framework and most modern JavaScript implementations now including JITs.