Pascal
For example in Pascal variant records and points are used:
TYPE list = ^ node;
node= RECORD {...}
CASE type:(nil, atom, pair) IN
nil: ();
atom: (^atoms);
pair:( car, cdr: list ); {...}
END {node};
atoms=RECORD
image:PACKED ARRAY [1..] OF char; {...}
CASE type:(number, string, word) IN
string:(...);
word:(...)
number:(...);
END {atoms};
enum node_type{nil, atom, pair};
enum atom_type{number, string, word /*,...*/ };
struct Pair {Node *car, *cdr}pair;
struct Atom;//defined below.
struct Pair;//defined below.
union Data {
Atom *atom;
Pair pair;
};//either an atom or a pair
struct Node{
/* various useful things*/
enum node_type type;
Data d;
Node(node_type t, Data d0){type=t;d=d0;}
};
typedef struct Node * list;
struct Atom{
char *image;
enum atom_type type;
union atomic_data{ char *string;
char *word;
int number;
/*...*/
}d;
/*...*/
};
Parsing input lists
I will use the constructors for Atom, Data, and Node to try and
explain how Lists are handled.
There is a map that parses and stores the output from the lexical stage. Call
this store then for instance, store("(" car a ")") is the address an object
In general store works like this: store("()")::= address(Node(nil)),
The interpreter reinterprets lists when it evaluates them according to the semantics of S_expressions.
. . . . . . . . . ( end of section Internal Representation) <<Contents | End>>
Variables
At any time the LISP interpreter binds values to some atoms. At any time there
are sets of variables and function names:
Expressions
The meaning of input to a LISP interpreter is based on the value returned
when the expression is evaluated.
The evaluation of an expression depends on the meanings currently bound to the function names and variables:
These bindings can be changed as the side-effect of obeying the LISP function EVAL. Notice that "EVAL" is the LISP function that implements a function we can call 'eval':
For x, we write eval(x) for the value returned by EVAL when string x is input:
Constants avoid most evaluation - numbers are converted to binary and atoms are capitalised,...
For n:number, eval(n) = value of n as a decimal number,
For S:#non(quote), eval( quote S quote )= Internal string encoding S.
A mapping is very like a function in C/C++/Pascal/Ada except it has not been given an identifier. Instead the whole Lambda expression is the name of the function: In place of short name f in
f(a){return e;}we hav a description of 'f' and no 'f'
(LAMBDA (a) e){ return e; }However.... DEFINE and DEFUN do give names to functions, when we want to.
Inside the S_expression, the atoms (if any) are new variables which are bound to a value when the lambda_form is applied as a function.
So for example
Net
Some functions have predefined mappings (here I've used the C/C++ "->" operator):
Using Ada record notation for CONS we get:
Conditional expressions
"COND" is more complex. "COND" stands for conditional and requires a
sequence of pairs as its arguments:
"(" "COND"
"("c1 e1")"
"("c2 e2")"
"("c3 e3")"
...
"(" c[n] e[n] ")"
")"
The exact syntax for defining a new function varies for system to system. For example XLisp uses:
"(" DEFUN a "(" p ")" e ")"The effect is to add atom to the set of function_names and bind it to the mapping defined by
"(" "LAMBDA" "(" p ")" e ")".
Or in C/C++
List a( p){return e;}
Others use a general DEFINE function for binding atoms to meanings:
"(" "DEFINE" (form) expression")"where form is typically like this
"(" name arguments ")"
Another variation is
"(" "DEFINE" name expression ")"
Scheme LISP (and our version of XLISP) has a macro with these syntaxes
"(" "DEFINE" "("function arguments")" expression ")"
"(" "DEFINE" function lambda_expression ")"
"(" "DEFINE" "("function arguments")" "( EVAL" expression ")" ")"The last form implies that the body of the function is itself the value of the expression.... not the expression itself. (read this twice).
Note
Scheme uses static binding and LISP use dynamic binding, but our XLISP
manages to have both! Our DEFINE substitutes the function for the
call and so uses dynamic binding, but a function defined with DEFUN
is a closure that encapsulates the environment of the DEFUN and this
is used when the functions is called.
In pure LISP variables are actual formal parameters that are bound to a value when functions are called and freed when the function returns a result.
Most LISPS provide
"(" "SETQ" atom S_expression ")"As a function with side effects.
"(" "SETQ" a e ")" = "(" "SET" "(" "QUOTE" a ")" e ")"where
"(" SET e1 e2 ")"evaluates both e1 and e2 (using eval) and if eval(e1) is an atom will bind eval(e2) as the value of eval(e2).
SETQ
(SETQ variable espression)::=following
Net
List SETQ( List & variable, Expression expression)
{ List v=value(expression);
bind(variable, v);
return v;
}
List LIST( ... )
{ List arg, val, result;
bind(result, NIL);
for(each arg in turn)
{
val=value(arg);
append(v, result);//put val at end of result
}
return result;
}
(NORMALFUNCTION e1 e2 e3 e4 ...)::=following
Net
. . . . . . . . . ( end of section Semantics) <<Contents | End>>