SkillAgentSearch skills...

Jmurmel

A standalone or embeddable JVM based interpreter/ compiler for Murmel, a single-namespace Lisp dialect inspired by Common Lisp

Install / Use

/learn @mayerrobert/Jmurmel

README

JMurmel

Java CI with Maven CodeQL

JMurmel is a lightweight Lisp-1-ish (mostly based on a small subset of Common Lisp with a side of Scheme) interpreter/ compiler compatible with Java8..24-ea that can be used standalone as well as embedded.

Currently weighing in at ~460kB (size of the compiled jmurmel.jar file containing the interpreter + compiler + runtime + REPL), or one single Java source file.

Murmel is the name of the programming language (which is a Lisp dialect), JMurmel is the name of the interpreter/ compiler that implements Murmel. For more details on the language see murmel-langref.md, and mlib.md for Murmel's default library.

The interpreter, REPL, compiler as well as Jars with compiled Murmel all run on top of the JVM. Compilation is done as Murmel to Java source which is then compiled to .class files using the JDK compiler. JMurmel as well as compiled Murmel programs should run on all platforms that support Java8+ (including CheerpJ).

Key features

JMurmel features an interpreter and a compiler, a REPL with a trace facility (trace and untrace function calls), full tail call optimization, lexical environments, a macro facility, backquote expansion including nested backquotes, JSR223 support, support for JFR (Java Flight Recorder)/ JMC (Java Mission Control), turtle- and bitmap graphics and garbage collection c/o JVM.

Goals, Priorities

Murmel is inspired by Common Lisp, i.e. when in doubt try to do it the Common Lisp way. It should be relatively easy to port a program from Murmel to Common Lisp, and Murmel knowledge should transfer to Common Lisp.

See Murmel vs Common Lisp for an overview of differences and similarities.

Status

At this time Murmel is a personal project, don't expect it to be an industrial strength Lisp in the near future.

Both the language Murmel as well as the interpreter/ compiler JMurmel currently are work in progress. There may be some incompatible language changes in the future. See also "Known Issues" in murmel-langref.md.

Copyright

Murmel and JMurmel are Copyright (C) 2020-2024 Robert Mayer.

This work is licensed under the terms of the MIT license. For a copy, see LICENSE.

Getting started

Online REPL

Use the Murmel Online REPL for a first glimpse.

Quickstart for Java 11+ users

Instead of cloning the repo and building the jarfile Java 11+ users can download LambdaJ.java and run

C:\> java Lambdaj.java

for a first peek at JMurmel (compiling Murmel won't work, though, only the interpreter works that way).

Optional: building from source

Pre-requisites: JDK8 or higher (8..24-ea is being tested), optionally: maven

Clone the repo and build using maven (this will use the maven wrapper included in the repo, a local maven installation is not required):

C:\> git clone https://github.com/mayerrobert/jmurmel.git
...
C:\> cd jmurmel
C:\jmurmel> mvnw package

The resulting jar will end up in lambda\target\jmurmel.jar

Quickstart using a precompiled version

Make sure you have Java 8+ installed (Java 8 is minimum, more modern versions are preferred).

java -version will tell (GNU/linux or Unix or Mac users please adjust as appropriate):

C:\> java -version
openjdk version "1.8.0_252"
OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_252-b09)
OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.252-b09, mixed mode)

With jmurmel.jar (and preferably mlib.lisp) in the current directory start JMurmel with the following command

C:\> java -jar jmurmel.jar

At the REPL prompt enter e.g. (write "Hello, World!") and hit ENTER, your screen should look like this:

D:\jmurmel\lambda\target>java -jar jmurmel.jar
Enter a Murmel form or :command (or enter :h for command help or :q to exit):

JMurmel> (write "Hello, World!")
"Hello, World!"
==> "Hello, World!"
JMurmel>

You just wrote and ran a Murmel program!

Now take it from there, e.g. read murmel-langref.md and mlib.md.
Or take a look at the example code in samples.murmel/ and samples.murmel-mlib/ Or type :h at the REPL prompt.
Or run java -jar jmurmel.jar --help.

Standalone use

$ java -jar jmurmel.jar
Enter a Murmel form or :command (or enter :h for command help or :q to exit):
JMurmel>

The command above will wait for you to enter an S-expression, interpret it and print it's result. Try e.g.

$ java -jar jmurmel.jar
Enter a Murmel form or :command (or enter :h for command help or :q to exit):
JMurmel> (+ 1 2)

==> 3.0
JMurmel>

JMurmel also can read program text from stdin:

Windows:

C:\> echo (write (quote Hello\,\ World!))| java -jar jmurmel.jar
|Hello, World!|
==> |Hello, World!|

C:\>

or Unix:

$ echo '(write (quote Hello\,\ World!))' | java -jar jmurmel.jar
|Hello, World!|
==> |Hello, World!|

$

The commands above will read an S-expression from stdin and interpret it.

Command line parameters in standalone mode:

  • --result ... print the result to stdout, this is the default when reading an S-expression from the console
  • --help ..... show all the available commandline parameters and quit

Installation

Currently there is no setup.exe or .msi or .rpm or .deb file. Just copy jmurmel.jar and (optional but recommended) mlib.lisp somewhere convenient, and maybe create a batchfile along these lines (Windows .cmd-style shown here):

jm.cmd:
--- snip ---
@echo off
setlocal
set JAVA_HOME=C:\Apps\Java\X64\jdk8u252-b09
set JMURMEL=C:\jmurmel\lambda\target\jmurmel.jar
%JAVA_HOME%\bin\java -jar %JMURMEL% %*
endlocal
--- snip ---

or setup an alias (again Windows style shown here):

set JAVA_HOME=c:\Program files\jdk-17.0.2
set JAVA_OPTS=-Xms200M -Xmx1G -Xss2m -XX:+UseZGC
doskey jm="%JAVA_HOME%\bin\java" %JAVA_OPTS% -jar C:\jmurmel\lambda\target\jmurmel.jar --libdir C:\jmurmel\samples.murmel-mlib $*

That way to run e.g. the file hanoi.lisp you can use the following command:

C:\> jm hanoi.lisp

or if you want to interpret a file and then go into REPL:

C:\> jm --repl hanoi.lisp

Or use JMurmel from within Emacs: add (setq inferior-lisp-program "jm --tty") to your .emacs file and do M-x (run-lisp) and use C-M-x to eval Murmel S-expressions.

Or see HACKING for how to use rlwrap to get a command history and TAB-completion at the JMurmel REPL.

Embedded use

JMurmel can also be used embedded in another Java program. JMurmel uses Java8 only, but should run on higher versions as well (Github CI builds and tests 8..24-ea). It comes as one self contained jar, no further dependencies needed.

Minimal "Hello, World!" example:

@Test
public void testMinimal() {
    Object result = new LambdaJ()
        .interpretExpression(new StringReader("(cons 'Hello\\,\\ World! nil)")::read, s -> {});
    assertEquals("(Hello, World!)", result.toString());
}

Slightly more advanced example:

@Test
public void testCons() {
    // run a Lisp program
    LambdaJ interpreter = new LambdaJ();
    StringBuffer program = new StringReader("(cons 'a 'b)")
    StringBuffer output = new StringBuffer();
    Object result = interpreter.interpretExpression(program::read, output::append);
    // done, that was it!
    
    // check results
    assertEquals("(a . b)", result.toString());
    assertEquals(0, out.length());

    assertTrue(result instanceof LambdaJ.ConsCell); // type of result depends on the Lisp program
                                                    // could be String, Double or ConsCell (i.e. list)
    LambdaJ.ConsCell list = (LambdaJ.ConsCell)result;

    String s = "";
    for (Object car: list) { // the iterator will return subsequent car
                             // and - for dotted lists - the cdr of the last cons cell
        s += car.toString();
    }
    assertEquals("ab", s);
}

Java calls JMurmel function:

@Test
public void testCallMurmelFromJava() {
    LambdaJ interp = new LambdaJ();
    interp.interpretExpression(new StringReader("(defun f (p1 p2) (* p1 p2))")::read, s -> {});

    MurmelFunction add = interp.getFunction("f");
    Object result = add.apply(2, 3);
    assertEquals(6.0, result);
}

See EmbeddedTest.java or FFITest.java for more embedded use examples including an example of how to hook up your own Lisp primitives written in Java.

Or see JSR223Test.java for an example on how to use JMurmel through the Java Scripting API (setting/ accessing Java objects via JSR223 from Murmel code is not supported yet.)

Or use jrunscript to use JMurmel via JSR223:

C:\robert\jmurmel>jrunscript -cp lambda\target\jmurmel.jar -l Murmel -f samples.murmel/fizzbuzz.lisp
1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, Fizz Buzz, 16, 17, Fizz, 19, Buzz, Fizz,
22, 23, Fizz, Buzz, 26, Fizz, 28, 29, Fizz Buzz, 31, 32, Fizz, 34, Buzz, Fizz, 37, 38, Fizz, Buzz, 41,
Fizz, 43, 44, Fizz Buzz, 46, 47, Fizz, 49, Buzz, Fizz, 52, 53, Fizz, Buzz, 56, Fizz, 58, 59, Fizz Buzz,
61, 62, Fizz, 6

Related Skills

View on GitHub
GitHub Stars25
CategoryDevelopment
Updated6mo ago
Forks1

Languages

Java

Security Score

87/100

Audited on Sep 17, 2025

No findings