A modular C/C++ parsing and FFI binding generation toolkit for Scheme and Common Lisp.
C Tools provides independently reusable components for working with C and C++ code:
- Lexer - Tokenize C/C++ source with trivia preservation
- Parser - Build ASTs from declarations
- Preprocessor - Handle macros, includes, conditionals
- FFI Generator - Generate FFI bindings for Chez, Racket, Guile, Chicken, Gambit Scheme, and Common Lisp CFFI
- Effects System - Composable effect handlers for extensibility
Example uses may include:
- Generate FFI bindings for C/C++ libraries
- Build syntax highlighters or formatters
- Create code analysis tools
- Parse headers without full preprocessing
- Generate documentation from headers
Generate FFI bindings from C/C++ headers:
# Chez Scheme FFI (default)
./ffi.ss mylib.h -l mylib -o bindings.sls
# Racket FFI
./ffi.ss mylib.h --racket -o bindings.rkt
# Guile FFI
./ffi.ss mylib.h --guile -o bindings.scm
# Chicken Scheme FFI
./ffi.ss mylib.h --chicken -o bindings.scm
# Gambit Scheme FFI
./ffi.ss mylib.h --gambit -o bindings.scm
# Common Lisp CFFI
./ffi.ss mylib.h --cffi -o bindings.lisp
# C++ with name mangling (Scheme targets only)
./ffi.ss myclass.hpp -x c++ --guile -o bindings.scmAdd to your Scheme library path:
export CHEZSCHEMELIBDIRS="/path/to/c-tools/lib:${CHEZSCHEMELIBDIRS}"- Functions with automatic type mapping
- Structs (full definitions or opaque pointers)
- Enums as constants
- Typedefs
- Namespaces (fully supported)
- Classes as opaque pointer types
- Methods with automatic Itanium C++ ABI name mangling
- Template specializations
- Generic templates gracefully skipped
- Chez:
foreign-procedure,define-ftypefor structs - Racket:
ffi/unsafe,_funtypes,define-ffihelpers - Guile:
foreign-library-function, struct layouts as lists - Chicken:
foreign-lambda,foreign-declarefor headers - Gambit:
c-lambda,c-declarefor inline C
Chez Scheme (--chez, default):
(library (ffi mylib)
(export c-add c-multiply)
(import (chezscheme))
(define c-add
(foreign-procedure "mylib" "add"
(int int) int)))Racket (--racket):
#lang racket/base
(require ffi/unsafe ffi/unsafe/define)
(provide c-add c-multiply)
(define lib (ffi-lib "mylib"))
(define-ffi-definer define-ffi lib)
(define-ffi c-add (_fun _int _int -> _int) #:c-id "add")Guile (--guile):
(define-module (ffi mylib)
#:use-module (system foreign)
#:use-module (system foreign-library)
#:export (c-add c-multiply))
(define-foreign-library libmylib
(dynamic-link "mylib"))
(define c-add
(foreign-library-function libmylib "add"
#:return-type int
#:arg-types (list int int)))Chicken Scheme (--chicken):
(module mylib (c-add c-multiply)
(import scheme chicken foreign)
(foreign-declare "#include <mylib.h>")
(define c-add
(foreign-lambda int "add" int int)))Gambit Scheme (--gambit):
;; Gambit FFI bindings for mylib
(c-declare "#include <mylib.h>")
(define c-add
(c-lambda (int int) int "add"))(import (c-tools lexer c)
(c-tools parser c))
(let* ([tokens (tokenize-file "simple.h")]
[decls (parse-declarations tokens)])
decls)(import (c-tools preprocess c)
(c-tools parser c)
(c-tools codegen chez ffi)
(c-tools effects files)
(c-tools effects cpp core))
(with-file-system #f "."
(lambda ()
(with-effects '((cpp-include ())
cpp-macros
cpp-conditional)
(lambda ()
(let* ([tokens (preprocess-file "header.h")]
[decls (parse-declarations tokens)]
[ffi (generate-ffi-code decls "libname")])
ffi)))))(import (c-tools lexer c))
(call-with-values
(lambda ()
(call-with-input-file "header.h"
(lambda (port)
(tokenize-with-diagnostics port "header.h"))))
(lambda (cst-nodes diagnostics)
(for-each display-diagnostic diagnostics)
cst-nodes))- C++ constructors/destructors skipped (requires C wrapper functions)
- Generic templates skipped (only specializations supported)
- Virtual functions work but require proper object layout
- Operator overloading not yet supported
- C++ support requires Itanium ABI (GCC/Clang/etc). For MSVC, use a C wrapper or clang-cl.
The library is written in (mostly) R6RS Scheme with minimal implementation-specific dependencies:
- Core libraries: Pure R6RS (lexer, parser, AST, effects system)
- Utility library: R6RS-compatible implementations of
format,box,make-parameter, etc. - Chez-specific: Only file I/O operations (
lib/c-tools/effects/files.sls)