Next: Pre-Scheme, Up: (dir) [Contents]
This manual is for Scheme48 version 1.3.
Copyright © 2004, 2005, 2006 Taylor Campbell. All rights reserved.
This manual includes material derived from works bearing the following notice:
Copyright © 1993–2005 Richard Kelsey, Jonathan Rees, and Mike Sperber. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- The name of the authors may not be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
• Pre-Scheme: | A low-level dialect of Scheme | |
Pre-Scheme [Kelsey 97] is a low-level dialect of Scheme, designed for systems programming with higher-level abstractions. For example, the Scheme48 virtual machine is written in Pre-Scheme. Pre-Scheme is a particularly interesting alternative to C for many systems programming tasks, because not only does it operate at about the same level as C, but it also may be run in a regular high-level Scheme development with no changes to the source, without resorting to low-level stack munging with tools such as gdb. Pre-Scheme also supports two extremely important high-level abstractions of Scheme: macros and higher-order, anonymous functions. Richard Kelsey’s Pre-Scheme compiler, based on his PhD research on transformational compilation [Kelsey 89], compiles Pre-Scheme to efficient C, applying numerous intermediate source transformations in the process.
This chapter describes details of the differences between Scheme and Pre-Scheme, listings of the default environment and other packages available to Pre-Scheme, the operation of Richard Kelsey’s Pre-Scheme compiler, and how to run Pre-Scheme code as if it were Scheme in a regular Scheme environment.
Next: Pre-Scheme type specifiers, Up: Pre-Scheme [Contents]
Pre-Scheme is often considered either a dialect of Scheme or a subset of Scheme. However, there are several very important fundamental differences between the semantics of Pre-Scheme & Scheme to detail.
All memory management is manual, as in C, although there are two levels to memory management, for higher- and lower-level purposes: pointers & addresses. Pointers represent higher-level data that are statically checked for type coherency, such as vectors of a certain element type, or strings. Addresses represent direct, low-level memory indices.
Lambda
expressions that would require full closures at run-time
— e.g., those whose values are stored in the heap — are not
permitted in Pre-Scheme. However, the Pre-Scheme compiler can hoist
many lambda
expressions to the top level, removing the need of
closures for them. (Closures would be much less useful in the absence
of garbage collection, in any case.) If the Pre-Scheme compiler is
unable to move a lambda
to a place where it requires no closure,
it signals an error to the user.
The Pre-Scheme compiler optimizes tail calls where it is possible —
typically, just in local loops and top-level procedures that are not
exported from the package, but there are other heuristics —, but it
is not universal. Programmers may force tail call optimization with
Pre-Scheme’s goto
special form (see Tail call optimization in Pre-Scheme), but, in situations where the compiler would not have
optimized the tail call, this can make the generated code have to jump
through many hoops to be a tail call — often necessitating code bloat,
because the code of the tail-called procedure is integrated into the
caller’s driver loop —; and, where the compiler would have otherwise
optimized the tail call, goto
has no effect anyway.
The types of Pre-Scheme programs are statically verified based on Hindley-Milner type inference, with some modifications specific to Pre-Scheme. Type information is not retained at run-time; any tagging must be performed explicitly.
There is no call-with-current-continuation
or other continuation
manipulation interface. It has been suggested that downward-only
continuations, based on C’s setjmp
& longjmp
, might be
implemented in the future, but this is not yet the case.1
Pre-Scheme’s only numeric types are fixnums and flonums, with precision
determined by the architecture on which the Pre-Scheme code runs.
Fixnums are translated to C as the long
type; flonums are
translated as the float
type.
Closures actually are available, as long as they may be
eliminated before run-time. Code evaluated at compile-time also does
not require satisfaction of strict static typing. Moreover, certain
procedures, such as vector-length
, are available only at
compile-time.
Next: Standard Pre-Scheme environment, Previous: Differences between Pre-Scheme & Scheme, Up: Pre-Scheme [Contents]
Although Pre-Scheme’s static type system is based mostly on Hindley-Milner type inference, with as little explicit type information as possible, there are still places where it is necessary to specify types explicitly; for example, see Pre-Scheme access to C functions and macros. There are several different kinds of types with different syntax:
type-name
Symbols denote either record type or base types. Record types are
defined with the define-record-type
special form described
later; the following base types are defined:
integer
Fixed-size integers (fixnums). This type is translated into C as
long
. The actual size depends on the size of C’s long
,
which on most architectures is 32 bits.
float
Floating-point data. This type translates to C as double
.
null
Type which has no value. The null
type translates to the C
void
type.
unit
Type which has one value. Actually, this, too, translates to C’s
void
, so that it has one value is not strictly true.
boolean
Booleans translate to the C char
type. #t
is emitted as
TRUE
, and #f
, as FALSE
; these are usually the same
as 1
& 0
, respectively.
input-port
output-port
I/O ports. On Unix, since Pre-Scheme uses stdio
, these are
translated to FILE *
s, stdio
file streams.
char
Characters. The size of characters is dependent on the underlying C
compiler’s implementation of the char
type.
address
Simple addresses for use in Pre-Scheme’s low-level memory manipulation primitives; see that section for more details.
(=> (argument-type …) return-type …)
The types of procedures, known as ‘arrow’ types.
(^ type)
The type of pointers that point to type. Note that these are
distinct from the address type. Pointer types are statically verified
to be coherent data, with no defined operations except for accessing
offsets in memory from the pointer — i.e. operations such as
vector-ref
—; addresses simply index bytes, on which only
direct dereferencing, but also arbitrary address arithmetic, is
available. Pointers and addresses are not interchangeable, and
and there is no way to convert between them, as that would break the
type safety of Pre-Scheme pointers.
(tuple type …)
Multiple value types, internally used for argument & return types.
Next: More Pre-Scheme packages, Previous: Pre-Scheme type specifiers, Up: Pre-Scheme [Contents]
Pre-Scheme programs usually open the prescheme
structure. There
are several other structures built-in to Pre-Scheme as well, described
in the next section. This section describes the prescheme
structure.
Bindings for all the names specified here from R5RS Scheme are available in Pre-Scheme. The remainder of the sections after this one detail Pre-Scheme specifics that are not a part of Scheme.
These special forms & macros are all unchanged from their R5RS specifications.
Pre-Scheme’s macro facility is exactly the same as Scheme48’s.
Transformer-expression may be either a syntax-rules
or an
explicit renaming transformer, just as in Scheme48; in the latter case,
it is evaluated either in a standard Scheme environment or however the
for-syntax
clause specified of the package in whose code the
transformer appeared. For details on the extra aux-names operand
to define-syntax
, see ‘Explicit renaming macros’.
These procedures are all unchanged from their R5RS specifications.
These numerical operations are all unchanged from their R5RS counterparts, except that they are applicable only to fixnums, not to flonums, and they always return fixnums.
Next: Pre-Scheme bitwise manipulation, Previous: Scheme bindings in Pre-Scheme, Up: Standard Pre-Scheme environment [Contents]
The Pre-Scheme compiler can be forced to optimize tail calls, even
those it would not have otherwise optimized, by use of the goto
special form, rather than simple procedure calls. In every respect
other than tail call optimization, this is equivalent to calling
procedure with the given arguments. Note, however, that uses of
goto
may cause code to blow up if the Pre-Scheme compiler had
reason not to optimize the tail call were it not for the goto
:
it may need to merge the tail-called procedure into the caller’s code.
Next: Compound Pre-Scheme data manipulation, Previous: Tail call optimization in Pre-Scheme, Up: Standard Pre-Scheme environment [Contents]
Pre-Scheme provides basic bitwise manipulation operators.
Bitwise boolean logical operations.
Three ways to shift bit strings: shift-left
shifts integer
left by count, arithmetic-shift-right
shifts integer
right by count arithmetically, and logical-shift-right
shifts integer right by count logically.
Next: Pre-Scheme error handling, Previous: Pre-Scheme bitwise manipulation, Up: Standard Pre-Scheme environment [Contents]
Pre-Scheme has somewhat lower-level vector & string facilities than
Scheme, with more orientation towards static typing. It also provides
a statically typed record facility, which translates to C structs,
though not described here, as it is not in the prescheme
structure; see Pre-Scheme record types.
Vectors in Pre-Scheme are almost the same as vectors in regular Scheme,
but with a few differences. Make-vector
initializes what it
returns with null pointers (see below); it uses the required
(unlike Scheme) init argument only to determine the type of the
vector: vectors are statically typed; they can contain only values that
have the same static type as init. Vector-length
is
available only at the top level, where calls to it can be evaluated at
compile-time; vectors do not at run-time store their lengths. Vectors
must also be explicitly deallocated.
Warning: As in C, there is no vector bounds checking at run-time.
Strings in Pre-Scheme are the nearly same as strings in R5RS Scheme.
The only three differences here are that make-string
accepts
exactly one argument, strings must be explicitly deallocated, and
strings are nul
-terminated: string-length
operates by
scanning for the first ASCII nul
character in a string.
Warning: As in C, there is no string bounds checking at run-time.
Deallocates the memory pointed to by pointer
. This is necessary
at the end of a string, vector, or record’s life, as Pre-Scheme data
are not automatically garbage-collected.
Null-pointer
returns the distinguished null pointer object. It
corresponds with 0
in a pointer context or NULL
in C.
Null-pointer?
returns true if pointer is a null pointer,
or false if not.
Next: Input & output in Pre-Scheme, Previous: Compound Pre-Scheme data manipulation, Up: Standard Pre-Scheme environment [Contents]
Pre-Scheme’s method of error handling is similar to the most common one
in C: error codes. There is an enumeration errors
of some error
codes commonly and portably encountered in Pre-Scheme.
(define-enumeration errors (no-errors parse-error file-not-found out-of-memory invalid-port))
Each enumerand has the following meaning:
Absence of error: success.
Any kind of parsing error. The Scheme48 VM uses this when someone attempts to resume a malformed suspended heap image.
Used when an operation that operates on a file given a string filename found that the file for that filename was absent.
When there is no more memory to allocate.
Unused.
Returns a string describing the meaning of the errors
enumerand
error-status.
Signals a fatal error with the given message & related irritants and halts the program. On Unix, the program’s exit code is -1.
Next: Pre-Scheme access to C functions and macros, Previous: Pre-Scheme error handling, Up: Standard Pre-Scheme environment [Contents]
Pre-Scheme’s I/O facilities are somewhat different from Scheme’s, given
the low level and the static type strictness. There is no exception
mechanism in Pre-Scheme; everything is maintained by returning a status
token, as in C. Pre-Scheme’s built-in I/O facilities are buffered.
2
(see Low-level Pre-Scheme memory manipulation, for two other I/O
primitives, read-block
& write-block
, for reading &
writing blocks of direct memory.)
Open-input-file
& open-output-file
open ports for the
given filenames. They each return two values: the newly open port and
an errors
enumerand status. Users of these procedures should
always check the error status before proceeding to operate with the
port. Close-input-port
& close-output-port
close their
port arguments and return the errors
enumerand status of the
closing.
Read-char
reads & consumes a single character from its
input-port argument. Peek-char
reads, but does not
consume, a single character from input-port. Read-integer
parses an integer literal, including sign. All of these also return
two other values: whether or not the file is at the end and any
errors
enumerand status. If any error occurred, the first two
values returned should be ignored. If status is (enum
errors no-errors)
, users of these three procedures should then check
eof?; it is true if input-port was at the end of the file
with nothing more left to read and false otherwise. Finally, if both
status is (enum errors no-errors)
and eof? is false,
the first value returned may be safely used.
These all write particular elements to their output-port
arguments. Write-char
writes individual characters.
Newline
writes newlines (line-feed, or ASCII codepoint 10, on
Unix). Write-string
writes the contents of string.
Write-integer
writes an ASCII representation of integer to
port, suitable to be read by read-integer
. These all return an
errors
enumerand status. If it is no-errors
, the write
succeeded.
Forces all buffered output in output-port. Status tells whether or not the operation was successful.
Previous: Input & output in Pre-Scheme, Up: Standard Pre-Scheme environment [Contents]
Special form for accessing C functions & macros. Calls in Pre-Scheme to the resulting procedure are compiled to calls in C to the function or macro named by c-name, which should be a string. PS-type is the Pre-Scheme type that the procedure should have, which is necessary for type inference.
Next: Invoking the Pre-Scheme compiler, Previous: Standard Pre-Scheme environment, Up: Pre-Scheme [Contents]
Along with the prescheme
structure, there are several other
structures built-in to Pre-Scheme.
• Pre-Scheme floating point operation: | ||
• Pre-Scheme record types: | ||
• Multiple return values in Pre-Scheme: | ||
• Low-level Pre-Scheme memory manipulation: | ||
Next: Pre-Scheme record types, Up: More Pre-Scheme packages [Contents]
Since Pre-Scheme’s strict static type system would not permit
overloading of the arithmetic operators for integers & floats, it
provides a different set of operators for floats. These names are all
exported by the ps-flonums
structure.
All of these operations flop
correspond as floating point
variations of their op integer equivalents.
Next: Multiple return values in Pre-Scheme, Previous: Pre-Scheme floating point operation, Up: More Pre-Scheme packages [Contents]
The ps-record-types
structure defines the following special form
for introducing record types. Pre-Scheme record types are translated
to C as structs.
(define-record-type type type-descriptor (constructor argument-field-tag …) (field-tag<1> field-type-spec<1> field-accessor<1> [field-modifier<1>]) (field-tag<2> field-type-spec<2> field-accessor<2> [field-modifier<2>]) … (field-tag<n> field-type-spec<n> field-accessor<n> [field-modifier<n>])
Defines a record type. Type is mangled to the C struct type name (type-descriptor-name is unused unless running Pre-Scheme as Scheme). Constructor is defined to construct a record of the new type and initialize the fields argument-type-field … with its arguments, respectively. If it cannot allocate a sufficient quantity of memory, constructor returns a null pointer. The initial values of fields that are not passed to the constructor are undefined. For each field field<i> specified,
(=> (type-name) field-type-spec<i>)
; and
(=> (type-name
field-type-spec) unit)
.
Records must be deallocated explicitly when their lifetime has expired
with deallocate
.
Next: Low-level Pre-Scheme memory manipulation, Previous: Pre-Scheme record types, Up: More Pre-Scheme packages [Contents]
Pre-Scheme support multiple return values, like in Scheme. The only
difference is that one cannot operate on multiple return values as
lists, since Pre-Scheme does not have lists. Multiple return values
are implemented in C as returning in C the first value and passing
pointers to the remaining values, which the function returning multiple
values assigns. The prescheme
structure exports the two
multiple return value primitives, call-with-values
and
values
, but the ps-receive
structure exports this macro
for more conveniently binding multiple return values.
Binds the lambda
parameter list formals to the multiple
values that producer returns, and evaluates body with the
new variables bound.
(receive formals producer body) ≡ (call-with-values (lambda () producer) (lambda formals body))
Previous: Multiple return values in Pre-Scheme, Up: More Pre-Scheme packages [Contents]
Pre-Scheme is a low-level language. It provides very low-level, direct
memory manipulation. ‘Addresses’ index a flat store of sequences of
bytes. While Pre-Scheme ‘pointers’ are statically checked for data
coherency, allow no arbitrary arithmetic, and in general are high-level
abstract data to some extent, addresses are much lower-level, have no
statically checked coherency — the values an address represents are
selected by what operation used to read or write from it —, permit
arbitrary address arithmetic, and are a much more concrete interface
into direct memory. The ps-memory
structure exports these
direct memory manipulation primitives.
Allocate-memory
reserves a sequence of size bytes in the
store and returns an address to the first byte in the sequence.
Deallocate-memory
releases the memory at address, which
should have been the initial address of a contiguous byte sequence, as
allocate-memory
would return, not an offset address from such an
initial address.
Procedures for reading from & storing to memory.
Unsigned-byte-ref
& unsigned-byte-set!
access & store the
first unsigned byte at address. Word-ref
&
word-set!
access & store the first word — Pre-Scheme integer
— beginning at address. Flonum-ref
& flonum-set!
access & store 64-bit floats beginning at address..
Bug: Flonum-ref
& flonum-set!
are unimplemented
in the Pre-Scheme-as-Scheme layer (see Running Pre-Scheme as Scheme).
Disjoint type predicate for addresses.
Note: Address?
is available only at the top
level, where code is evaluated at compile-time. Do not use this in any
place where it may be called at run-time.
The null address. This is somewhat similar to the null pointer, except that it is an address.
Note: One acquires the null pointer by calling the
procedure null-pointer
, whereas the constant value of the
binding named null-address
is the null address.
Null-address?
returns true if address is the null
address and false if not.
Address arithmetic operators. Address+
adds increment to
address; address-
subtracts decrement from
address; and address-difference
returns the integer
difference between address<a> and address<b>.
For any address<p> & address<q>, (address+
address<p> (address-difference address<p>
address<q>))
is equal to address<q>.
Address comparators.
Integers and addresses, although not the same type, may be converted to
and from each other; integer->address
& address->integer
perform this conversion. Note that Pre-Scheme pointers may not
be converted to addresses or integers, and the converse is also true.
Copies count bytes starting at source-address to
target-address. This is similar to C’s memcpy
.
Compares the two sequences of count bytes starting at addresses address<a> & address<b>. It returns true if every byte is equal and false if not.
Char-pointer->string
returns a string with size bytes from
the contiguous sequence of bytes starting at address.
Char-pointer->nul-terminated-string
does similarly, but it
returns a string whose contents include every byte starting at
address until, but not including, the first 0 byte, i.e. ASCII
nul character, following address.
Read-block
attempts to read count bytes from port
into memory starting at address. Write-block
attempts to
write count bytes to port from the contiguous sequence in
memory starting at address. Read-block
returns three
values: the number of bytes read, whether or not the read went to the
end of the file, and the error status (see Pre-Scheme error handling). Write-block
returns the error status.
Next: Example Pre-Scheme compiler usage, Previous: More Pre-Scheme packages, Up: Pre-Scheme [Contents]
Richard Kelsey’s Pre-Scheme compiler is a whole-program compiler based on techniques from his research in transformational compilation [Kelsey 89]. It compiles the restricted dialect of Scheme to efficient C, and provides facilities for programmer direction in several optimizations.
There is a script, a Scheme48 ‘command
program’, that comes with Scheme48 to load the Pre-Scheme compiler,
which is in the file ps-compiler/load-ps-compiler.scm. It must
be loaded from the ps-compiler/ directory, from Scheme48’s main
distribution, into the exec
package, after having loaded
../scheme/prescheme/interface.scm &
../scheme/prescheme/package-defs.scm into the config
package. The Pre-Scheme compiler takes some time to load, so it may be
easier to load it once and dump a heap image of the suspended command
processor after having loaded everything; see ‘Image-building
commands’.
To load the Pre-Scheme compiler and dump an image to the file
ps-compiler.image that contains prescheme-compiler
in the
user package, send this sequence of commands to the command processor
while in the ps-compiler/ directory of Scheme48’s distribution:
,config ,load ../scheme/prescheme/interface.scm ,config ,load ../scheme/prescheme/package-defs.scm ,exec ,load load-ps-compiler.scm ,in prescheme-compiler prescheme-compiler ,user (define prescheme-compiler ##) ,dump ps-compiler.image "(Pre-Scheme)"
After having loaded the Pre-Scheme compiler, the
prescheme-compiler
structure is the front end to the compiler
that exports the prescheme-compiler
procedure.
Invokes the Pre-Scheme compiler. Config-filenames contain module
descriptions (see ‘Module system’) for the components of the program.
Structure-spec may be a symbol or a list of symbols, naming the
important structure or structures. All structures that it relies/they
rely on are traced in the packages’ open
clauses. Modules that
are not traced in the dependency graph with root vertices of the given
structure[s] are omitted from the output. C-filename is a string
naming the file to which the C code generated by the Pre-Scheme
compiler should be emitted. Init-name is the name for an
initialization routine, generated automatically by the Pre-Scheme
compiler to initialize some top-level variables. The command
arguments are used to control certain aspects of the compilation. The
following commands are defined:
(copy (structure copyable-procedure) …)
Specifies that each the body of each copyable-procedure from the respective structure (from one of config-filenames) may be integrated & duplicated.
(no-copy (structure uncopyable-procedure) …)
Specifies that the given procedures may not be integrated.
(shadow ((proc-structure procedure) (var-structure variable-to-shadow) …) …)
Specifies that, in procedure from proc-structure, the global variables variable-to-shadow from their respective var-structures should be shadowed with local variables, which are more likely to be kept in registers for faster operation on them.
(integrate (client-procedure integrable-procedure) …)
Forces integrable-procedure to be integrated in client-procedure.
Note: The integrate
command operates on the global
program, not on one particular module; each client-procedure and
integrable-procedure is chosen from all variables defined in the
entirety of the program, across all modules. It is advised that there
be only one of each.
(header header-line …)
Each header-line is added to the top of the generated C file,
after a cpp inclusion of <stdio.h>
and "prescheme.h"
.
The command arguments to prescheme-compiler
are optional; they
are used only to optimize the compiled program at the programmer’s
request.
Next: Running Pre-Scheme as Scheme, Previous: Invoking the Pre-Scheme compiler, Up: Pre-Scheme [Contents]
The ps-compiler/compile-vm.scm,
ps-compiler/compile-gc.scm, and
ps-compiler/compile-vm-no-gc.scm files give examples of running
the Pre-Scheme compiler. They are Scheme48 ‘command programs’, to be loaded into the exec
package after
having already loaded the Pre-Scheme compiler. compile-vm.scm &
compile-vm-no-gc.scm generate a new scheme48vm.c in the
scheme/vm/ directory — compile-vm.scm includes the
garbage collector, while compile-vm-no-gc.scm does not
3 —, and compile-gc.scm
generates a new scheme48heap.c, scheme48read-image.c, &
scheme48write-image.c in the scheme/vm/ directory.
Here is a somewhat simpler example. It assumes a pre-built image with the Pre-Scheme compiler loaded is in the ps-compiler.image file in the current directory (see Invoking the Pre-Scheme compiler, where there is a description of how to dump an image with the Pre-Scheme compiler loaded).
% ls hello.scm packages.scm ps-compiler.image % cat hello.scm (define (main argc argv) (if (= argc 2) (let ((out (current-output-port))) (write-string "Hello, world, " out) (write-string (vector-ref argv 1) out) (write-char #\! out) (newline out) 0) (let ((out (current-error-port))) (write-string "Usage: " out) (write-string (vector-ref argv 0) out) (write-string " <user>" out) (newline out) (write-string " Greets the world & <user>." out) (newline out) -1))) % cat packages.scm (define-structure hello (export main) (open prescheme) (files hello)) % scheme48 -i ps-compiler.image heap size 3000000 is too small, using 4770088 Welcome to Scheme 48 1.3 (Pre-Scheme) Copyright (c) 1993-2005 by Richard Kelsey and Jonathan Rees. Please report bugs to scheme-48-bugs@s48.org. Get more information at http://www.s48.org/. Type ,? (comma question-mark) for help. > (prescheme-compiler 'hello '("packages.scm") 'hello-init "hello.c") packages.scm hello.scmChecking types main : ((integer **char) -> integer) In-lining single-use procedures Call Graph: <procedure name> <called non-tail-recursively> <called tail-recursively> main (exported) Merging forms Translating main #{Unspecific} > ,exit % cat hello.c #include <stdio.h> #include "prescheme.h" long main(long, char**); long main(long argc_0X, char **argv_1X) { FILE * out_3X; FILE * out_2X; { if ((1 == argc_0X)) { out_2X = stdout; ps_write_string("Hello, world, ", out_2X); ps_write_string((*(argv_1X + 1)), out_2X); { long ignoreXX; PS_WRITE_CHAR(33, out_2X, ignoreXX) } { long ignoreXX; PS_WRITE_CHAR(10, out_2X, ignoreXX) } return 0;} else { out_3X = stderr; ps_write_string("Usage: ", out_3X); ps_write_string((*(argv_1X + 0)), out_3X); ps_write_string(" <user>", out_3X); { long ignoreXX; PS_WRITE_CHAR(10, out_3X, ignoreXX) } ps_write_string(" Greets the world & <user>.", out_3X); { long ignoreXX; PS_WRITE_CHAR(10, out_3X, ignoreXX) } return -1;}} } %
Previous: Example Pre-Scheme compiler usage, Up: Pre-Scheme [Contents]
To facilitate the operation of Pre-Scheme systems within a high-level
Scheme development environment, Scheme48 simply defines the
prescheme
, ps-memory
, ps-record-types
,
ps-flonums
, and ps-receive
structures in terms of Scheme;
Pre-Scheme structures can be loaded as regular Scheme structures
because of this. Those structures and the interfaces they implement
are defined in the files scheme/prescheme/interface.scm and
scheme/prescheme/package-defs.scm from the main Scheme48
distribution; simply load these files into the ‘config package’ before loading any Pre-Scheme configuration
files.
The Pre-Scheme emulation layer in Scheme has some shortcomings:
It
may be possible to use Pre-Scheme’s C FFI to manually use setjmp
& longjmp
, but the author of this manual cannot attest to this
working.
Scheme48’s VM does not use Pre-Scheme’s built-in I/O
facilities to implement ‘channels’ — it builds its
own lower-level facilities that are still OS-independent, but, because
they’re written individually for different OSs, they integrate better
as low-level I/O channels with the OS. On Unix, the Scheme48 VM uses
file descriptors; Pre-Scheme’s built-in I/O uses stdio
.
Scheme48’s VM uses Pre-Scheme’s built-in I/O only to read heap images.
The actual distribution of Scheme48 separates the garbage collector and the main virtual machine.