CRUNCH
- CRUNCH
- Description
- Author
- Requirements
- Repository
- Introduction
- Principle of Operation
- Usage
- Supported Language
- The Debugger
- Interoperability with C
- Configuring and customizing the generated C code
- Examples
- Changelog
- License
Description
A compiler for a statically typed subset of R7RS Scheme.
Author
Requirements
Repository
This egg is hosted on the CHICKEN Subversion repository:
https://anonymous@code.call-cc.org/svn/chicken-eggs/release/6/crunch
If you want to check out the source code repository of this egg and you are not familiar with Subversion, see this page.
Introduction
CRUNCH is an embedded compiler for a statically typed subset of R7RS Scheme, generating C code. The compiler uses type inference to decorate the code with type information without requiring declarations. CRUNCH can be used to translate embedded Scheme code sections, whole programs or multiple source modules into standalone executables or compiled code that can be invoked from Scheme.
The generated C code uses a small runtime-system contained completely in a single C header file. Reference counting is used for managing aggregate data like strings which removes the need for full tracing garbage collection or manual memory management while still having a relatively small overhead.
Since more or less a direct translation of Scheme to C is done, the generated code should run at roughly the same performance as C. No type-checking takes place as the types of all values have been inferred at compile time, and values are not tagged. With the exception of reference counted objects there is no additional runtime overhead and Scheme and C can directly interchange data. This makes CRUNCH very appropriate for writing programs that need a maximum of speed or that are target for constrained environments like deeply embedded systems. The code is portable to all systems that at least have a C compiler.
UNICODE strings are supported and can optionally be disabled for improving performance and reducing code size.
CRUNCH is heavily inspired by PreScheme, the low-level compiler that is originally part of the Scheme48 project. In fact, CRUNCH can be considered a modern reimplementation of PreScheme written in and for use with CHICKEN.
Principle of Operation
CRUNCH takes Scheme source code and translates it to C99 (with some GCC/Clang extensions). The main entry point is the crunch procedure provided by the (crunch compiler) module. The Scheme code undergoes namespace-resolution and macro-expansion and is translated into an internal node tree.
After expansion, the node tree is decorated with type-information and an iterative type-inference phase attempts to complete missing type information in the node tree until the type of every expression is known. Should nodes with incomplete type information still exist after the inference phase, the compiler aborts with an error message.
Once the tree is completely typed, the optimization phase performs a number of transformations on the node tree to reduce overhead and code size. These optimizations are also important to handle certain idiomatic Scheme programming constructs that would normally be disallowed in a fully static compiler without automatic storage management and full tail calls. See below for what restrictions apply to the dialect of Scheme that CRUNCH supports.
After optimization C code is generated and written to an external file, to be compiled separately or for embedding into Scheme code, to be accessed later using the CHICKEN foreign function interface.
For using CRUNCH most effectively it is of great advantage to understand how the compilation process works. This helps you work around the existing restrictions that apply to Scheme code to compile successfully and to make sense of error messages that the compiler may show in case code can not be compiled. The compilation process is split into three phases: macroexpansion and namespace resolution, type inference and reconstruction and optimizations. These phases are now described in more detail.
Expansion phase
During expansion the default module- and macroexpansion machinery of the CHICKEN Scheme system is used to resolve syntactic definitions and modules. Code compiled with CRUNCH can use arbitrary macros, from the core system or from extensions, as long as the expanded code is suitable for compilation.
Note that the full module- and macro-facility of CHICKEN is available, including syntax-rules, procedural macros and expansion- time evaluation. Compile-time code can make full use of CHICKEN code and libraries.
Top-level expressions are evaluated at compile time - this is different from the normal compilation model used in CHICKEN but can be quite helpful for diagnostics and the compile-time creation of data structures like tables. During evaluation, the full Scheme language is available, as long as the values computed and globally defined (and thus shared with the code compiled with CRUNCH) are representable.
During expansion of crunched code, the feature identifier crunched is defined to allow conditionally expanding code using cond-expand.
Type inference phase
During this phase all nodes in the node tree are decorated with type information from literals and calls to known primitive procedures.
The analysis interatively propagates types through the code and performs unification of type variables to (hopefully) end with a fully decorated program. Certain primitive operations are overloaded and so support a larger class of argument types - the effective operation performed in the compiled code depends on the types passed to the operation. Notable examples are the generic arithmetic primitives like "+" or arithmetic comparisons like ">=".
Known procedures that are called with arguments of different types are supported by creating variants, effectively copying the procedure for every call site that uses a different set of argument types. Note that this mechanism is currently not available for procedures with optional arguments.
Optimization phase
CRUNCH performs roughly three types of optimizations. First, integration of known calls to procedures that are only invoked once ("integration"), including the detection of loops (procedures that tail-call themselves recursively). The second optimization is "lambda lifting", i.e. passing free variables to procedures with known call sites where the free variable is accessible. This also includes lifting locally let-bound variables to toplevel, effectively turning them into global variables, if possible.
Finally, a dead code elimination pass removes unused variables, side-effect free code and performs certain canonicalizations and simplifications to make code-generation easier.
The set of optimizations performed is intentionally small to simplify the compiler and delegate micro-optimizations to the C compiler. For example, "constant folding" (evaluation of expressions with constant arguments) is not performed. Scheme's compile-time facilities are already powerful enough and give the user full control over source-to-source transformations.
The optimizer is not intended to make bad code run well. CRUNCH is intended to be used for constrained targets and high-performance code and relies on the user to know what he or she is doing.
Restrictions
Code compiled with CRUNCH has a considerable number of restrictions, but care has been taken to allow more or less straighforward idiomatic Scheme code in most cases. These restrictions are in part based on the statically typed nature of the supported Scheme dialect, but also on the requirement to generate (more or less) straightforward C code without the need for an extensive and slow run-time system. Also, there is a limit to how much effort should be put into compiler optimizations, as an excessively complex compilation process increases compile-times, makes reasoning about how code is generated harder and leads to hard to understand and maintain code.
Scheme, as understood by CRUNCH, does not support the following features:
- Continuations
- Fully general tail-call optimization
- Multiple values
- Closures
- "Dotted" rest-argument lists
- Full numeric tower
The compiler can turn certain obvious tail-recursive procedures into loops, specifically the expansions of named let and do.
Procedures are always represented as a pure function pointer, as in C. If a procedure accesses "free" variables (variables defined in an outer scope), the compiler will pass these free variables as extra arguments, if all call-sites have access to these variables. If not, the procedure can not be compiled and the translation will abort with an error. Free variables are lifted to global scope if it can be determined that the containing procedure has only ever a single executing instance.
Procedures have a fixed number of arguments, with limited support for optional arguments (using the #!optional keyword). If default values are not given, optional arguments contain a valid but unspecified value of the inferred type. Default values can refer to previous argument variables.
One specfific use of procedures with a single "rest" argument is allowed, see below for the documentation of apply to learn where the case is supported.
Modules can only export procedures, imported bindings may not be destructively modified.
The types of values that are allowed in CRUNCH programs are limited to integers, floating point numbers, inexact complex numbers, booleans, characters, vectors and strings. Symbols, lists/pairs and keywords are not supported. Primitive R7RS procedures that may return results of non-uniform type (like, for example, string->number which may return either a number or #f) are not supported. Supported numeric types are integers, floating-point numbers (double) and C99 complex numbers (double complex). There is no overflow detection.
The lifetime of vectors, strings and ports is management using reference counting, objects are deallocated shortly before the last procedure referencing them returns.
Usage
CRUNCH can be used in several different ways, either as a batch compiler, translating Scheme source files to C, as a macro for embedding compiled code into files compiled with the normal CHICKEN compiler or as a library.
Standalone
The simplest way: use the chicken-crunch executable to compile one or more Scheme files:
% cat hello.scm (import (scheme write)) (define (main) (display "Hello, world!\n")) % chicken-crunch hello.scm -o hello.c % cc hello.c -o hello
The crunch program accepts the same options as the crunch procedure from the (crunch compiler) module described later, as it is mainly just a small wrapper program for that module.
Embedded
To directly embed compiled code into CHICKEN Scheme source code, use the crunch macro from the crunch module:
[syntax] (crunch FORM ...)Treats FORM ... as an implicit begin holding toplevel expressions. The forms will be compiled to C and replace the (crunch ...) form with the verbatim compiled code and a number of foreign procedure wrappers that can be used to invoke every exported procedure in FORM .... The Forms may contain module or R7RS library definitions or just normal toplevel definitions, in the latter case all defined procedures will be exported and wrapped.
(import crunch (chicken bitwise) (chicken fixnum)) (crunch (define (popcount32 x) ; From "Hacker's Delight" (let ((x (fx- x (bitwise-and (arithmetic-shift x -1) #x55555555)))) (let ((x (fx+ (bitwise-and x #x33333333) (bitwise-and (arithmetic-shift x -2) #x33333333)))) (let ((x (bitwise-and (fx+ x (arithmetic-shift x -4)) #x0F0F0F0F))) (let ((x (fx+ x (arithmetic-shift x -8)))) (let ((x (fx+ x (arithmetic-shift x -16)))) (bitwise-and x #x0000003F)))))))))
Embedded code compiled using the crunch form exposes only toplevel procedure definitions. Most types are allowed as arguments and results to crunched procedures and conversion from and to Scheme data objects is automatic. Arguments and results can be of the types character, float, integers (but not bignums), complex, string, bytevector, pointer and numeric vector. Strings, bytevectors and numeric vectors passed as arguments share storage with the argument value on the Scheme side, but are copied when returned as results. If a bytevector or number-vector that was passed as an argument is returned, then the resulting value on the Scheme side will share storage with the original object, but may or may to be eq? to it.
[syntax] (: NAME TYPE)Declares the type of a variable, which is sometimes required for embedded code as the call-sites originate from non-crunched code making argument types unprecise. This is re-exported from the (crunch declarations) module (see below) for convenience and only effective inside a crunch form. Outside of it, the behaviour is identical to the : exported from the core module (chicken type).
[syntax] (crunch-options OPTION ...)Set options for subsequent invocations of the crunch macro. The options should be unquoted and are of the same format as the options passed to the crunch procedure exported from the (crunch compiler) module.
Using CRUNCH as a library
Finally, the (crunch compiler) module provides a single entry point for compiling code:
[procedure] (crunch ARGUMENT ...)ARGUMENT should be a string, symbol or list specifying files or forms to compile or options that should be applied to the compilation process. The following arguments expressions are supported (symbols or lists):
- whole-program
- Compile all provided source files and source expressions as a single unit, only export the main procedure.
- explain
- describe optimizations performed during translation.
- (emit-import-library MODULENAME)
- After translation, emit import library for the given module.
- emit-all-import-libraries
- Emit import libraries for all compiled modules.
- (output-file FILENAME)
- Store compiled code in FILENAME (a string).
- export-all
- Export all toplevel procedures, even if inside a module.
- verbose
- Show progress information during compilation.
- dump-expanded
- Dump intermediate node tree to stdout after macro-expansion.
- dump-resolved
- Dump node tree after type-inference.
- dump-optimized
- Dump node tree after optimizations.
- dump-with-types
- When dumping the intermediate node tree, decorate it with some type information.
- list-types
- Show exported entities with their types.
- (consumer PROCEDURE)
- After compilation invoke PROCEDURE with strings containing the compiled code. The output-file option effectively overrides this setting to write the code to a file.
- (export-consumer PROCEDURE)
- Invoke procedure with name names and an a-alist of exports after compilation.
- (error-handler PROCEDURE)
- Invoke procedure with exception object in case compilation fails with an error (default is abort).
- (information-handler PROCEUDRE)
- Invoke procedure with diagnostic message and port for informational messages (default is display).
- (begin FORM ...)
- Compile given forms.
- debug
- Do not produce any code but invoke the graphical debugger (see below).
- FILENAME
- (string) Treat string as name of file containing Scheme source code.
Import libraries
Code compiled with CRUNCH can generate import libraries for modules which can be used in separately compiled code in the same manner as CHICKEN import libraries are used. The import libraries hold syntactic definitions and information about the exported toplevel procedures, together with the inferred argument and result types. So if file a.scm contains a module definition for module A and file b.scm uses (imports) the module), the having the (possibly compiled) import library in the same directory (or in the location where import libraries for extensions are stored) allows b.scm to import A and access the exported toplevel bindings and/or syntactic definitions from that module.
CRUNCH code can use import libraries generated by CHICKEN for normal Scheme code but have only access to exported syntactic definitions.
Supported Language
Where possible, R7RS standard procedures and CHICKEN runtime library operations are mapped to CRUNCH primitives to ease porting code and to allow testing code in CHICKEN before compiling it with CRUNCH. This is done by mapping the fully resolved, module-qualified procedure name to an internal CRUNCH primitive. The set of primitives can be extended by using certain forms from the (crunch declarations) module, described later.
Types
CRUNCH understands the following value types:
- integer
- word-sized signed integers (long)
- float
- 64-bit IEEE floating point numbers (double)
- complex
- complex numbers with 64-bit real and imaginary parts (double complex)
- boolean
- truth value (int)
- char
- 8 or 32-bit character code or UNICODE code-point
- string
- a sequence of 8-bit characters or 32-bit UNICODE code points
- (vector T)
- a vector of elements of type T
- pointer
- a raw machine pointer (void *)
- (pointer T)
- a pointer to a an object of type T ((T *), may also be written as (* T)
- (struct NAME)
- a C structure of the given name (struct NAME), where NAME must be a symbol
- (union NAME)
- a C union (union NAME), where NAME must be a symbol
- (typename NAME)
- an arbitrary C type
- port
- An input/output port
- void
- An unspecified type, used when a procedure or primitive has no result value
- (numvector S)
- A homogeneous numeric vector, S may be one of the symbols u8, s8, u16, s16, u32, s32, u64, s64, f32, f64, c64 or c128
- (procedure (T1 ... [#!optional T2 ...]) T)
- a procedure type, possibly with optional arguments
Procedure types may also be given in the notation (ARGTYPE ... -> RETURNTYPE).
Normal vectors holding elements basic numeric type may be convertible to certain numeric vector types, but this depends on the word size of the platform. It is advisible to use the most specific vector type required.
Note that struct and union types are by value, not pointer types (use the (pointer ...) qualifier in that case).
Some primitive operations are overloaded: they accept arguments of different types and usually promote arguments of less precision to the more precise type. For example (+ <integer> <float>) will promote the result to float. Generally, promotion is the same as in C, where values are promoted from left to right: integer -> float -> complex.
Symbols, keywords, lists and pairs have no equivalent type and are not available, as are the empty list, the eof or bwp objects or any other type not explicitly listed here.
Meta-language
The full meta-language of CHICKEN Scheme is provided, including define-syntax, module, define-library, import, etc.
Modules may only export toplevel procedure bindings. Global variables may hold non-procedures, as long as the variables are not exported.
An exported main procedure is specifically handled. It represents the entry point for a program and takes zero to three arguments: the argument count, a pointer to the programs arguments and a pointer to the environment (argc, argv, envp) as in C. The result of the main procedure is returned if an integer. A result of any other type is assumed to have status code 0.
Exported procedures must have valid C function names, so names containing special characters are "mangled", like this:
(define (foo-bar) ...)
will export the C symbol foo_X2dbar. Exported procedures are not decorated in any way with a module prefix or qualifier: they are visible with the same name as the one that has been exported. If you mix multiple modules in the same source file or link multiple separately compiled modules, then the names will clash if they have the same exported identifier.
R7RS (Small) Scheme primitives
R7RS builtin procedures that return arguments of different types (depending on arguments) are not supported. Also not available are type-predicates as all expressions in a program must have a known distinct type.
Strings are assumed to be in UTF-8 representation unless the generated code is compiled with the CRUNCH_NO_UTF C macro defined, which disables all UTF-8 related handling and assumes strings contain 8 bit characters.
Vector- and string element references perform bounds checking by default. This can be disabled by defining the NDEBUG C macro when compiling the generated code.
All R7RS (Small) syntactic forms are available and CHICKEN specific syntax or syntax provided by extensions are usable as long as the expansion produces forms and procedure calls that CRUNCH understands.
Note that and and or are only defined for boolean values.
(scheme base)
[procedure] (+ N1 #!optional N2)[procedure] (* N1 #!optional N2)
[procedure] (- N1 #!optional N2)
[procedure] (/ N1 #!optional N2)
Overloaded arithmetic operators. These accept numbers of any type and perform argument type promotion, resulting in a numeric type that is complex if any argument is, float if any argument type is float (but not complex) or is an integer. These operators only accept up to two arguments.
[procedure] (< N1 N2)[procedure] (<= N1 N2)
[procedure] (= N1 N2)
[procedure] (> N1 N2)
[procedure] (>= N1 N2)
Numeric comparison, also overloaded and return a boolean.
[procedure] (abs N)[procedure] (exact N)
[procedure] (inexact N)
[procedure] (negative? N)
[procedure] (positive? N)
[procedure] (zero? N)
[procedure] (square N)
Overloaded numeric operators. abs returns a result of the same type as the argument, exact an integer, inexact a float and all others a boolean.
[procedure] (max N1 N2)[procedure] (min N1 N2)
Overloaded numeric maximum and minimum, arguments must be of the same type.
[procedure] (even? I)[procedure] (odd? I)
Integer predicates, returning a boolean.
[procedure] (quotient I1 I2)[procedure] (remainder I1 I2)
[procedure] (modulo I1 I2)
[procedure] (truncate-remainder I1 I2)
[procedure] (truncate-quotient I1 I2)
Integer division.
[procedure] (eq? X1 X2)[procedure] (eqv? X1 X2)
[procedure] (equal? X1 X2)
Equality predicates. The arguments must be of the same type.
[procedure] (ceiling F)[procedure] (floor F)
[procedure] (truncate F)
[procedure] (round F)
Floating point truncation, also defined for complex numbers. Return a value of the same type as the argument.
[procedure] (char<=? C1 C2)[procedure] (char<? C1 C2)
[procedure] (char=? C1 C2)
[procedure] (char>=? C1 C2)
[procedure] (char>? C1 C2)
[procedure] (char->integer I)
[procedure] (integer->char C)
Character conversion and comparison. These operators assume full UNICODE code point range unless the generated code has disabled unicode support.
[procedure] (vector #!optional X1 X2 X3 X4 X5 X6 X7 X8 X9 X10)[procedure] (make-vector I X)
[procedure] (vector-append V1 V2 #!optional V3 V4 V5 V6 V7 V8 V9 V10)
[procedure] (vector-copy V #!optional I1 I2)
[procedure] (vector-copy Vx #!optional integer integer)
[procedure] (vector-copy! Vx integer Vx #!optional integer integer)
[procedure] (vector-fill! Vx X #!optional integer integer)
[procedure] (vector-for-each P V)
[procedure] (vector-for-each PROC V)
[procedure] (vector-length V)
[procedure] (vector-map P V)
[procedure] (vector-map PROC V1)
[procedure] (vector-ref V I)
[procedure] (vector-set! V I X)
Operations on vectors. The vector constructor must receive arguments of identical types or all arguments must be numeric types and will be promoted.
[procedure] (string #!optional C1 C2 C3 C4 C5 C6 C7 C8 C9 C10)[procedure] (make-string I #!optional C)
[procedure] (vector->string V #!optional I1 I2)
[procedure] (string->vector S #!optional I1 I2)
[procedure] (string-append S1 S2 #!optional S3 S4 S5 S6 S7 S8 S) S10)
[procedure] (string-copy S1 #!optional I1 I2)
[procedure] (string-copy! S1 I1 S2 #!optional I2 I3)
[procedure] (string-fill! S C #!optional I1 I2)
[procedure] (string-for-each PROC S)
[procedure] (string-length S)
[procedure] (string-map PROC S1)
[procedure] (string->utf8 S #!optional I1 I2)
[procedure] (string<=? S1 S2)
[procedure] (string<? S1 S2)
[procedure] (string=? S S)
[procedure] (string>=? S1 S2)
[procedure] (string>? S1 S2)
[procedure] (substring S I1 I2)
[procedure] (number->string N #!optional I)
[procedure] (utf8->string NVu8 #!optional I1 I2)
Operations on strings, default to UNICODE with UTF-8 encoding. number->string is overloaded to accept any numeric type for the first argument.
[procedure] (make-bytevector I1 #!optional I2)[procedure] (bytevector-append NVu8 NVu8 #!optional NVu8 NVu8 NVu8 NVu8 NVu8 NVu8 NVu8 NVu8)
[procedure] (bytevector-copy NVu8 #!optional I1 I2)
[procedure] (bytevector-copy! NVu81 I1 NVu82 #!optional I2 I3)
[procedure] (bytevector-length NVu8)
[procedure] (bytevector-u8-ref NVu8 I)
[procedure] (bytevector-u8-set! NVu8 I1 I2)
Operations on bytevectors (or u8vectors in SRFI-4 parlance).
[procedure] (call-with-port P PROC)[procedure] (char-ready? #!optional P)
[procedure] (close-input-port P)
[procedure] (close-output-port P)
[procedure] (close-port P)
[procedure] (flush-output-port #!optional P)
[procedure] (get-output-bytevector P)
[procedure] (get-output-string P)
[procedure] (input-port-open? P)
[procedure] (newline #!optional P)
[procedure] (open-input-bytevector)
[procedure] (open-input-string)
[procedure] (open-output-bytevector)
[procedure] (open-output-string)
[procedure] (output-port-open? P)
[procedure] (peek-char #!optional P)
[procedure] (peek-u8 #!optional P)
[procedure] (read-bytevector N #!optional P)
[procedure] (read-bytevector! NVu8 #!optional P I1 I2)
[procedure] (read-char #!optional P)
[procedure] (read-string I #!optional P)
[procedure] (read-u8 #!optional P)
[procedure] (u8-ready? #!optional P)
[procedure] (write-bytevector Vu8 #!optional P I1 I2)
[procedure] (write-char C #!optional P)
[procedure] (write-string S #!optional P I1 I2)
[procedure] (write-u8 I #!optional P)
Operations on ports, input and output. Note that errors occurring when a file can not be opened do not raise an exception or error. To test whether a port could be successfully opened, use *-open-open?.
All I/O is done using ANSI-C FILE streams.
[procedure] (current-error-port #!optional P)[procedure] (current-input-port #!optional P)
[procedure] (current-output-port #!optional P)
Accessors for the current I/O ports. Since parameterize is not available, these procedures accept an optional argument to change the current input-, output- or error-port.
[procedure] (output-port? P)[procedure] (input-port? P)
[procedure] (binary-port? P)
[procedure] (textual-port? P)
Port predicates. These operations always expect a port as argument and only distinguish between input/output and/or textual/binary ports.
[procedure] (eof-object? X)Returns #t if X is an EOF-object. X must be the result of a char, bytevector or string input procedure. This operation is overloaded in a particular way to allow its idiomatic use in Scheme code. For this reason there exist several internal EOF-objects, one for each possible type.
[procedure] (error S #!optional ...)Ignores all but the first argument and aborts execution with a runtime error.
[procedure] (boolean=? B1 B2)[procedure] (not B)
Miscellaneous operations. Note that the argument to not must be a boolean.
[procedure] (dynamic-wind BEFORE WHILE AFTER)Merely invokes the three thunks and returns the result of WHILE. Since CRUNCH does not support continuations, this procedure exists only to ease porting code.
[procedure] (values X)[procedure] (call-with-values THUNK CONSUMER)
Minimal support for producing values. Only single-valued cases are supported to allow idiomatic code to be compiled.
[procedure] (apply PROCEDURE ARGUMENT ... VALUES-LIST)A very restricted variant of the standard apply procedure. PROCEDURE is called with the given arguments and VALUES-LIST, which must be the single "rest" argument of a user defined procedure. The following code will return a vector of two elements, the first being the integer 123, the second being the result of the procedure foo:
(call-with-values
foo
(lambda vals (apply vector 123 vals)))
Since CRUNCH does not support either lists or multiple values, the last argument to apply always ever holds a single value. This particular use of apply is only implemented to support the common idiom where all results of an expression are saved and returned unchanged, which is often used in macros:
(define-syntax begin0
(syntax-rules ()
((_ e0 e1 ...)
(call-with-values
(lambda () e0)
(lambda var
(begin
e1 ...
(apply values var)))))))
(scheme complex)
[procedure] (make-rectangular N1 N2)[procedure] (make-polar N1 N2)
[procedure] (magnitude N)
[procedure] (angle N)
[procedure] (real-part N)
[procedure] (imag-part N)
Operations on complex numbers. All of these are overloaded to handle numbers of any type.
(scheme inexact)
[procedure] (sin N)[procedure] (cos N)
[procedure] (tan N)
[procedure] (asin N)
[procedure] (acos N)
[procedure] (atan N1 #!optional N2)
[procedure] (exp N)
[procedure] (log N)
[procedure] (sqrt N)
[procedure] (finite? N)
[procedure] (infinite? N)
[procedure] (nan? N)
[procedure] (expt N1 N2)
Operations on inexact numbers, overloaded. atan with two arguments is currently not defined for complex numbers. expt with integer arguments is not defined for negative powers.
(scheme write)
[procedure] (write X #!optional P)[procedure] (write-simple X #!optional P)
[procedure] (display X #!optional P)
Overloaded output operations. write-simple is just an alias for write.
(scheme char)
[procedure] (char-alphabetic? C)[procedure] (char-numeric? C)
[procedure] (char-lower-case? C)
[procedure] (char-upper-case? C)
[procedure] (char-whitespace? C)
[procedure] (char-downcase C)
[procedure] (char-upcase C)
[procedure] (char-foldcase C)
[procedure] (char-ci=? C1 C2)
[procedure] (char-ci>? C1 C2)
[procedure] (char-ci<? C1 C2)
[procedure] (char-ci>=? C1 C2)
[procedure] (char-ci<=? C1 C2)
[procedure] (string=? S S)
[procedure] (string>? S1 S2)
[procedure] (string<? S1 S2)
[procedure] (string>=? S1 S2)
[procedure] (string<=? S1 S2)
[procedure] (string-downcase S1)
[procedure] (string-upcase S1)
[procedure] (string-foldcase S1)
UNICODE-aware character and string operations (unless UNICODE support is disabled).
(scheme file)
[procedure] (call-with-input-file S PROC)[procedure] (call-with-output-file S PROC)
[procedure] (open-binary-input-file S)
[procedure] (open-binary-output-file S)
[procedure] (open-input-file S)
[procedure] (open-output-file S)
[procedure] (with-input-from-file S PROC)
[procedure] (with-output-to-file S PROC)
[procedure] (delete-file S)
[procedure] (file-exists? S)
File operations.
(scheme time)
[procedure] (current-jiffy)[procedure] (current-second)
[procedure] (jiffies-per-second)
Time operations.
(scheme process-context)
[procedure] (exit #!optional B|I)[procedure] (emergency-exit #!optional B|I)
Exiting the process. See (crunch process-context) for CRUNCH-specific ways to access the command line and environment.
CHICKEN core primitives
A number of CHICKEN runtime library procedures are mapped to equivalent CRUNCH primitives, as described in the following.
(chicken base)
[procedure] (add1 N)[procedure] (sub1 N)
[procedure] (void #!optional ...)
A few helpful overloaded operations.
(chicken bitwise)
[procedure] (bitwise-and I1 I2)[procedure] (bitwise-ior I1 I2)
[procedure] (bitwise-xor I1 I2)
[procedure] (bitwise-not I)
[procedure] (arithmetic-shift I1 I2)
[procedure] (integer-length I)
Bitwise arithmetic on integers.
(chicken fixnum)
[procedure] (fx+ I1 I2)[procedure] (fx- I1 I2)
[procedure] (fx* I1 I2)
[procedure] (fx/ I1 I2)
[procedure] (fxmax I1 I2)
[procedure] (fxmin I1 I2)
[procedure] (fxand I1 I2)
[procedure] (fxior I1 I2)
[procedure] (fxxor I1 I2)
[procedure] (fxmod I1 I2)
[procedure] (fxrem I1 I2)
[procedure] (fxshr I1 I2)
[procedure] (fxshl I1 I2)
[procedure] (fx= I1 I2)
[procedure] (fx> I1 I2)
[procedure] (fx< I1 I2)
[procedure] (fx>= I1 I2)
[procedure] (fx<= I1 I2)
[procedure] (fxnot I1)
[procedure] (fxneg I1)
[procedure] (fxeven? I1)
[procedure] (fxodd? I1)
[procedure] (fxlen I1)
Fixnum (integer) operations.
(chicken flonum)
[procedure] (fp+ F1 F2)[procedure] (fp- F1 F2)
[procedure] (fp* F1 F2)
[procedure] (fp/ F1 F2)
[procedure] (fpmax F1 F2)
[procedure] (fpmin F1 F2)
[procedure] (fp= F1 F2)
[procedure] (fp> F1 F2)
[procedure] (fp< F1 F2)
[procedure] (fp>= F1 F2)
[procedure] (fp<= F1 F2)
[procedure] (fpsin F1)
[procedure] (fpcos F1)
[procedure] (fptan F1)
[procedure] (fpsinh F1)
[procedure] (fpcosh F1)
[procedure] (fptanh F1)
[procedure] (fpasin F1)
[procedure] (fpacos F1)
[procedure] (fpatan F1)
[procedure] (fpasinh F1)
[procedure] (fpacosh F1)
[procedure] (fpatanh F1)
[procedure] (fpatan2 F1 F2)
[procedure] (fpceiling F1)
[procedure] (fptruncate F1)
[procedure] (fpround F1)
[procedure] (fpfloor F1)
[procedure] (fpneg F1)
[procedure] (fp*+ F1 F2 F3)
[procedure] (fpexp F1)
[procedure] (fplog F1)
[procedure] (fpsqrt F1)
[procedure] (fpexpt F1 F2)
Floating point arithmetic and comparison.
(chicken memory)
[procedure] (allocate I)[procedure] (free P)
[procedure] (address->pointer I)
[procedure] (pointer->address I)
[procedure] (pointer=? P1 P2)
[procedure] (pointer-u8-ref P)
[procedure] (pointer-s8-ref P)
[procedure] (pointer-u16-ref P)
[procedure] (pointer-s16-ref P)
[procedure] (pointer-u32-ref P)
[procedure] (pointer-s32-ref P)
[procedure] (pointer-u64-ref P)
[procedure] (pointer-s64-ref P)
[procedure] (pointer-f32-ref P)
[procedure] (pointer-f64-ref P)
[procedure] (pointer-c64-ref P)
[procedure] (pointer-c128-ref P)
[procedure] (pointer-u8-set! P I)
[procedure] (pointer-s8-set! P I)
[procedure] (pointer-u16-set! P I)
[procedure] (pointer-s16-set! P I)
[procedure] (pointer-u32-set! P I)
[procedure] (pointer-s32-set! P I)
[procedure] (pointer-u64-set! P I)
[procedure] (pointer-s64-set! P I)
[procedure] (pointer-f32-set! P F)
[procedure] (pointer-f64-set! P F)
[procedure] (pointer-c64-set! P C)
[procedure] (pointer-c128-set! P C)
Pointer operations and raw memory access. Note that free is overloaded to accept any kind of pointer.
(chicken number-vector)
[procedure] (u8vector-ref NVu8 I)[procedure] (s8vector-ref NVs8 I)
[procedure] (u16vector-ref NVu16 I)
[procedure] (s16vector-ref NVs16 I)
[procedure] (u32vector-ref NVu32 I)
[procedure] (s32vector-ref NVs32 I)
[procedure] (u64vector-ref NVu64 I)
[procedure] (s64vector-ref NVs64 I)
[procedure] (f32vector-ref NVf32 I)
[procedure] (f64vector-ref NVf64 I)
[procedure] (c64vector-ref NVc64 I)
[procedure] (c128vector-ref NVc128 I)
[procedure] (u8vector-set! NVu8 I1 I2)
[procedure] (s8vector-set! NVs8 I1 I2)
[procedure] (u16vector-set! NVu16 I1 I2)
[procedure] (s16vector-set! NVs16 I1 I2)
[procedure] (u32vector-set! NVu32 I1 I2)
[procedure] (s32vector-set! NVs32 I1 I2)
[procedure] (u64vector-set! NVu64 I1 I2)
[procedure] (s64vector-set! NVs64 I1 I2)
[procedure] (f32vector-set! NVf32 I F)
[procedure] (f64vector-set! NVf64 I F)
[procedure] (c64vector-set! NVc64 I C)
[procedure] (c128vector-set! NVc128 I C)
[procedure] (make-u8vector I1 #!optional I2)
[procedure] (make-s8vector I1 #!optional I2)
[procedure] (make-u16vector I1 #!optional I2)
[procedure] (make-s16vector I1 #!optional I2)
[procedure] (make-u32vector I1 #!optional I2)
[procedure] (make-s32vector I1 #!optional I2)
[procedure] (make-u64vector I1 #!optional I2)
[procedure] (make-s64vector I1 #!optional I2)
[procedure] (make-f32vector I #!optional F)
[procedure] (make-f64vector I #!optional F)
[procedure] (make-c64vector I #!optional C)
[procedure] (make-c128vector I #!optional C)
[procedure] (u8vector-length NVu8)
[procedure] (s8vector-length NVs8)
[procedure] (u16vector-length NVu16)
[procedure] (s16vector-length NVs16)
[procedure] (u32vector-length NVu32)
[procedure] (s32vector-length NVs32)
[procedure] (u64vector-length NVu64)
[procedure] (s64vector-length NVs64)
[procedure] (f32vector-length NVf32)
[procedure] (f64vector-length NVf64)
[procedure] (c64vector-length NVc64)
[procedure] (c128vector-length NVc128)
[procedure] (subu8vector NVu8 I1 I2)
[procedure] (subs8vector NVs8 I1 I2)
[procedure] (subu16vector NVu16 I1 I2)
[procedure] (subs16vector NVs16 I1 I2)
[procedure] (subu32vector NVu32 I1 I2)
[procedure] (subs32vector NVs32 I1 I2)
[procedure] (subu64vector NVu64 I1 I2)
[procedure] (subs64vector NVs64 I1 I2)
[procedure] (subf32vector NVf32 I1 I2)
[procedure] (subf64vector NVf64 I1 I2)
[procedure] (subc64vector NVc64 I1 I2)
[procedure] (subc128vector NVc128 I1 I2)
Operations on homogenous numeric vectors.
CRUNCH-specific primitives
(crunch c)
This module provides syntax to allow accessing C code. the forms in this module can also be used in CHICKEN code and expand uses into the corresponding macros of the (chicken foreign) module. Note that not all type-specifiers of CRUNCH have support in the CHICKEN FFI.
[syntax] (c-lambda ((ARGTYPE1 ARGNAME1) ...) RTYPE BODY ...)Defines a procedure that contains verbatim C code. RTYPE and ARGTYPE should be valid type specifiers. BODY ... should be one or more strings containing C code that will be inserted in the compiled code as it is and can access the arguments via ARGNAME.
[syntax] (c-declare TEXT ...)Includes the strings TEXT ... verbatim into the generated code at toplevel.
[syntax] (c-include INCLUDE ...)Expands into (c-declare "#include INCLUDE" ...). INLCUDE may be a string or a symbol, in the latter case it will be inserted verbatim, in the former as a quoted string.
[syntax] (c-external [INCLUDE] NAME (ARGTYPE ...) RTYPE)Declares the external procedure NAME with the given argument and result types. The optional INCLUDE can name an include-file that is needed to access NAME and may be either a string or a symbol. If the former, then it is included as #include "INCLUDE", if the latter it is included as #include INCLUDE.
[syntax] (c-value TYPE STRING)Expands into the inline C expression given by STRING with the result being of the given type.
(crunch process-context)
This module provides alternative implementations for some operations from (scheme process-context) that return values of types that CRUNCH supports. The module also provides a CHICKEN implementation to make switching between CHICKEN and CRUNCH easier.
[procedure] (command-line)Similar to R7RS' command-line, but returns a vector instead of a list.
[procedure] (get-environment-variable S1 #!optional S2)Returns a string. If the variable S1 is currently not defined in the environment S2 is returned and defaults to the empty string.
For example, this program can be compiled and run with both CHICKEN and CRUNCH:
(module main (main)
(import (scheme base) (scheme write)
(crunch process-context))
(define (main)
(display "Hello, I'm ")
(display (vector-ref (command-line) 0))
(newline))
(cond-expand ((not crunched) (main)) (else)) )
(crunch memory)
[procedure] (pointer-ref P)[procedure] (pointer-set! P X)
Access the value pointer to by the pointer P, which must be of a qualified pointer type (pointer T).
[procedure] (pointer-char-ref P)[procedure] (pointer-char-set! P C)
Character-specific pointer dereferencing. Unless UNICODE is disabled, these operations decode and encode a UTF-8 character.
[procedure] (pointer->string Pc)Decode the zero-terminated UNICODE string at Pc, which must be of the type (pointer char). If UNICODE support is disabled, this operations simply extracts the zero-terminated byte sequence.
[procedure] (string->pointer S)Returns a value of type (pointer char) that points to the start of the character sequence in the argument string. The pointer is only valid until the current procedure returns.
(crunch aggregate-types)
This module provides syntax for enum, struct and union definitions.
[syntax] (define-enum ENUMNAME (VALUENAME ...))Defines an enumeration type. Values of the type can be accessed by the syntax (ENUMNAME VALUENAME). Value names are associated with integer values starting from zero. VALUENAME may also be (VALUENAME VALUE) in the define-enum form to override the current value index.
[syntax] (define-struct STRUCT (SLOTSPEC TYPE) ...)[syntax] (define-union UNION (SLOTSPEC TYPE) ...)
Define C structure and union types. A struct or union definition will be emitted in the generated C code and the type will be internally registered in the compiler to be used later with define-compound-accessors, if desired.
SLOTSPEC should be a symbol naming the slot or a list of the form (SLOTNAME COUNT) to specify that the slot is an array of COUNT items.
[syntax] (declare-struct STRUCT (SLOTSPEC TYPE) ...)[syntax] (declare-union UNION (SLOTSPEC TYPE) ...)
Define C structure and union types. This is similar to define-struct/define-union but only registers the type internally, not emitting any C level definition.
STRUCT/UNION may optionally be of the form (typename NAME) to declare a typedef that represents a struct or union.
[syntax] (define-compound-accessors STRUCT (CTOR ARGUMENT ...) SLOTSPEC ...)Defines procedures that construct and access instances of a struct or union previously declared with define-struct, define-union, declare-struct or dexclare-union.
CTOR names the constructor taking the given arguments when initializing a structure or union instance. The arguments must match the slot names, omitted slots are uninitialized.
STRUCT may be one of the forms (struct NAME), (union NAME), (typename NAME) or (pointer S) (with S being one of the former). If pointer is given, then the generated constructor will allocate a structure or union instance via malloc(3), otherwise the constructor will create and return a structure instance by value. A plain symbol is interpreted as (typename S).
Each SLOTSPEC should be a list of 2 or 3 elements (SLOTNAME GETTER [SETTER]) where GETTER and SETTER (if given) designate getter and setter procedures to read or write the respective slot value:
(GETTER INSTANCE [INDEX]) => VALUE (SETTER INSTANCE [INDEX] VALUE)
INDEX is required for slots that are declared to have multiple elements.
To release the storage allocated for pointer-struct/union constructors use the free primitive from the (chicken memory) module.
Example:
(import (scheme base) (scheme write) (crunch memory) (chicken memory) (crunch aggregate-types)) (define-struct point (x float) (y float)) ; => struct point {float x, y;} ;; by value (define-compound-accessors (struct point) (make-point x y) (x get-x set-x) (y get-y set-y)) ;; by reference, use "free" to release (define-compound-accessors (pointer (struct point)) (alloc-point x y) (x get-x* set-x*) (y get-y*)) (define (main) (let ((x (make-point 123 4.5)) (y (alloc-point 2 1.23))) (write (vector (get-x x) (get-y x) (get-x* y) (get-y* y))) (newline) (set! x (pointer-ref y)) (write (vector (get-x x) (get-y x) (get-x* y) (get-y* y))) (newline) (free y))) ; writes: ; #f64(123 4.5 2 1.23) ; #f64(2 1.23 2 1.23)
(crunch declarations)
This module contains some defining forms for declaring types and extends the set of available primitives.
[syntax] (: [safe] NAME TYPE)[syntax] (: [safe] (NAME ARGTYPE ...) RTYPE)
Declares the type of a variable, which is sometimes required for embedded code as the call-sites originate from non-crunched code making argument types unprecise. The second form is an abbreviation for
(: NAME (procedure (ARGTYPE ...) RTYPE))
If the symbol safe precedes the first part of the declaration, any generated Scheme wrappers will be generated in a way to allow callbacks into Scheme from the called procedure. Specifically as a wrapper for a non-safe procedure will be created using foreign-lambda, when safe is given, foreign-safe-lambda will be used.
Since the implementation and use of the remaining macros in this module is still under development, you are invited to consult the crunch.compiler.primitives.scm source file in the CRUNCH distribution for inspiration.
The Debugger
To help debugging the compiler, a graphical interface was implemented to show an annotated version of the internal node tree. The tool may also be helpful for investigating type errors in code compiled with CRUNCH.
When the debug option is given, no code will be generated and a window is shown that displays the node tree and source code, either after optimization (if the compilation was successful) or after the last failed compiler pass.
The window is separated in three subwindows: the node tree is shown on the left, moving the mouse cursor over the text of a node will display the result type (or better: type variable) of the node in the field above and highlight the code in green. If the type variable has an associated "value set" (the set of possible values), it is shown below the type. A node that has an unknown type (usually indicating an error during type checking) is highlighted in red.
When a node is highlighted and the node has source code information, then the line of code is shown in the top right window. The filename is shown in the combobox above the source view and the shown file can be changed by selecting another source file, if more than one file has been compiled. Moving the mouse cursor over a source line will highlight the node in the node tree if an association can be found.
Note that source information may be incomplete, because macro expansion and optimization transformations often rewrite the code considerably making it difficult to identify the related location in the source code.
Hovering with the mouse cursor over a lambda id (<f...>) will show some information about that procedure in the source pane on the upper right.
The lower right side holds a log of compiler messages, errors and other explanatory messages.
Clicking with the right mouse button on a word separated by whitespace or delimiters will search for the next occurrence of the word under the cursor in any of the three text panes.
When using the crunch procedure from the (crunch compiler) module, every invocation with the debug option will start a separate subprocess showing the window. To exit the debugger, close the window or press the Esc key.
To understand the code in the node tree, it will be helpful to point out a few specifics:
- "%idlambda" nodes are the internal form of "lambda" expressions and have an associated lambda-ID shown before the parameter list.
- "%idcall" is a call to a known call target, the "%idlambda" with the identical ID.
- Variables are sometimes shown as "<external name>/<internal name>", the internal name is used to distinguish variables of the same name but with separate identities.
- Quoted symbols correlate to lambda IDs, not symbols as such.
The debugger is implemented in Tcl/Tk and has been tested with Tcl/Tk 8.6. It assumes the wish executable is in your path. To override the name of the executable, set the environment variable CRUNCH_WISH to the name of the Tcl/Tk interpreter you want to use.
The three text panes understand the default key bindings for Tcl/Tk text widgets.
Interoperability with C
Scheme code compiled with CRUNCH and C code are fully interoperable, there are no hidden mechanisms that constrain control flow and the compiled code uses the C stack just like normal C code. C code can call exported Scheme functions using their exported (and possibly mangled name). The only special case is that Scheme procedures that take optional arguments have an explicit first argument of type int that holds the number of arguments passed (excluding the argument count).
All data exchanged between Scheme and C code are either scalar and have the same meaning and value range in both languages or are reference counted structured objects (vectors, strings ands ports). They can be manipulated from C by using the macros and functions in the header file crunch.h, consult this file if you want to know more.
Configuring and customizing the generated C code
The compilation can be tweaked by defining a number of macros when translating the generated C code to binary form.
The runtime library with the implementation of primitives is contained in crunch.h. Defining one or more of the following macros will enable or disable certain features depending on your need.
- CRUNCH_NO_UTF
- disables UTF-8 handling in crunch.h and assumes all strings contain 8-bit characters; character literals are assumed to be in ASCII or an 8 bit encoding.
- CRUNCH_DEBUG
- enables some more runtime checks, in particular a memory leak checker which increases overhead but shows memory leask for reference-counted data on normal program exit.
- CRUNCH_FREESTANDING
- disables use of ANSI stdio files in crunch.h and includes the user-supplied include file crunch-environment.h. This mode is currently untested and likely to be incomplete.
- NDEBUG
- disables most runtime checks like array and string bound checks, etc.
UNICODE-aware support code in contained in the compiled object file crunch-utf.o which is installed along with CRUNCH. If you use procedures from the (scheme char) standard module or if you want standard string operations to handle UTF-8, you will have to link this C module with the generated code.
Examples
Here is a slightly longer example, to demonstrate interfacing with natrive libraries: A port of the core_basic_screen_manager example from the https://www.raylib.com/raylib website.
;; raylib [core] examples - basic screen manager ;; ;; NOTE: This example illustrates a very simple screen manager based on a states machines ;; Example originally created with raylib 4.0, last time updated with raylib 4.0 ;; ;; Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, ;; BSD-like license that allows static linking with closed source software ;; ;; Copyright (c) 2021-2024 Ramon Santamaria (@raysan5) ;; ;; ported to CRUNCH by Felix L. Winkelmann ;; ;; compile and link like this (required libraries may vary according to ;; platform): ;; ;; $ chicken-crunch core_basic_screen_manager.scm -o a.c ;; $ cc $(chicken-crunch -cflags) -lraylib -lm -ldl -lGl -lX11 (module main (main) (import (scheme base)) (import (chicken syntax)) ; for er-macro-transformer (import (crunch c) ; for c-external, c-lambda (crunch memory) ; for string->pointer (crunch aggregate-types)) ; for define-enum (import (except miscmacros define-enum)) ; for select, inc! (c-include "raylib.h") ; include raylib header ;; Bindings to raylib functions (define InitWindow (c-external InitWindow (integer integer (pointer char)) void)) (define SetTargetFPS (c-external SetTargetFPS (integer) void)) (define WindowShouldClose (c-external WindowShouldClose () boolean)) (define IsKeyPressed (c-external IsKeyPressed (integer) boolean)) (define IsGestureDetected (c-external IsGestureDetected (integer) boolean)) (define BeginDrawing (c-external BeginDrawing () boolean)) (define EndDrawing (c-external EndDrawing () boolean)) (define ClearBackground (c-external ClearBackground (integer) boolean)) (define CloseWindow (c-external CloseWindow () void)) (define DrawText (c-external DrawText ((pointer char) integer integer integer (typename Color)) void)) (define DrawRectangle (c-external DrawRectangle (integer integer integer integer (typename Color)) void)) (define-enum game-screen (LOGO TITLE GAMEPLAY ENDING)) ;; macros for easy access to constant values (define-syntax get-constant (er-macro-transformer (lambda (x r c) `(,(r 'c-value) integer ,(cadr x))))) (define-syntax get-color (er-macro-transformer (lambda (x r c) `(,(r 'c-value) (typename Color) ,(cadr x))))) ;; convert CRUNCH strings to char-pointers (define (str s) (string->pointer s)) ;; main program (define (main) (let ((screen-width 800) (screen-height 450) (current-screen (game-screen LOGO)) (frames-counter 0) ; Useful to count frames (KEY_ENTER (get-constant "KEY_ENTER")) (GESTURE_TAP (get-constant "GESTURE_TAP"))) (InitWindow screen-width screen-height (str "raylib [core] example - basic screen manager")) (SetTargetFPS 60) ; Set desired framerate (frames-per-second) (do () ((WindowShouldClose) ; Detect window close button or ESC key (CloseWindow)) ;; Update (select current-screen (((game-screen LOGO)) (inc! frames-counter) ; Count frames ;; Wait for 2 seconds (120 frames) before jumping to TITLE screen (when (> frames-counter 120) (set! current-screen (game-screen TITLE)))) (((game-screen TITLE)) ;; Press enter to change to GAMEPLAY screen (when (or (IsKeyPressed KEY_ENTER) (IsGestureDetected GESTURE_TAP)) (set! current-screen (game-screen GAMEPLAY)))) (((game-screen GAMEPLAY)) ;; Press enter to change to ENDING screen (when (or (IsKeyPressed KEY_ENTER) (IsGestureDetected GESTURE_TAP)) (set! current-screen (game-screen ENDING)))) (((game-screen ENDING)) ;; Press enter to return to TITLE screen (when (or (IsKeyPressed KEY_ENTER) (IsGestureDetected GESTURE_TAP)) (set! current-screen (game-screen TITLE))))) ;; Draw (BeginDrawing) (ClearBackground (get-color "RAYWHITE")) (select current-screen (((game-screen LOGO)) (DrawText (str "LOGO SCREEN") 20 20 40 (get-color "LIGHTGRAY")) (DrawText (str "WAIT for 2 SECONDS...") 290 220 20 (get-color "GRAY"))) (((game-screen TITLE)) (DrawRectangle 0 0 screen-width screen-height (get-color "GREEN")) (DrawText (str "TITLE SCREEN") 20 20 40 (get-color "DARKGREEN")) (DrawText (str "PRESS ENTER or TAP to JUMP to GAMEPLAY SCREEN") 120 220 20 (get-color "DARKGREEN"))) (((game-screen GAMEPLAY)) (DrawRectangle 0 0 screen-width screen-height (get-color "PURPLE")) (DrawText (str "GAMEPLAY SCREEN") 20 20 40 (get-color "MAROON")) (DrawText (str "PRESS ENTER or TAP to JUMP to ENDING SCREEN") 130 220 20 (get-color "MAROON"))) (((game-screen ENDING)) (DrawRectangle 0 0 screen-width screen-height (get-color "BLUE")) (DrawText (str "ENDING SCREEN") 20 20 40 (get-color "DARKBLUE")) (DrawText (str "PRESS ENTER or TAP to RETURN to TITLE SCREEN") 120 220 20 (get-color "DARKBLUE")))) (EndDrawing)))) )
Changelog
- 0.9 Added "c-value" to (crunch c) module; directly exporting procedure definitions that refer to c-externals creates wrapper
- 0.8 Fixed missing object file in import-test; handle uncalled inline- or overloaded primitives in value position more gracefully
- 0.7 Fix dependency problem in egg file; better mark c-lambda/c-external function-ids in debugger
- 0.6 Allow use of (crunch c) module from CHICKEN; added missing implementation of "expt" for integer arguments, fixed primitive declaration; respected change of "c-lambda" in (crunch aggregate-types) module; allow defaults for optional args; fixed tagging of "c-lambda" function IDs in debugger
- 0.5 Changed position of return-type in "c-lambda" form and make it work in expression position; added primitive for internal "##sys#eqv?"; ensure initial meta environment is populated before expansion
- 0.4 Compatibility fixes, adapted file-inclusion to recent changes in core
- 0.3 Added missing test-dependency (miscmacros); fixed primitive mapping of "command-line" and totally broken implementation (reported by Ben Jacob)
- 0.2 fixed linkage of crunch-utf.o in egg, removed warning in crunch.h
- 0.1 Initial release
License
Copyright (c) 2024, Felix L. Winkelmann 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. Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICESLOSS OF USE, DATA, OR PROFITSOR 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.