README
Cumalis Lisp
Cumalis Lisp is a stack-based implementation of R7RS Scheme Language in Typescript.
Can be used as a library in web browsers or Node.js environment.
Installation
$ yarn add cumalis-lisp
# or
$ npm install cumalis-lisp
Features
- Almost full implementation of R7RS (small) except complex/fraction numbers, including:
- call-with-current-continuation (call/cc).
- guard / with-exception-handler / raise / raise-continuable.
- dynamic-wind.
- define-library / import.
- make-parameter / parameterize.
- define-record-type.
- let-values / values.
- syntax-rules with nested patterns.
- quasiquote.
- nested multiline comments.
- datum tags.
- etc.
- Standard libraries in R7RS (small) except (scheme complex) are implemented.
- (scheme base) -- imported by default.
- (scheme read)
- (scheme write)
- (scheme promise)
- (scheme time)
- (scheme inexact)
- (scheme case-lambda)
- (scheme char)
- (scheme cxr)
- (scheme process-context)
- (scheme file)
- (scheme eval)
- (scheme repl)
- (scheme load)
- (scheme r5rs)
- Proper tail recursion. (tail call optimization)
- Javascript interfaces
- Adding built-in syntaxes, procedures, libraries.
- Able to write expressions as Javascript Arrays and evaluate.
- Able to contain Javascript objects in AST.
- All objects, AST, and call-frames consist of pure JSON objects.
- Continuations can be serialized to JSON strings. (Circular references need to be resolved.)
- Simple JSON serializer/deserializer utility is bundled. (toReferentialJSON / fromReferentialJSON)
- No dependency.
With these features, The following application fields can be considered.
- Macro system for online applications. (Programs can be built as simple Javascript objects.)
- Mobile agent systems. (Send running application via network and continue to run on another machine.)
- Games that need to save the running status.
- Work-flow systems.
- Backend of Web-based visual programming environment, like scratch-blocks or Blockly.
- Note: Cumalis Lisp is expected to be the backend of "Cumalis" in the next major version.
- etc.
Note: Implementations of Scheme are not required to implement fraction and complex numbers. See section "6.2.3. Implementation restrictions" on R7RS.
Web REPL & Sandbox
How to use as a module
Basic usage
import { Interpreter, toJS } from "cumalis-lisp";
const itrp = new Interpreter(); // Create interpreter.
const ret = itrp.eval(`
(define (fib n)
(if (<= n 2)
1
(+ (fib (- n 1)) (fib (- n 2)))))
(fib 10)
`); // Evaluate S-expression.
const num = toJS(ret); // returns 55.
Defining built-in procedures / built-in macros
import { Interpreter, is, create, toJS, fromJS, defineBuiltInProcedure } from "cumalis-lisp";
const itrp = new Interpreter(); // Create interpreter.
const helloProc = defineBuiltInProcedure("hello", [ // Define procedure
{ name: "obj" }
], function ({obj}) {
if (!is.Object(obj)) {
throw new Error("Not a object");
}
console.log(`Hello ${toJS(obj)}`);
return create.Number(42);
});
const hello2Proc = defineBuiltInProcedure("hello2", [ // Define macro
{ name: "obj" }
], function ({obj}) {
if (!is.Object(obj)) {
throw new Error("Not a object");
}
return fromJS(["string-append", `"HELLO "`, obj]); // Write LISP as JS array.
}, true); // <-- this "true" indicates macro.
itrp.setBuiltInProcedure(helloProc); // Set the procedure to the interpreter.
itrp.setBuiltInProcedure(hello2Proc); // Set the procedure to the interpreter.
console.log(toJS(itrp.eval(`(hello "world")`))); // => 42
console.log(toJS(itrp.eval(`(hello2 "WORLD")`))); // => HELLO WORLD
Suspend / serialize / deserialize / resume
import {
Interpreter, LISP, create, toJS,
SuspendEnvelope, isSuspendEnvelope, suspendValueFromEnvelope,
toReferentialJSON, fromReferentialJSON,
} from "cumalis-lisp";
const itrp = new Interpreter(); // Create interpreter.
// Suspend
let suspend: SuspendEnvelope | null = null;
try {
itrp.eval(`(+ 11 (suspend "SUSPEND HERE"))`);
} catch (e) {
if (isSuspendEnvelope(e)) {
suspend = e;
} else {
throw e;
}
}
if (suspend) {
console.log(toJS(suspendValueFromEnvelope(suspend))); // => "SUSPEND HERE"
// Serialize/Deserialize
const json = toReferentialJSON(suspend, "$