Pass Expression as arguments to Clojure REPL - command-line

I have been learning Clojure and lately I have been using the REPL as a comand line calculator, my 'workflow' would be greatly improved if it were possible to pass arguments to the Clojure REPL and get the output, does anyone know how to do that?
Clarification: For example I would like to execute lein "(+ 2 2)" and have it return 4
~  lein "(+ 2 2)"
'(+ 2 2)' is not a task. See 'lein help'.

grenchman creates a repl, and each command line invocation gets a result from that repl, this is likely what you want.

lein (Leiningen) is the wrong tool for this, other than starting up a REPL. If you really want a command line interface to some Clojure program, that's possible too, but requires you to compile it to a jar and execute it, cf. this article on building CLI clojure apps.

Anything you def is available at the REPL.
=> (def ten 10)
...
=> (defn fact [n] (apply * (range 1 (inc n))))
...
=> (fact ten)
3628800
=>

# as bash variable
{ echo "$clj-expressions"; cat - ; } | lein repl
# as file
{ cat ./script.clj; cat - ; } | lein repl
Lucky for us, lein repl is just a plain-old unix process
The idea here is to send your commands to the repl's stdin but ensure that the current terminal's stdin is connected afterwards.
Thanks to Jonathan Leffler for this one. His answer here solved this.
To collect output, you can always spit something out as part of the script you run.

This is exactly how REPL works - you write some expression and press Enter, got expression result back.
→ lein repl
nREPL server started on port 59650 on host 127.0.0.1
REPL-y 0.3.0
Clojure 1.5.1
Docs: (doc function-name-here)
(find-doc "part-of-name-here")
Source: (source function-name-here)
Javadoc: (javadoc java-object-or-class-here)
Exit: Control+D or (exit) or (quit)
Results: Stored in vars *1, *2, *3, an exception in *e
user=> (+ 42 42)
84
user=>

Related

display executing name of the current running script

I have tried a few methods, one of them being:
(define (program) (find-system-path 'pref-file))
I have read from the documentation (after attempting the above code) and have noticed it is not what i'd need to use, obviously :) Any ideas?
Would also like to save this information to a variables. VBS example:
script = WScript.ScriptFullName
#lang racket
(display "Program name: ")
(displayln (find-system-path 'run-file))
Output (if saved as "test.rkt" and run with the command racket test.rkt):
Program name: test.rkt
From the racket console REPL, (find-system-path 'run-file) will return #<path:racket>. I didn't try it in DrRacket.

Racket: execute file and stay in interactive mode

Is there a way from a command line to run Racket file and stay in the interactive mode afterwards?
E.g. same in Python it would be:
python -i <file.py>
Assuming a foo.rkt that's this:
#lang racket
(provide x)
(define x 42)
(define y 4242)
Then you can use -i to specify interactive mode (= REPL), together with -t to require the file:
$ racket -it foo.rkt
Welcome to Racket vX.X.X.
> x
42
> y
y: undefined; ...
> (exit)
Note that y is not bound since it's in the module and not provided out. More likely you want a REPL that is "inside" the foo module, which can be done using enter! to go into the module's namespace, either in the REPL:
$ racket
> (enter! "foo.rkt")
> x
42
> y
4242
> (exit)
or on the command-line, using -e (and also -i to request a REPL):
$ racket -i -e '(enter! "foo.rkt")'
Welcome to Racket vX.X.X.
> x
42
> (+ x 12)
54
> (exit)
xrepl
If you do this a lot, you'll probably like xrepl. In your ~/.racketrc simply add:
(require xrepl)
Now the example becomes:
$ racket
Welcome to Racket vX.X.X.
-> ,en foo.rkt
42
"foo.rkt"> x
42
"foo.rkt"> (+ x 12)
54
"foo.rkt"> ,ex
Aside from ,en, XREPL has a bunch of goodness -- like the prompt indication of the module you're currently in, as well as a bunch of other useful commands:
$ racket
Welcome to Racket vX.X.X.
-> ,h
; Available commands:
; help (h ?): display available commands
; exit (quit ex): exit racket
; cd: change the current directory
; pwd: display the current directory
; shell (sh ls cp mv rm md rd git svn): run a shell command
; edit (e): edit files in your $EDITOR
; drracket (dr drr): edit files in DrRacket
; apropos (ap): look for a binding
; describe (desc id): describe a (bound) identifier
; doc: browse the racket documentation
; require (req r): require a module
; require-reloadable (reqr rr): require a module, make it reloadable
; enter (en): require a module and go into its namespace
; toplevel (top): go back to the toplevel
; load (ld): load a file
; backtrace (bt): see a backtrace of the last exception
; time: time an expression
; trace (tr): trace a function
; untrace (untr): untrace a function
; errortrace (errt inst): errortrace instrumentation control
; profile (prof): profiler control
; execution-counts: execution counts
; coverage (cover): coverage information via a sandbox
; switch-namespace (switch): switch to a different repl namespace
; syntax (stx st): set syntax object to inspect, and control it
; check-requires (ckreq): check the `require's of a module
; log: control log output
; install!: install xrepl in your Racket init file
Emacs
However if you're an Emacs user you might prefer using something like:
Geiser
Quack minor mode for scheme-mode
racket-mode (shameless self-promotion)
If you are using Visual Studio Code as an editor, you may want to use the "Code Runner extension"
make sure it's installed from the vs code marketplace
then enter Preferences: Open Settings (JSON) and past the following:
"code-runner.executorMap": {
"racket": "(exit); racket -i -e '(enter! \"$fileName\")'",
},
You will be able to run directly your file by clicking the Run Code icon or by pressing Ctrl+Alt+N
NB: the same manouvre goes for "scheme" since it's interpreted by racket as well, however putting #lang racket in the top of your file is necessary
It can be done with
racket -if <file.rkt>
However it will not work as expected if the file starts with
#lang racket

emacs lisp will not start

I am trying to setup the slime mode in emacs for using common lisp. When I attemp to start slime with M-x slime I get an error message saying:
process inferior-lisp not running.
So, I checked the value of the variable inferior-lisp-program which turned out to be "/opt/sbcl/bin/sbcl". sbcl is an acronym for an implementation of common lisp known as steel bank common lisp. Note that this variable is defined in file slime.el. As I do not have sbcl (the previous directory does not even exist on my machine) installed on my machine (which runs os x 10.8.3) this will not work.
I have the clisp implementation which is located in the directory: /opt/local/bin/. I tried to change the value of the variable inferior-lisp-program by:
(setq inferior-lisp-program '/opt/local/bin/clisp/)
However, this did not work and I do not know what else to try.
How can I get inferior-lisp to run and hence get slime to work?
EDIT: Here is some extra information I believe that could be helpful. If I try to just start common lisp in emacs by executing M-x run-lisp I get the following output from emacs:
(progn (load "/Users/s2s2/.emacs.d/slime/swank-loader.lisp" :verbose t) (funcall \
(read-from-string "swank-loader:init")) (funcall (read-from-string "swank:start-s\
erver") "/var/folders/wf/yjgymt8j14v2tqwjnny68wq00000gn/T/slime.28222"))
Can't exec program: /opt/sbcl/bin/sbcl
Process inferior-lisp exited abnormally with code 1
Can't exec program: /opt/sbcl/bin/sbcl
Process inferior-lisp exited abnormally with code 1
Hope this helps! All help is greatly appreciated!
The variable slime-lisp-implementations has higher priority than inferior-lisp-program for slime if set; try this instead (adjust parameters accordingly):
(setq slime-lisp-implementations
'((clisp ("/opt/local/bin/clisp" "-q -I"))
(sbcl ("/usr/local/bin/sbcl") :coding-system utf-8-unix)))
The first thing to try is to run the command in a regular shell window - just type or copy and paste the executable path there and see what bash tells you:
$ sbcl < /dev/null
bash: sbcl: command not found
$ clisp < /dev/null
<<clisp splash screen>>
$ which clisp
/usr/bin/clisp
Once you find out what the correct executable is, you set inferior-lisp to it:
(setq inferior-lisp "/usr/bin/clisp")
Notes:
It should be a string, not a symbol, so you need the quotes ".
It should point to a file, not a directory, so your trailing slash / is wrong

Evaluate emacs lisp expression on command line

I'm a newbie to emacs. I'm working with emacs-24.1 on redhat linux, and trying to evaluate an elisp expression. What I want emacs to do is to evaluate the elisp expression without launching emacs itself. I'm trying different things
emacs --eval '(+ 2 3)'
I do not know if emacs is evaluating the expression, but the result is not shown on console and emacs window comes up. Next I tried this
emacsclient --eval '(+ 2 3)'
Emacs client is expecting a server. It could not find the server and hence throwing an error (can't find socket. start server etc). So I launched a server (server-name is SERVER) and ran emacsclient again
emacsclient --server-file=SERVER -e '(+ 2 3)'
This time, emacs evaluated the expression and printed the result on console. That is because emacs is using the existing server to evaluate the expression. Now I get a problem when the server is not running.
emacsclient --server-file=ANOTHER_SERVER -e '(+ 2 3)' -a emacs
This time, I'm not getting any error on console. Emacs is launching a new window, because of -a (my .emacs has (server-start) command in it and server-name set to ANOTHER_SERVER). But emacs then is trying to edit the file (+ 2 3). It is shown on the mode line. I'm confused. emacsclient --help showed me this
-e, --eval Evaluate the FILE arguments as ELisp expressions
and emacs manual says this.
'-e'
'--eval'
Tell Emacs to evaluate some Emacs Lisp code, instead of visiting some files.
When this option is given, the arguments to emacsclient are interpreted as a
list of expressions to evaluate, not as a list of files to visit.
I do not know how to proceed on this. As I said, my goal is to evaluate an elisp expression without launching emacs. Is it possible?
After a bit of testing it looks like you can use --batch to have emacs dump any messages to stderr. Then you can call message to print things to stderr where you'll be able to see them. Your example would become emacs --batch --eval '(message (number-to-string (+ 2 3)))' and the result would be printed to stderr.
If you're trying to redirect the output to a file you'll need to redirect stderr instead of stdout by using 2> instead of just >.
Try
emacs --batch --eval '(print (+ 2 3))'

Flymake specify location of Makefile

I'm trying to feed flymake output from Haxe compiler, but I don't know how to tell it where the make file lives (ideally, I'd use nxml file instead). So far I have this in the Makefile:
BIN = ./bin
MAIN = com.wunderwafer.Main
SWF = wunderwafer.swf
SWFSETTINGS = -debug -swf-version 10 -swf-header 800:600:31
HFLAGS = -main $(MAIN) $(SWFSETTINGS) -cp ./src -swf $(BIN)/$(SWF)
HC = haxe
default: compile
compile: $(HC) $(HFLAGS)
clean:
$(RM) -r $(BIN)/*
.PHONY: check-syntax
check-syntax:
$(HC) $(HFLAGS)
If I run it later like so:
$ make -k check-syntax
It produces the expected output. However flymake isn't able to find the Makefile (or so it seems) because the files I'm trying to check are deeper inside the src directory.
What is the way to configure flymake so it knows where the makefile is? (or, even better, just execute a shell command, because the common way to compile Haxe code is by using *.nxml settings file.
EDIT:
It looks like I'm getting closer, lots of thanks, but flymake is doing something strange, and I can't understand what exactly it does, so, here's the log:
received 65 byte(s) of output from process 967
file /home/wvxvw/projects/wafer/src/com/wunderwafer/map/Battlefield.hx, init=haxe-flymake-init
parsed 'Error : Invalid class name /home/wvxvw/projects/wafer/build.nxml', no line-err-info
file /home/wvxvw/projects/wafer/src/com/wunderwafer/map/Battlefield.hx, init=haxe-flymake-init
process 967 exited with code 1
cleaning up using haxe-flymake-cleanup
deleted file /tmp/flymake-Battlefield-855Cad.hx
Battlefield.hx: 0 error(s), 0 warning(s) in 0.15 second(s)
switched OFF Flymake mode for buffer Battlefield.hx due to fatal status CFGERR, warning Configuration error has occurred while running (haxe /home/wvxvw/projects/wafer/build.nxml)
The command I'm trying to make it run looks like this:
(defun haxe-flymake-get-cmdline (source base-dir)
"Gets the cmd line for running a flymake session in a Haxe buffer.
This gets called by flymake itself. The output is a list of two elements:
the command to run, and a list of arguments. The resulting command is like:
$ haxe ${project-root}/build.nxml
"
(message "base-dir %s" (file-name-as-directory base-dir))
(list *haxe-compiler*
(list
(concat (file-name-as-directory base-dir)
*build-nxml*))))
The message printed looks like this:
base-dir /home/wvxvw/projects/wafer/
So, as far as I could understand, the resulting command should be:
haxe /home/wvxvw/projects/wafer/build.nxml
But it looks like flymake either adds something in front of the argument or afterwards, which makes Haxe compiler generate the error "Error : Invalid class name" - this error would be given if there was one extra argument, which the compiler would have understood as an extra class to compile. But the log doesn't show what is being sent...
EDIT 2:
I've added:
#!/usr/bin/env bash
echo "$#" > /home/wvxvw/projects/wafer/log
And made flymake invoke this script instead of the compiler, and it passes only one argument, just as I would expect it... sigh
It's a good question. I don't know a simple way of adding in a new "flavor" of make tool into flymake. I know of a way, it's just not simple. This is what I did for php codesniffer - it will be similar for any arbitrary make tool.
First, define an install fn.
(defun fly/phpcs-install ()
"install flymake stuff for PHP CodeSniffer files."
(add-to-list
'flymake-err-line-patterns
(list fly/phpcs-error-pattern 1 2 3 4))
(let* ((key "\\.php\\'")
(phpentry (assoc key flymake-allowed-file-name-masks)))
(if phpentry
(setcdr phpentry '(fly/phpcs-init fly/phpcs-cleanup))
(add-to-list
'flymake-allowed-file-name-masks
(list key 'fly/phpcs-init 'fly/phpcs-cleanup)))))
This installs a new entry into the flymake alist, keyed on .php as a file extension. The entry in flymake's list basically relates the file extension to a pair of functions, one for init and one for cleanup.
The init fn simply returns the command to run to check syntax. This can be a shell command, with the appropriate arguments. For codesniffer this fn looks like this:
(defun fly/phpcs-init ()
"initialize flymake for PHP using the PHP CodeSniffer tool."
(let ((create-temp-f 'fly/phpcs-create-temp-intemp)
(use-relative-base-dir t)
(use-relative-source t)
(get-cmdline-f 'fly/phpcs-get-cmdline)
args
temp-source-file-name)
(setq temp-source-file-name (flymake-init-create-temp-buffer-copy create-temp-f)
args (flymake-get-syntax-check-program-args
temp-source-file-name "."
use-relative-base-dir use-relative-source
get-cmdline-f))
args))
Yikes! Down the rabbit hole we go. The get-cmdline fn looks like this:
(defun fly/phpcs-get-cmdline (source base-dir)
"Gets the cmd line for running a flymake session in a PHP buffer.
This gets called by flymake itself. The output is a list of two elements:
the command to run, and a list of arguments. The resulting command is like:
php.exe -d auto_append_file="" -d auto_prepend_file="" phpcs\scripts\phpcs --report=emacs file.php
"
(list fly/phpcs-phpexe
(list
"-d" "auto_append_file=''"
"-d" "auto_prepend_file=''"
(concat (file-name-as-directory fly/phpcs-phpcs-dir)
"scripts\\phpcs")
(concat "--standard=" fly/phpcs-standard)
"--report=emacs"
"-s" ;; show the fullname of the rule being violated
(expand-file-name source))))
You can see the full elisp at http://www.emacswiki.org/emacs/flyphpcs.el
There's probably a simpler way. I just don't know it.