Define my own read macro - lisp

There are some read macros in Common Lisp such as ' #' #P, but how can I write a read macro?
Like this:
#T"hello world"
====================>
(gettext "hello world")

You can use set-macro-character and set-dispatch-macro-character, for example after:
(set-dispatch-macro-character #\# #\T
(lambda (s c n)
`(gettext ,(read s t nil t))))
==> T
you can use the installed read syntax
(read-from-string "#T\"this is a test\"")
==> (GETTEXT "this is a test")

Related

Evaluating a macro when form is read from a string

I have a macro with-voice:
(defmacro with-voice (tag body)
`(format nil "<span class=\"~a\">~%~a~%</span>" ',tag ,body))
which emits some text surrounded by a tag with some class. I know there are great libraries like CL-WHO - but I just need something tiny...
CL-USER> (with-voice narrator "foo")
"<span class=\"NARRATOR\">
foo
</span>"
which is is the desired outcome
I would like to be able to do this from a string
(let ((s (read-from-string "(with-voice narrator \"foo\")")))
(print (eval s)))
This works:
CL-USER> (let ((s (read-from-string "(with-voice narrator \"foo\")")))
(print (eval s)))
"<span class=\"NARRATOR\">
foo
</span>"
"<span class=\"NARRATOR\">
foo
</span>
but it has the dreaded eval. I've tried to get this working using macros and lambdas, but I can't get it working.
I'd be grateful for some help
thanks!
You could funcall the symbol you expect in a certain position of the read form:
(destructuring-bind (operator class text)
(read-from-string "(with-voice narrator \"foo\")")
(funcall operator class text))
Or even just expect those forms to be funcallable:
(apply #'funcall (read-from-string "(with-voice narrator \"foo\")"))
If you have more different shapes of data, you might want to match those, e. g. using optima, or using dispatch by eql specializer. This can also help with validation if the read input might contain harmful intent.

How to tell `make-module-evaluator` to use a custom reader like #lang does?

I'm trying to execute a custom made #lang on a given string (not in a file). Let's call it broccoli.
Setup
My lang is defined as so:
broccoli/main.rkt
(module reader racket/base
(require broccoli/private/reader)
(provide read read-syntax)) ; basically a reprovide
broccoli/private/reader.rkt
(provide
(rename-out
[my-read read]
[my-read-syntax read-syntax]))
(define (my-read in)
(syntax->datum
(my-read-syntax #f in)))
(define (my-read-syntax src in)
(with-syntax ([parse-tree (parse src (make-tokenizer in src))]) ; brag stuff
(strip-context
#'(module program broccoli/private/expander
parse-tree))))
broccoli/private/expander.rkt
(provide
(rename-out [module-begin #%module-begin]))
(define-syntax-rule (module-begin expr)
(#%module-begin
(provide meal)
(define meal (transform 'expr)))) ; some kind of computation
It works fairly well used the classic way :
#lang broccoli
Hello world!
will produce:
(module program broccoli/private/expander
(program
(sentence (word "Hello") (word "world"))))
which will then expand into:
(provide meal)
(define meal (list 42 38)) ; the result is for the sake of the example, don't mind it
But I'm trying to apply it to an arbitrary string I get from a network request, and send back the result.
And this time, it gets more complicated.
Here's what I tried:
Try #1
(define text "Hello world!")
(define evaluator (make-evaluator 'broccoli text)) ; Error: no #%module-begin found
(evaluator 'meal)
Try #2
(define text "Hello world!")
(define module (broccoli-read-syntax #f (open-input-string text)))
(define evaluator (make-module-evaluator #:language 'broccoli/private/expander module))
(evaluator 'meal) ; Error: meal undefined
Try #3
(define text "Hello world!")
(define evaluator (make-module-evaluator (string-append "#lang broccoli " text)))
(evaluator 'meal) ; Error: meal undefined
Try #4 (it works but it's not what I want)
(define text "Hello world!")
(define module (broccoli-read-syntax #f (open-input-string text)))
(define ns (make-base-namespace))
(eval module ns)
(namespace-require ''program ns)
(define result (eval 'meal ns))
This last result works correctly, but it doesn't use a sandbox, and uses eval directly.
I'm sure there's a better way, but I don't get what's going wrong.
I was sooo close!
I had to require the generated module inside the evaluator (which implies providing #%app, #%top, #%top-interaction, require and quote from the expander).
(define evaluator (make-module-evaluator (string-append "#lang broccoli\n" text)))
(evaluator '(require 'program)) ; missing step
(evaluator 'meal)
What I find weird in this solution is that it doesn't behave the same way the documentation says:
> (define base-module-eval
(make-module-evaluator '(module m racket/base
(define (f) later)
(define later 5))))
> (base-module-eval 'later)
5
I suggest using read-lang-module to turn the string into a syntax object representing a module. And then use make-module-evaluator.

Which is the easiest way to extend a Lisp with a small correction in the evaluation?

I would like to try extending some Lisp (Scheme, Racket, Clojure, any) to run external commands as follows:
; having
(define foo ...)
(define bar ...)
; on command
(ls (foo bar) baz)
; this lisp should evaluate (foo bar) as usual, with result "foobar", then
(ls foobar baz)
; here "ls" is not defined
; instead of rising "undefined identifier" exception
; it must look for "ls" command in the directories
; in the "PATH" environment variable
; and launch the first found "ls" command
; with strings "foobar" and "baz" on input
I just want to run it anyhow, without carrying about correct conversion from lisp's data structures to strings or handling the exit code and the output of the command in stdout/stderr.
I think there is no way to extend it within normal environment (like catching the "undefined" exception all the time). The eval procedure of the interpreter itself must be changed.
Which Lisp is the best to extend it like this and how is it done? Maybe there already exists a project performing something similar?
Common Lisp has a standard error system which may be used to implement that.
In Common Lisp implementations which provide a use-value or store-value restart for errors of type undefined-function.
Example
CL-USER 69 > (flet ((call-use-value-restart (c)
(use-value (lambda (arg)
(format t "~%dummy function with arg ~a~%" arg))
c)))
(handler-bind ((undefined-function #'call-use-value-restart))
(this-function-does-not-exist "foo")))
dummy function with arg foo
NIL
In the above example the function this-function-does-not-exist does not exist. As you can see, the error is handled and another function is called instead, which then does some output.
If we call the undefined function on its own, we get an error:
CL-USER 70 > (this-function-does-not-exist "foo")
Error: Undefined operator THIS-FUNCTION-DOES-NOT-EXIST in form (THIS-FUNCTION-DOES-NOT-EXIST "foo").
1 (continue) Try invoking THIS-FUNCTION-DOES-NOT-EXIST again.
2 Return some values from the form (THIS-FUNCTION-DOES-NOT-EXIST "foo").
3 Try invoking something other than THIS-FUNCTION-DOES-NOT-EXIST with the same arguments.
4 Set the symbol-function of THIS-FUNCTION-DOES-NOT-EXIST to another function.
5 Set the macro-function of THIS-FUNCTION-DOES-NOT-EXIST to another function.
6 (abort) Return to top loop level 0.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.
CL-USER 71 : 1 >
Our example basically calls the restart number 3 programmatically:
It binds a handler which calls the function call-use-value-restart when an error of type undefined-function happens.
The function call-use-value-restart then calls the use-value restart with a function it provides. Here you could provide a function which calls an external program of the name given by (cell-error-name c). The use-value restart then just calls the provided function and keeps on executing the program as usual.
Hint for a solution
Typically one would write a small top-level loop where such a handler is provided.
Another way to call the restart
In this example we use a hook to add a handler in case an error happens. Here we use the global variable *debugger-hook*. This should be a function and in our case it calls a new function when the condition c is of type undefined-function.
* (defun provide-a-function-hook (c hook)
(declare (ignore hook))
(typecase c
(undefined-function (use-value (lambda (arg)
(format t "~%dummy function with arg ~a~%" arg))
c))))
PROVIDE-A-FUNCTION-HOOK
* (setf *debugger-hook* #'provide-a-function-hook)
#<FUNCTION PROVIDE-A-FUNCTION-HOOK>
* (this-function-does-not-exist "foo")
; in: THIS-FUNCTION-DOES-NOT-EXIST "foo"
; (THIS-FUNCTION-DOES-NOT-EXIST "foo")
;
; caught STYLE-WARNING:
; undefined function: THIS-FUNCTION-DOES-NOT-EXIST
;
; compilation unit finished
; Undefined function:
; THIS-FUNCTION-DOES-NOT-EXIST
; caught 1 STYLE-WARNING condition
dummy function with arg foo
NIL
In racket you may override #%top:
#lang racket
(provide
(combine-out
(except-out (all-from-out racket) #%top)
(rename-out [shell-curry #%top])))
(require racket/system)
(define (stringify a)
(~a (if (cmd? a) (cmd-name a) a)))
(struct cmd (name proc)
#:property prop:procedure
(struct-field-index proc)
#:transparent
#:methods gen:custom-write
[(define (write-proc x port mode)
(display (string-append "#<cmd:" (stringify x) ">") port))])
(define (shell name)
(define (cmd-proxy . args)
(define cmd
(string-join (map stringify (cons name args))
" "))
(system cmd))
cmd-proxy)
(define-syntax shell-curry
(syntax-rules ()
((_ . id)
(cmd 'id (shell 'id)))))
Save this as shell.rkt and make this runner.rkt in the same directory:
#lang s-exp "shell.rkt"
(define test (list /bin/ls /usr/bin/file))
(second test) ; ==> #<cmd:/usr/bin/file>
(first test) ; ==> #<cmd:/bin/ls>
((second test) (first test))
; ==> t (prints that /bin/ls is an executable on my system)
Now from here to make it a #lang myshell or something like that is pretty easy.

How to read user input in Lisp

I'm very new to Lisp and am trying to write a program that simply asks a user to enter 3 numbers and then sums them and prints the output.
I've read that you can you a function like:
(defvar a)
(setq a (read))
To set a variable in Lisp, but when I try to compile my code using LispWorks I get the following error:
End of file while reading stream #<Concatenated Stream, Streams = ()>
I feel like this should be relatively simple and have no idea where I'm going wrong.
I've not worked with LispWorks, so it's only a guess.
When compiler traverses your code it gets to the line (setq a (read)), it tries to read input, but there is no input stream while compiling, thus you get an error.
Write a function:
(defvar a)
(defun my-function ()
(setq a (read))
It should work.
This should evaluate properly in your Lisp:
(defun read-3-numbers-&-format-sum ()
(flet ((prompt (string)
(format t "~&~a: " string)
(finish-output)
(read nil 'eof nil)))
(let ((x (prompt "first number"))
(y (prompt "second number"))
(z (prompt "third number")))
(format t "~&the sum of ~a, ~a, & ~a is:~%~%~a~%"
x y z (+ x y z)))))
Simply evaluate the above function definition, then run the form:
(read-3-numbers-&-format-sum)
at your LispWorks interpreter.

Lisp - string concat

I am trying to concat strings in lisp, using clisp on linux.
I run following code:
(defun bingo ()
(strcat "Correct! You guessed " (itoa *count*) " times."))
but, get following error:
EVAL: undefined function STRCAT
EVAL: undefined function ITOA
Any suggestion?
CL-USER 1 > (format nil "This is too easy. ~a day I'll learn Lisp." 1)
"This is too easy. 1 day I'll learn Lisp."