A self-interpreter is a programming language interpreter written in the language it interprets. Interpreters are written in some other existing language and then later converted to the language they interpret. In these cases the early mock-ups can be used to develop the source code of the interpreter. Once the system is bootstrapped , new versions of the interpreter can be developed in the language itself. There are many ways of bootstrapping ; such as 1) implementing an interpreter or compiler for language X in language Y, 2) another interpreter or compiler for X has already been written in another language Y, 3) earlier versions of the compiler were written in a subset of X for which there existed some other compile, 4) the compiler for X is cross compiled from another architecture where there exists a compiler for X, 5) writing the compiler in X; then hand-compiling it from source (most likely in a non-optimized way) and running that on the code to get an optimized compiler. Donald Knuth used this for his WEB literate programming system. (WEB by the way again proves the very close approximation of text or document processing and programming tools , WEB is based on the idea that one could create software as works of literature , by embedding source code inside descriptive text, rather than the reverse as is common practice in most programming languages. It consists of two primary programs: TANGLE, which produces compilable Pascal code from the source texts, and WEAVE, which produces nicely-formatted, printable documentation using TeX .)

A meta-circular interpreter is a special case of a self-interpreter in which the existing facilities of the parent interpreter are directly applied to the source code being interpreted, without any need for additional parsing. Meta-circular evaluation is only possible in homoiconic languages.

In computer programming, homoiconicity is a property of some programming languages , in which the primary representation of programs is also a data structure primitive type of the language itself, from homo meaning the same and icon meaning representation.

Lisp is one of family of homoiconic programming languages.

Again from a Wikipedia article I found an evidence in case for text handling (Document Processing) being the major motivator of ideas in compilation and interpretation. In the paper Macro Instruction Extensions of Compiler Languages, and according to the early and influential paper the TRAC, A Text-Handling Language , the main designing goal of the system was that the input script of TRAC (what is typed in by the user) should be identical to the text which guides the internal action of the TRAC processor… Lisp is "homoiconic" in that its internal and external representations are essentially the same.

Consider the following meta circular interpreter of Lisp in Lisp, taken from paul graham’s site

———————————————————————————————————————-

; The Lisp defined in McCarthy’s 1960 paper, translated into CL.

; Assumes only quote, atom, eq, cons, car, cdr, cond.

; Bug reports to lispcode@paulgraham.com.

(defun null. (x)

(eq x ‘()))

(defun and. (x y)

(cond (x (cond (y ‘t) (‘t ‘())))

(‘t ‘())))



(defun not. (x)

(cond (x ‘())

(‘t ‘t)))

(defun append. (x y)

(cond ((null. x) y)

(‘t (cons (car x) (append. (cdr x) y)))))



(defun list. (x y)

(cons x (cons y ‘())))



(defun pair. (x y)

(cond ((and. (null. x) (null. y)) ‘())

((and. (not. (atom x)) (not. (atom y)))

(cons (list. (car x) (car y))

(pair. (cdr x) (cdr y))))))

(defun assoc. (x y)

(cond ((eq (caar y) x) (cadar y))

(‘t (assoc. x (cdr y)))))

(defun eval. (e a)

(cond

((atom e) (assoc. e a))

((atom (car e))

(cond

((eq (car e) ‘quote) (cadr e))

((eq (car e) ‘atom) (atom (eval. (cadr e) a)))

((eq (car e) ‘eq) (eq (eval. (cadr e) a)

(eval. (caddr e) a)))

((eq (car e) ‘car) (car (eval. (cadr e) a)))

((eq (car e) ‘cdr) (cdr (eval. (cadr e) a)))

((eq (car e) ‘cons) (cons (eval. (cadr e) a)

(eval. (caddr e) a)))

((eq (car e) ‘cond) (evcon. (cdr e) a))

(‘t (eval. (cons (assoc. (car e) a)

(cdr e))

a))))

((eq (caar e) ‘label)

(eval. (cons (caddar e) (cdr e))

(cons (list. (cadar e) (car e)) a)))

((eq (caar e) ‘lambda)

(eval. (caddar e)

(append. (pair. (cadar e) (evlis. (cdr e) a))

a)))))



(defun evcon. (c a)

(cond ((eval. (caar c) a)

(eval. (cadar c) a))

(‘t (evcon. (cdr c) a))))

(defun evlis. (m a)

(cond ((null. m) ‘())

(‘t (cons (eval. (car m) a)

(evlis. (cdr m) a)))))

———————————————————————————————————————-

One advantage of homoiconicity is that extending the language with new concepts typically becomes simpler, as data representing code can be passed between the meta and base layer of the program. This is how CLOS ( Common Lisp Object System) and MOP came into being.

A metaobject protocol (MOP) is an interpreter of the semantics of a program that is open and extensible. Therefore, a MOP determines what a program means and what its behavior is, and it is extensible in that a programmer can alter program behavior by extending parts of the MOP. The MOP exposes some or all internal structure of the interpreter to the programmer. The MOP may manifest as a set of classes and methods that allow a program to inspect the state of the supporting system and alter its behaviour . MOPs are implemented as object-oriented programs where all objects are metaobjects.

MOPs may be runtime or compile time . The metaobjects of runtime MOPs exist while the program itself is executed. The metaobjects of compile-time MOPs, however, exist only when the program is compiled. They may alter or extend the compiling process, but do not exist when the program is running.

One of the the best-known runtime MOPs is the one described in the book The Art of the Metaobject Protocol; it applies to the ( CLOS ) and allows the mechanisms of inheritance , method dispatch , class instantiation and so on to be manipulated . CLOS features multiple inheritance, multiple dispatch ("multimethods"), and a powerful system of "method combinations".

Consider the following features of MOP in pseudo-Java syntax as explained here .

before and after methods

CLOS provides a feature called method combination. For every method it is possible to define a method that is executed immediately before the primary method is executed (called the method’s before-method), and a method that is executed after the primary method (the after-method). These methods can also be defined in subclasses. An example is given in listing 5.

LISTING 5

——————————————————————————-

01 class Shape {

02 public void paint() {

03 // paint shape

04 }

05 }

06

07 class ColoredShape extends Shape {

08 public before void paint() {

09 // set brush color

10 }

11 }

12

13 class Test {

14 public void run() {

15 Shape shape = new Shape();

16 shape.paint();

17 Shape shape2 = new ColoredShape();

18 shape2.paint();

19 }

20 }

In line 16 the Test class calls the paint() method of a Shape object. The effect is, that Shape.paint() is called. In line 18, when shape2.paint() is called, the before method of the ColoredShape class is invoked before the superclass’s paint() method is called. It is interesting to see that an after- method does not influence the primary method’s return value. If a class has multiple levels of parents, the invocation semantics of a method are as follows:

execute all before-methods of the class and of all parent classes

execute the primary method

execute all after-methods of the class and of all parent classes

Because of the given execution order, before-methods are often used to do error checking (precondition validation) or to do preparative work, and after- methods can be used to clear up the environment (and to ensure postconditions). AOP introduces the very same features: They are called before advises and after advises.

The connection to AOP\ FOP is somewhat evident. From Raganwald’s entry , “ While meta-programming, you are working on two tiers either simultaneously or alternately: you work on expressing your solution in your solution language, and you work on the implementation (whether that be fun stuff like making it expressive or plumbing stuff like making it fast enough to be practical) in your implementation language.”

And yes, eclipse does something similar even though java has no self interpreter, by manipulating the AST. But in LISP the program is the AST. How simply and in far fetching manner this fact could be exploited we know from Genetic Programming.