Next: compiler topics clambda, Previous: compiler topics topbind, Up: compiler topics [Index]
Lexical local bindings are defined in the core language by the forms:
let
, letrec
, letrec*
, lambda
,
case-lambda
, annotated-case-lambda
. These forms have
syntax:
(let ((?lex ?rhs) ...) ?body) (letrec ((?lex ?rhs) ...) ?body) (letrec* ((?lex ?rhs) ...) ?body) (case-lambda (?formals ?body) ...) (annotated-case-lambda ?annotation (?formals ?body) ...) (lambda ?formals ?body)
in which: ?lex is a lexical gensym that uniquely identifies the binding in the core language form; ?rhs is the initialisation expression; ?formals is a proper or improper list of lex gensyms uniquely identifying arguments to function.
References to local bindings are represented by standalone ?lex
gensyms; assignments to local bindings are represented by set!
forms:
(set! ?lex ?rhs)
where the right–hand side expression ?rhs will, at run–time, evaluate to the new binding’s value.
The let
, letrec
and letrec*
forms are recordised
into nested hierarchies of bind
, recbind
and
rec*bind
structs:
(bind ((?prel ?rhs) ...) ?body) (recbind ((?prel ?rhs) ...) ?body) (rec*bind ((?prel ?rhs) ...) ?body)
in which ?prel is a prelex
struct holding the ?lex
gensym.
The lambda
, case-lambda
and annotated-case-lambda
forms are recordised as clambda
structs:
(clambda (?prel-formals ?body) ...)
in which ?prel-formals is a a proper or improper list of
prel
structs representing arguments to each clambda
clause.
References to local bindings are represented by standalone
prelex
structs; assignments to local bindings are represented
by assign
structs:
(assign ?prel ?rhs)
We distinguish between bindings that are only referenced (unassigned,
read–only) and bindings that are also assigned (assigned, read–write).
Example of code in which the binding X
is only referenced:
(let ((X 123)) (display X))
example of code in which the binding X
is assigned and
referenced:
(let ((X 123)) (set! X 456) (display X))
The implementation technique for continuations used by Vicare mandates that: once an immediate Scheme object or reference to non–immediate Scheme object is stored in a machine word on the Scheme stack, such machine word must not be mutated; this allows the run–time system to copy stack frames at will. As a consequence the following implementation techniques for local bindings are adopted:
References to unassigned bindings must be implemented as Scheme stack operations; references and assignments to assigned bindings must be substituted with appropriate vector operations.
After the letrec
optimiser has processed recbind
and
rec*bind
forms, unassigned local bindings always end up
defined as:
(bind ((?prel ?rhs)) ?body)
and references are represented by standalone prelex
structs.
Whenever a ?rhs expression is recognised to be of type
clambda
, the corresponding bind
is transformed into:
(fix ((?prel ?rhs)) ?body)
No further transformations are needed for recordised code.
After the letrec
optimiser has processed recbind
and
rec*bind
forms, assigned local bindings always end up defined
as:
(bind ((?prel ?rhs)) ?body)
references are represented by standalone prelex
structs and
assignments are represented by assign
structs.
NOTE No matter if a ?rhs expression is of type
clambda
: its associatedbind
struct is not transformed into afix
struct; only unassignedclambda
bindings can be defined by afix
struct.
Definitions, references and assignments are transformed by introducing the appropriate vector operations; such operation are equivalent to the following:
(let ((x 123)) (set! x 456) x)
is transformed into:
(let ((t 123)) (let ((x (vector t))) ($vector-set! x 0 456) ($vector-ref x 0)))
where the temporary binding t
is generated by the compiler; the
code always only access the first and single slot of the vector. Using
recordised code:
(bind ((x_0 123)) (seq (assign x_0 456) x))
is transformed into:
(bind ((x_0 123)) (bind ((x_1 (funcall (primref vector) x_0))) (seq (funcall (primref $vector-set!) x_1 (constant 0) (constant 456)) (funcall (primref $vector-ref) x_1 (constant 0)))))
Formal bindings in clambda
structs are also processed this
way; for example:
(lambda (a) (display a) (set! a 1) a)
is transformed into:
(lambda (a_0) (bind ((a_1 (funcall (primref vector) a_0))) (seq (funcall (primref display) (funcall (primref $vector-ref) a_1 (constant 0))) (funcall (primref $vector-set!) a_1 (constant 0) (constant 1)) (funcall (primref $vector-ref) a_1 (constant 0)))))
NOTE Assigned local bindings whose RHS expression is a
clambda
struct are also transformed by introducing a vector. After this transformation: there are no morebind
struct whose RHS is aclambda
struct.
Next: compiler topics clambda, Previous: compiler topics topbind, Up: compiler topics [Index]