how to make in lisp a reader macro to read raw strings (ie backslash is a literal not an escape character) - macros

In python you can write this r"c:\data"
How do I the same in lisp? ie I want a reader macro that has the same functionality so I no longer need to escape backslashes in windows paths. I do not want to use forward slashes as plenty of commands in windows dont understand them.

For instance
(defun read-literal-string (stream delimiter arg)
(declare (ignore arg))
(loop for char = (read-char stream nil stream)
when (eq char stream)
do (error "hit end of stream")
until (char= char delimiter)
collect char into chars
finally (return (coerce chars 'string))))
(set-dispatch-macro-character #\# #\" #'read-literal-string)
And now
> #"fo\o"
"fo\\o"
> #"foo\"
"foo\\"
obviously a real version would be more carefully written

Related

What are limitations of reader macros in Common Lisp

I have my own Lisp interpreter in JavaScript that I work for some time now, and now I want to implement reader macros like in Common Lisp.
I've created Streams (almost working except for special symbols like ,# , ` ') but it freezes the browser for a few seconds when it's loading the page with included scripts (lisp files that have 400 lines of code). This is because my Streams are based on substring function. If I first split tokens and then use TokenStream that iterate over tokens, it works fine.
So my question is this, is string streams really something that is in Common Lisp? Can you add reader macros that create whole new syntax like Python inside CL, this simplify to question can I implement """ macro (not sure if you can have 3 characters as reader macro) or other character that will implement template literal inside lisp for instance:
(let ((foo 10) (bar 20))
{lorem ipsum ${baz} and ${foo}})
or
(let ((foo 10) (bar 20))
""lorem ipsum ${baz} and ${foo}"")
or
(let ((foo 10) (bar 20))
:"lorem ipsum ${baz} and ${foo}")
would yield string
"lorem ipsum 10 and 20"
is something like this possible in Common Lisp and how hard would be to implement #\{ or #\: as reader macro?
The only way I can think of to have template literals in Lisp is something like this:
(let ((foo 10) (bar 20))
(tag "lorem ipsum ${baz} and ${foo}")))
where tag is macro that return strings with ${} as free variable. Can reader macro also return lisp code that is evaluated?
And another question can you implement reader macros like this:
(list :foo:bar)
(list foo:bar)
where : is reader macro and if it's before symbols it convert symbol to
foo.bar
and if it's inside it throw error. I'm asking this because with token based macros :foo:bar and foo:bar will be symbols and will not be processed by my reader macros.
and one more question can reader macro be put in one line and second line use it? This will definitely be only possible with string streams and from what I've tested not possible with interpreter written in JavaScript.
There are some limitations in the sense that it is pretty hard to, for instance, intervene in the interpretation of tokens in any way short of 'implement your own token interpreter from scratch'. But, well, you could if you wanted to do just that: the problem is that your code would need to deal with numbers & things as well as the existing code does and things like floating-point parsing are pretty fiddly to get right.
But the macro functions associated with macro characters get the stream that is being read, and they are free to read as much or as little of the stream as they like, and return any kind of object (or no object, which is how comments are implemented).
I would strongly recommend reading chapters 2 & 23 of the hyperspec, and then playing with an implementation. When you play with the implementation be aware that it is just astonishingly easy to completely wedge things by mucking around with the reader. At the minimum I would suggest code like this:
(defparameter *my-readtable* (copy-readtable nil))
;;; Now muck around with *my-readtable*, *not* the default readtable
;;;
(defun experimentally-read ((&key (stream *standard-input*)
(readtable *my-raedtable*)))
(let ((*readtable* readtable))
(read stream)))
This gives you at least some chance to recover from catastrophe: if you can once abort experimentally-read then you are back in a position where *readtable* is something sensible.
Here is a fairly useless example which shows how much you can subvert the syntax with macro characters: a macro character definition which will cause ( ...) to be read as a string. This may not be fully debugged, and as I say I can see no use for it.
(defun mindless-parenthesized-string-reader (stream open-paren)
;; Cause parenthesized groups to be read as strings:
;; - (a b) -> "a b"
;; - (a (b c) d) -> "a (b c) d"
;; - (a \) b) -> "a ) b"
;; This serves no useful purpose that I can see. Escapes (with #\))
;; and nested parens are dealt with.
;;
;; Real Programmers would write this with LOOP, but that was too
;; hard for me. This may well not be completely right.
(declare (ignore open-paren))
(labels ((collect-it (escaping depth accum)
(let ((char (read-char stream t nil t)))
(if escaping
(collect-it nil depth (cons char accum))
(case char
((#\\)
(collect-it t depth accum))
((#\()
(collect-it nil (1+ depth) (cons char accum)))
((#\))
(if (zerop depth)
(coerce (nreverse accum) 'string)
(collect-it nil (1- depth) (cons char accum))))
(otherwise
(collect-it nil depth (cons char accum))))))))
(collect-it nil 0 '())))
(defvar *my-readtable* (copy-readtable nil))
(set-macro-character #\( #'mindless-parenthesized-string-reader
nil *my-readtable*)
(defun test-my-rt (&optional (stream *standard-input*))
(let ((*readtable* *my-readtable*))
(read stream)))
And now
> (test-my-rt)
12
12
> (test-my-rt)
x
x
> (test-my-rt)
(a string (with some parens) and \) and the end)
"a string (with some parens) and ) and the end"

How to get code point of a character in elisp (and other way too)?

I was very surprised not to be able to find this in the elisp manual or SO. I just want the equivalent of many languages' chr() and ord() or similar: convert between actual characters and their (unicode) code point values.
Emacs Lisp: getting ascii value of character explains that to elisp, a char just is its code-point. But what if I need the representation of that char~int as a series of ASCII decimal digits?
For example, if I wanted to generate in a buffer, a readable table showing the equivalences?
Thanks!
As you've already noted, characters are integers.
(eq ?A 65)
For example, if I wanted to generate in a buffer
Either of the following inserts the character A into the buffer:
(insert ?A)
(insert 65)
If you need to deal with strings, characters can be converted to strings:
(char-to-string ?A)
(char-to-string 65)
(format "%c" 65)
"A"
vs
(number-to-string 65)
(format "%d" 65)
"65"

Read from a file into a Emacs lisp list

I have the following file data.txt
A
B
C
D
I would like to read the contents of this file into a Lisp list, like
(defun read-list-from-file (fn)
(interactive)
(list "A" "B" "C" "D"))
(defun my-read ()
(interactive)
(let (( mylist (read-list-from-file "data.txt")))
(print mylist t)))
How can I modify read-list-from-file such that it returns the same list, but instead reads from the file given as input argument fn.. Each line in the file should be a separate item in the list..
This code:
(with-current-buffer
(find-file-noselect "~/data.txt")
(split-string
(save-restriction
(widen)
(buffer-substring-no-properties
(point-min)
(point-max)))
"\n" t))
UPD:
Here's a version with insert-file-contents:
(defun slurp (f)
(with-temp-buffer
(insert-file-contents f)
(buffer-substring-no-properties
(point-min)
(point-max))))
(split-string
(slurp "~/data.txt") "\n" t)
Much easier, than creating a temporary buffer, is to use f file manipulation library's f-read function that returns textual content of a file (default coding UTF-8).
f is a third-party that you need to install from MELPA, read Xah Lee's tutorial on how to use Emacs package management system. Then you could use either split-string or s string manipulation library's s-split (which is a simple wrapper around the former function):
(s-split "\n" (f-read "~/data.txt") t) ; ("A" "B" "C" "D")
Note: third parameter to s-split set to t omits empty strings. You could use s-lines, but as textual files on *nix systems usually contain a trailing newline, the returned list would be ("A" "B" "C" "D" ""). You can remove the last element with dash list manipulation library's butlast function, but this works in O(n), so probably stick to s-split, unless you want empty lines to be preserved in the list.

how to print special char in emacs lisp

For example, one line of code in my function
(message "char %c:%d" character count)
will print the counts for each character. For nonprintable chars, such as newline and tab, I want the output looks like:
\n:4
\t:6
instead of printing a newline and tab literally. how can I do that?
You can achieve some of this by let-binding certain variables before printing.
`print-escape-newlines' is a variable defined in `C source code'.
Its value is nil
Documentation:
Non-nil means print newlines in strings as `\n'.
Also print formfeeds as `\f'.
There's also:
print-escape-nonascii
Non-nil means print unibyte non-ASCII chars in strings as \OOO.
print-escape-multibyte
Non-nil means print multibyte characters in strings as \xXXXX.
These all work with prin1, so you can use the %S code in format. e.g.:
(let ((print-escape-newlines t))
(format "%S" "new\nline"))
As suggested by #wvxvw
(defun escaped-print (c)
(if (and (< c ?z)
(> c ?A))
(string c)
(substring (shell-command-to-string (format "printf \"%%q\" \"%s\"" (string c)))
2 -1)))
The substring part is to cut out extra stuff from printf's output. I don't have a great deal of knowledge about this command, so it might not be flawless.
There may be some code somewhere in emacs that can do this for you, but one way would be to write a function that converts the special characters to a string:
(defun print-char(c)
(case c
(?\n "\\n")
(?\t "\\t")
(t (string c))))
Note that you need to use string format rather than character because you're actually writing multiple characters for each special character.

lisp decoding?

how to decode a binary stream in lisp
i did with with-open -file and passing a argument as element-type '(unsigned byte 8) but returning as numbers not a string
please help me on this problem
;;; Flexi-Streams "bivalent streams" solve the binary vs. character stream problem.
;;; You'll want to install QuickLisp and understand the REPL * and ** variables:
(require 'flexi-streams) ;; or (ql:quickload 'flexi-streams)
(with-open-file (out "foo.text" :direction :output)
(write-line "Foo" out)) ; "Foo"
(with-open-file (in "foo.text")
(read-line in)) ; "Foo", NIL
(with-open-file (in "foo.text" :element-type '(unsigned-byte 8))
(read-line in)) ;; read-line wrong stream type error
(with-open-file (in "foo.text" :element-type '(unsigned-byte 8))
(let ((s (make-array 3)))
(read-sequence s in) s)) ; #(70 111 111)
(map 'list #'code-char *) ; (#\F #\o #\o)
(map 'string #'code-char **) ; "Foo"
(with-open-file (raw "foo.text" :element-type 'flexi-streams:octet)
(with-open-stream (in (flexi-streams:make-flexi-stream raw))
(read-line in))) ; "Foo", NIL
;; Thanks to Edi Weitz for providing this essential tool.
Your problem is not a problem, I think. When you open a file in binary mode as unsigned-byte 8, you are specifying to read the file, 8 bits as a time, represented as a number from 0 to 255. Depending on how you read it, you might get it as an ARRAY or a LIST.
A 'text' file is a set of numbers using the ASCII representation of characters. For more sophisticated text, Unicode representation is used, but that is closer to a traditional binary format than a text one.
If you attempt to read a PDF file, you will have to follow the file format to gain meaningful data from it. Wotsit's site has a library of file formats.
From your question, it sounds as if you are just learning programming. I don't recommend working with PDFs when you are just learning.
The question is a bit unclear. I think your problem is that you have created a file that you have written one (or more) elements of type (unsigned-byte 8), but when you try to read it you are getting characters, not binary values.
If that is the case, you will need to open the file with :element-type '(unsigned-byte 8).
If I have misunderstood what you want, please edit your question and I shall try to answer your question.
The short answer is that you don't need to specify the :element-type if you want to read in strings.
The type '(unsigned-byte 8) refers to a number, not to chars as in C. In Lisp, character is an actual datatype and you would need to open the file with this element type to get strings. The important thing to realize is that :element-type determines what data type elements in the file will be parsed into and returned as. If you read the hyperspec page on open you see that element-type has to either be a subtype of character, integer, or be unsigned-byte or signed-byte. The default, however, is character, which produces strings in whatever format your lisp uses.