A standalone nREPL/prepl client written in Go and heavily inspired by Grenchman

Trenchman

release test Go Report Card

A standalone nREPL/prepl client written in Go, heavily inspired by Grenchman

Trenchman is a standalone nREPL/prepl client, which means that it can be used as an ordinary REPL without having to make it cooperate with an editor or any other development tool. Unlike ordinary Clojure REPLs, it starts up instantly as it just connects to a running nREPL/prepl server, eliminating the overhead of launching a JVM process and bootstrapping Clojure for every startup.

Features

  • Fast startup
  • Written in Go and runs on various platforms
  • Support for nREPL and prepl
  • Works as a language-agnostic nREPL client

Table of Contents

Installation

Homebrew (macOS and Linux)

To install Trenchman via Homebrew, run the following command:

$ brew install athos/tap/trenchman

To upgrade:

$ brew upgrade trenchman

Manual Install

Pre-built binaries are available for linux, macOS and Windows on the releases page.

If you have the Go tool chain installed, you can build and install Trenchman by the following command:

$ go install github.com/athos/trenchman/cmd/trench@latest

Trenchman does not have readline support at this time. If you want to use features like line editing or command history, we recommend using rlwrap together with Trenchman.

Usage

usage: trench [<flags>] [<args>...]

Flags:
      --help                    Show context-sensitive help (also try --help-long and --help-man).
  -p, --port=PORT               Connect to the specified port.
      --port-file=FILE          Specify port file that specifies port to connect to. Defaults to .nrepl-port.
  -P, --protocol=nrepl          Use the specified protocol. Possible values: n[repl], p[repl]. Defaults to nrepl.
  -s, --server=[(nrepl|prepl)://]host[:port]
                                Connect to the specified URL (e.g. prepl://127.0.0.1:5555).
      --retry-timeout=DURATION  Timeout after which retries are aborted. By default, Trenchman never retries connection.
      --retry-interval=1s       Interval between retries when connecting to the server.
  -i, --init=FILE               Load a file before execution.
  -e, --eval=EXPR               Evaluate an expression.
  -f, --file=FILE               Evaluate a file.
  -m, --main=NAMESPACE          Call the -main function for a namespace.
      --init-ns=NAMESPACE       Initialize REPL with the specified namespace. Defaults to "user".
  -C, --color=auto              When to use colors. Possible values: always, auto, none. Defaults to auto.
      --version                 Show application version.

Args:
  [<args>]  Arguments to pass to -main. These will be ignored unless -m is specified.

Connecting to a server

One way to connect to a running server using Trenchman is to specify the server URL with the -s (--server) option. For example, the following command lets you connect to an nREPL server listening on localhost:12345:

$ trench -s nrepl://localhost:12345

In addition to nREPL, Trenchman supports the prepl protocol as well. To connect to a server via prepl, use the prepl:// scheme instead of nrepl://:

$ trench -s prepl://localhost:5555

Also, the connecting port and protocol can be specified with dedicated options:

  • port: -p, --port=PORT
  • protocol: -P, --protocol=(nrepl|prepl)

If you omit the protocol or server host, Trenchman assumes that the following default values are specified:

  • protocol: nrepl
  • server host: 127.0.0.1

So, in order to connect to nrepl://127.0.0.1:12345, you only have to do:

$ trench -p 12345

rather than trench -s nrepl://127.0.0.1:12345.

If you omit the port number, Trenchman will read it from a port file, as described in the next section.

Port file

A port file is a file that only contains the port number that the server is listening on. Typical nREPL servers generate a port file named .nrepl-port at startup.

Trenchman tries to read the port number from a port file if the connecting port is not specified explicitly. By default, Trenchman will read .nrepl-port for nREPL connection and .prepl-port for prepl connection.

So, the following example connects to nrepl://127.0.0.1:12345:

$ cat .nrepl-port
12345
$ trench

If you'd rather use another file as a port file, specify it with the --port-file option:

$ cat my-port-file
3000
$ trench --port-file my-port-file

Retry on connection

When connecting to a server that is starting up, it's useful to be able to automatically retry the connection if it fails.

The --retry-timeout and --retry-interval options control connection retries. --retry-timeout DURATION specifies the amount of time before connection retries are aborted and --retry-interval DURATION specifies the time interval between each retry (DURATION can be specified in the format accepted by Go's duration parser, like 500ms, 10s or 1m).

For example, the following command will retry the connection every 5 seconds for up to 30 seconds:

$ trench --retry-timeout 30s --retry-interval 5s

If the connection fails after retrying the connection until the timeout, Trenchman will print the error and exit.

If --retry-timeout is not specified, Trenchman will not retry the connection.

Evaluation

By default, Trenchman starts a new REPL session after the connection is established:

$ trench
user=> (println "Hello, World!")
Hello, World!
nil
user=>

To exit the REPL session, type Ctrl-D or :repl/quit.

In addition to starting a REPL session, Trenchman provides three more evaluation modes (-e/-f/-m).

Evaluating an expression (-e)

If the -e option is specified with an expression, Trenchman evaluates that expression:

$ trench -e '(println "Hello, World!")'
Hello, World!
$

Trenchman will print the evaluation result if the given expression evaluates to a non-nil value:

$ trench -e '(map inc [1 2 3])'
(2 3 4)
$

Evaluating a file (-f)

With the -f option, you can load (evaluate) the specified file:

$ cat hello.clj
(println "Hello, World!")
$ trench -f hello.clj
Hello, World!
$

Note that the specified file path is interpreted as one from the client's working directory. The client will send the entire content of the file to the server once the connection is established.

If - is specified as the input file, the input code will be read from stdin:

$ echo '(println "Hello, World!")' | trench -f -
Hello, World!
$

Calling -main for a namespace (-m)

With the -m option, you can call the -main function for the specified namespace:

$ cat src/hello/core.clj
(ns hello.core)

(defn -main []
  (println "Hello, World!"))
$ trench -m hello.core
Hello, World!

Note that the file for the specified namespace must be on the server-side classpath.

License

Copyright (c) 2021 Shogo Ohta

Distributed under the MIT License. See LICENSE for details.

Owner
Shogo Ohta
Clojure(Script) dev
Shogo Ohta
Comments
  • `--debug` option for printing debug info

    `--debug` option for printing debug info

    Resolves #9.

    After this being merged, Trenchman prints debug info (i.e. messages communicated between the client and server) with the —debug option:

    $ trench --debug
    [DEBUG:SEND] map["id":"init" "op":"clone"]
    [DEBUG:RECV] map["id":"init" "new-session":"cee88e31-b813-4fd4-8fc2-33fa085c7a0c" "session":"none" "status":["done"]]
    [DEBUG:SEND] map["op":"describe"]
    [DEBUG:RECV] map["id":"unknown" "ops":map["clone":map[] "close":map[] "complete":map[] "describe":map[] "eldoc":map[] "eval":map[] "info":map[] "load-file":map[] "lookup":map[] "ls-sessions":map[]] "session":"none" "status":["done"] "versions":map["babashka":"0.8.156" "babashka.nrepl":"0.0.6-SNAPSHOT"]]
    user=> (map inc [1 2 3])
    [DEBUG:SEND] map["code":"(map inc [1 2 3])" "id":"a524995d-a048-4b43-869a-db23185c90c4" "ns":"user" "op":"eval" "session":"cee88e31-b813-4fd4-8fc2-33fa085c7a0c"]
    [DEBUG:RECV] map["id":"a524995d-a048-4b43-869a-db23185c90c4" "ns":"user" "session":"cee88e31-b813-4fd4-8fc2-33fa085c7a0c" "value":"(2 3 4)"]
    [DEBUG:RECV] map["id":"a524995d-a048-4b43-869a-db23185c90c4" "session":"cee88e31-b813-4fd4-8fc2-33fa085c7a0c" "status":["done"]]
    (2 3 4)
    user=>
    
  • Support nrepl+unix connection

    Support nrepl+unix connection

    nrepl/nrepl 0.9.0 added support for connections via UNIX domain sockets.

    This PR adds support for this feature. Once this is merged, Trenchman can connect to a server listening on a UNIX domain socket /foo/bar.socket as follows:

    trenchman -s nrepl+unix:/foo/bar.socket
    
  • Retry on connection

    Retry on connection

    Resolves #2.

    This PR adds the --retry-timeout DURATION and --retry-interval DURATION options to control connection retry. For example, the following command will retry the connection every 5 seconds for up to 30 seconds:

    $ trench --retry-timeout 30s --retry-interval 5s
    

    This change breaks the library-level compatibility for the nrepl and prepl clients. Before the change, to create an nREPL client, you needed to do something like the following:

    client := nrepl.NewClient(&Opts{Host: "localhost", Port: 12345, ...})
    

    After the change, you need to do something like this:

    client := nrepl.NewClient(&Opts{ConnBuilder: &client.TCPConnBuilder{Host: "localhost", Port: 12345}, ...})
    

    The same goes for the prepl client.

  • Pass command-line args to -main

    Pass command-line args to -main

    Currently, there is no way to pass command-line args to the -main function when -m is specified. This PR provides the way to do that:

    $ cat src/arith.clj
    (ns arith)
    
    (defn -main [op & args]
      (if-let [op' (case op "add" + "sub" - nil)]
        (let [args' (map read-string args)]
          (prn (apply op' args')))
        (throw (ex-info (str "Unknown op: " op) {}))))
    $ trench -m arith add 1 2
    3
    $ trench -m arith sub 3 1
    2
    $
    

    Also, -- can be used as a separator to distinguish arguments to pass to -main from the ones that should be passed directly to Trenchman:

    $ trench -m arith -- add 1 2
    
  • Delimiting prepl prelude messages with newline

    Delimiting prepl prelude messages with newline

    The current implementation of ClojureScript's standard prepl server cannot correctly accept multiple consecutive forms sent without delimiters in between, as shown below:

    (set! *print-namespace-maps* false)(require 'clojure.string)(in-ns 'clojure.string)
    

    This PR adds an extra newline to the end of the prepl prelude message (set! *print-namespace-maps* false) to work around the issue for some cases.

  • Add --init option to load file before execution

    Add --init option to load file before execution

    As with Clojure CLI's --init option:

    $ cat init.clj
    (defn fib [n]
      (loop [n n a 0 b 1]
        (if (= n 0)
          a
          (recur (dec n) b (+ a b)))))
    $ trench --init init.clj -e '(map fib (range 10))'
    (0 1 1 2 3 5 8 13 21 34)
    $
    
  • Option to specify initial REPL namespace

    Option to specify initial REPL namespace

    This PR adds the --init-ns option like the following:

    $ ./trench -s prepl://localhost:5555 --init-ns clojure.string
    clojure.string=> (upper-case "foo")
    "FOO"
    clojure.string=>
    

    Note that the prelude message for prepl must be:

    (require '<namespace>)
    (in-ns '<namespace>)
    

    instead of:

    (do (require '<namespace>) (in-ns '<namespace>))
    

    because ClojureScript does not accept the latter as a valid form.

  • Built-in readline support

    Built-in readline support

    Currently, Trenchman does not have built-in support for readline-like features, like line editing or history search. The installation instruction suggests using the tool with rlwrap, but for more usability, it would be nice to have built-in support for readline.

    Joker seems to use candid82/liner for readline, so it's worth giving it a try.

⛳ A minimal programming language inspired by Ink, JavaScript, and Python.

⛳ Golfcart My blog post: Creating the Golfcart Programming Language Getting Started Scope Rules Usage Building and tests Contributions License Golfcar

Sep 6, 2022
❄️ Elsa is a minimal, fast and secure runtime for JavaScript and TypeScript written in Go

Elsa Elsa is a minimal, fast and secure runtime for JavaScript and TypeScript written in Go, leveraging the power from QuickJS. Features URL based imp

Jan 7, 2023
Gentee - script programming language for automation. It uses VM and compiler written in Go (Golang).

Gentee script programming language Gentee is a free open source script programming language. The Gentee programming language is designed to create scr

Dec 15, 2022
A multi-pass compiler written in Go comprised of scanner, recursive-descent parser, generation of AST, intermediate representation (ILOC), and code generation (Armv8).

GoLite Project - Go Huskies! This is a project conducted and led in the course MPCS 51300 Compilers at the University of Chicago. In a group of two, w

Jan 10, 2022
Scriptable interpreter written in golang
Scriptable interpreter written in golang

Anko Anko is a scriptable interpreter written in Go. (Picture licensed under CC BY-SA 3.0, photo by Ocdp) Usage Example - Embedded package main impor

Dec 23, 2022
A POSIX-compliant AWK interpreter written in Go

GoAWK: an AWK interpreter written in Go AWK is a fascinating text-processing language, and somehow after reading the delightfully-terse The AWK Progra

Dec 31, 2022
A BASIC interpreter written in golang.
A BASIC interpreter written in golang.

05 PRINT "Index" 10 PRINT "GOBASIC!" 20 PRINT "Limitations" Arrays Line Numbers IF Statement DATA / READ Statements Builtin Functions Types 30 PRINT "

Dec 24, 2022
A basic Forth parser written in Go.

GoForth ======= I got really interested in Forth and thus I began making a parser, of sorts, in Go. Though I don't intend for it to catch on, it's st

Mar 1, 2022
A customisable virtual machine written in Go

== About GoLightly == GoLightly is a lightweight virtual machine library implemented in Go, designed for flexibility and reuse. Traditionally popular

Nov 16, 2022
A simple virtual machine - compiler & interpreter - written in golang

go.vm Installation Build without Go Modules (Go before 1.11) Build with Go Modules (Go 1.11 or higher) Usage Opcodes Notes The compiler The interprete

Dec 17, 2022
A dialect of Lisp extended to support concurrent programming, written in Go.

LispEx A dialect of Lisp extended to support concurrent programming. Overview LispEx is another Lisp Interpreter implemented with Go. The syntax, sema

Nov 22, 2022
Genetic Algorithms library written in Go / golang

Description Genetic Algorithms for Go/Golang Install $ go install git://github.com/thoj/go-galib.git Compiling examples: $ git clone git://github.com

Sep 27, 2022
Chip-8 emulator written in Go
Chip-8 emulator written in Go

Welcome to Chippy ?? Chippy is a CHIP-8 emulator that runs Chip-8 public domain roms. The Chip 8 actually never was a real system, but more like a vir

Dec 3, 2022
Mini lisp interpreter written in Go.

Mini Go Lisp Mini lisp interpreter written in Go. It is implemented with reference to the d-tsuji/SDLisp repository written in Java. Support System Fu

Nov 25, 2022
🏃 An x86-64 assembler written in Go.

asm An x86-64 assembler written in Go. It is used by the Q programming language for machine code generation. Architectures Linux x86-64 (ELF binaries)

Nov 7, 2022
A Lisp-dialect written in Go
A Lisp-dialect written in Go

Lispy ✏️ Intro Lispy is a programming language that is inspired by Scheme and Clojure. It's a simple Lisp-dialect I built to better understand Lisp an

Dec 8, 2022
Expr – a tiny stack-based virtual machine written in Go

Expr – a tiny stack-based virtual machine written in Go The executor is designed to interpret a simple expression language and it's useful in delegati

Nov 11, 2022
T# Programming Language. Something like Porth, Forth but written in Go. Stack-oriented programming language.

The T# Programming Language WARNING! THIS LANGUAGE IS A WORK IN PROGRESS! ANYTHING CAN CHANGE AT ANY MOMENT WITHOUT ANY NOTICE! Something like Forth a

Jun 29, 2022
interpreter for the basic language written in golang
interpreter for the basic language written in golang

jirachi interpreter for the basic language written in golang The plan supports the following functions: Arithmetic Operations (+, -, *, /, ^) Comparis

Sep 17, 2022