racket/collects/scribblings/raco/zo-parse.scrbl
Matthew Flatt a4da2a3f4c fix varref' in compiler/zo-structs', etc.
and sync docs better with implementation
2011-05-09 09:43:32 -06:00

78 lines
3.9 KiB
Racket

#lang scribble/doc
@(require scribble/manual
(for-label racket/base
racket/contract
compiler/zo-parse
racket/set))
@(define-syntax-rule (defstruct+ id fields . rest)
(defstruct id fields #:prefab . rest))
@title{API for Parsing Bytecode}
@defmodule[compiler/zo-parse]
The @racketmodname[compiler/zo-parse] module re-exports
@racketmodname[compiler/zo-structs] in addition to
@racket[zo-parse].
@defproc[(zo-parse [in input-port? (current-input-port)]) compilation-top?]{
Parses a port (typically the result of opening a @filepath{.zo} file)
containing bytecode. Beware that the structure types used to
represent the bytecode are subject to frequent changes across Racket
versons.
The parsed bytecode is returned in a @racket[compilation-top]
structure. For a compiled module, the @racket[compilation-top]
structure will contain a @racket[mod] structure. For a top-level
sequence, it will normally contain a @racket[seq] or @racket[splice]
structure with a list of top-level declarations and expressions.
The bytecode representation of an expression is closer to an
S-expression than a traditional, flat control string. For example, an
@racket[if] form is represented by a @racket[branch] structure that
has three fields: a test expression, a ``then'' expression, and an
``else'' expression. Similarly, a function call is represented by an
@racket[application] structure that has a list of argument
expressions.
Storage for local variables or intermediate values (such as the
arguments for a function call) is explicitly specified in terms of a
stack. For example, execution of an @racket[application] structure
reserves space on the stack for each argument result. Similarly, when
a @racket[let-one] structure (for a simple @racket[let]) is executed,
the value obtained by evaluating the right-hand side expression is
pushed onto the stack, and then the body is evaluated. Local
variables are always accessed as offsets from the current stack
position. When a function is called, its arguments are passed on the
stack. A closure is created by transferring values from the stack to
a flat closure record, and when a closure is applied, the saved values
are restored on the stack (though possibly in a different order and
likely in a more compact layout than when they were captured).
When a sub-expression produces a value, then the stack pointer is
restored to its location from before evaluating the sub-expression.
For example, evaluating the right-hand size for a @racket[let-one]
structure may temporarily push values onto the stack, but the stack is
restored to its pre-@racket[let-one] position before pushing the
resulting value and continuing with the body. In addition, a tail
call resets the stack pointer to the position that follows the
enclosing function's arguments, and then the tail call continues by
pushing onto the stack the arguments for the tail-called function.
Values for global and module-level variables are not put directly on
the stack, but instead stored in ``buckets,'' and an array of
accessible buckets is kept on the stack. When a closure body needs to
access a global variable, the closure captures and later restores the
bucket array in the same way that it captured and restores a local
variable. Mutable local variables are boxed similarly to global
variables, but individual boxes are referenced from the stack and
closures.
Quoted syntax (in the sense of @racket[quote-syntax]) is treated like
a global variable, because it must be instantiated for an appropriate
phase. A @racket[prefix] structure within a @racket[compilation-top]
or @racket[mod] structure indicates the list of global variables and
quoted syntax that need to be instantiated (and put into an array on
the stack) before evaluating expressions that might use them.}