I am trying to parse some function to (let([x 1]) x) but racket read [ as (. Is there any simple way that I can keep it []?
this is what returns when I try to escape with backslash[ backslash]:
(let (|[| fact #f |]| |[| fact2 #f |]| |[| fact3 #f |]|) fact3)
what I want is:
, (let ([fact #f][fact2 #f][fact3 #f]) fact3)
If you are using the read-syntax primitive, then the parentheses within the parsed data structure have a paren-shape property that will tell you if they were square or not.
For example:
> (define stx-1 (read-syntax #f (open-input-string "(hello)")))
> (define stx-2 (read-syntax #f (open-input-string "[hello]")))
> stx-1
#<syntax::1 (hello)>
> stx-2
#<syntax::1 (hello)>
> (syntax-property stx-1 'paren-shape)
#f
> (syntax-property stx-2 'paren-shape)
#\[
So the syntax data structure can remember that the square brackets are there.
Related
I am trying to define a reader macro that reads the length of a string. The string will be enclosed in vertical bars (pipes). For example:
|yes| -> 3
|| -> 0
|The quick brown fox| -> 19
|\|| -> 1 — The pipe character can be escaped by preceding it with a backslash.
(let ((x |world|)) (+ |hello| x)) -> 10
I managed to write this:
#lang racket
(define (handle-pipe in [count 0])
(define cur-char (read-char in))
(cond [(eqv? cur-char #\|)
count]
[else
(when (and (eqv? cur-char #\\) ; Handle escape ("\|").
(eqv? (peek-char in) #\|))
(read-char in)) ; Consume |.
(handle-pipe in (+ count 1))]))
(parameterize ([current-readtable
(make-readtable (current-readtable)
#\|
'terminating-macro
(lambda (char in src-name line col pos)
(handle-pipe in)))])
(eval (read (open-input-string "(let ((x |world|)) (+ |hello| x))"))
(make-base-namespace)))
This returns 10, as expected.
The problem now is: I would like to use the reader macro directly in my Racket code instead of having to feed a string into (eval (read (open-input-string ...))). For example, I would like to use the reader macro like this:
#lang racket
(define (handle-pipe in [count 0])
(define cur-char (read-char in))
(cond [(eqv? cur-char #\|)
count]
[else
(when (and (eqv? cur-char #\\) ; Handle escape ("\|").
(eqv? (peek-char in) #\|))
(read-char in)) ; Consume |.
(handle-pipe in (+ count 1))]))
(current-readtable
(make-readtable (current-readtable)
#\|
'terminating-macro
(lambda (char in src-name line col pos)
(handle-pipe in))))
(let ((x |world|)) ; Using the reader macro directly in Racket.
(+ |hello| x))
However, there is an error message when I run the program above:
my-program.rkt:20:9: world: unbound identifier
in: world
location...:
my-program.rkt:20:9
context...:
do-raise-syntax-error
for-loop
[repeats 1 more time]
finish-bodys
lambda-clause-expander
for-loop
loop
[repeats 3 more times]
module-begin-k
expand-module16
expand-capturing-lifts
temp118_0
temp91_0
compile15
temp85_0
standard-module-name-resolver
What did I do wrong? How do I use the reader macro in my code?
That's not possible. The pipeline of compilation/evaluation in Racket is:
Read
Expand macros
Evaluate expanded program
Your configuration of current-readtable is done in step 3, so it cannot influence things that happened already in step 1.
The reason your first code works is that eval starts the pipeline again on the datum you provided it to.
Note that the reader macro is actually intended to be used when you create a new #lang. But since you want it to work with #lang racket, it's not applicable.
I'm trying to write a racket reader extension procedure that disables the special treatment of the pipe character |.
I have two files: mylang/lang/reader.rkt to implement the lang reader and mylang/testing.rkt to try it out. I've run raco pkg install --link to install the lang.
Here is reader.rkt:
#lang s-exp syntax/module-reader
racket
#:read my-read
#:read-syntax my-read-syntax
(define (parse-pipe char in srcloc src linum colnum)
#'\|)
(define my-readtable
(make-readtable #f #\| 'terminating-macro parse-pipe))
(define (my-read-syntax src in)
(parameterize ((current-readtable my-readtable))
(read-syntax src in)))
(define (my-read in)
(syntax->datum
(my-read-syntax #f in)))
With testing.rkt like this:
#lang mylang
(define | 3)
(+ 3 2)
runs and produces 5 as expected. But this next one doesn't:
#lang mylang
(define |+ 3)
(+ |+ 2)
complaining that define: bad syntax (multiple expressions after identifier) in: (define \| + 3) which is reasonable since parse-pipe produces a syntax object, not a string, so it terminates the reading of the symbol prematurely.
One thing I can do is keep reading until the end of the symbol, but that is hackish at best because I would be reimplementing symbol parsing and it wouldn't fix the case that the symbol has the pipe char in the middle, or if | is inside a string, etc.
What I would like to do is remove the default reader procedure for | from the readtable, but I don't know how/if it can be done.
OK, I've found a way. The documentation for make-readtable says:
char like-char readtable — causes char to be parsed in the same way
that like-char is parsed in readtable, where readtable can be #f to
indicate the default readtable.
so I can make the reader read | like a normal character like a with:
(define my-readtable
(make-readtable #f #\| #\a #f))
And it works
(define hawdy|+ "hello")
(string-append hawdy|+ "|world")
; => "hello|world"
I am generating emacs elisp code from a clojure function. I originally started off using a defmacro, but I realized since I'm going cross-platform and have to manually eval the code into the elisp environment anyway, I can just as easily use a standard clojure function. But basically what I'm doing is very macro-ish.
I am doing this because my goal is to create a DSL from which I will generate code in elisp, clojure/java, clojurescript/javascript, and maybe even haskell.
My "macro" looks like the following:
(defn vt-fun-3 []
(let [hlq "vt"]
(let [
f0 'list
f1 '(quote (defun vt-inc (n) (+ n 1)))
f2 '(quote (ert-deftest vt-inc-test () (should (= (vt-inc 7) 8))))]
`(~f0 ~f1 ~f2)
)))
This generates a list of two function definitions -- the generated elisp defun and a unit test:
(list (quote (defun vt-inc (n) (+ n 1))) (quote (ert-deftest vt-inc-test () (should (= (vt-inc 7) 8)))))
Then from an emacs scratch buffer, I utilize clomacs https://github.com/clojure-emacs/clomacs to import into the elisp environment:
(clomacs-defun vt-fun-3 casc-gen.core/vt-fun-3)
(progn
(eval (nth 0 (eval (read (vt-fun-3)))))
(eval (nth 1 (eval (read (vt-fun-3))))))
From here I can then run the function and the unit test:
(vt-inc 4)
--> 5
(ert "vt-inc-test")
--> t
Note: like all macros, the syntax quoting and escaping is very fragile. It took me a while to figure out the proper way to get it eval properly in elisp (the whole "(quote (list..)" prefix thing).
Anyway, as suggested by the presences of the "hlq" (high-level-qualifier) on the first "let", I want to prefix any generated symbols with this hlq instead of hard-coding it.
Unfortunately, when I use standard quotes and escapes on the "f1" for instance:
f1 '(quote (defun ~hlq -inc (n) (+ n 1)))
This generates:
(list (quote (defun (clojure.core/unquote hlq) -inc (n) (+ n 1)))
(quote (ert-deftest vt-inc-test () (should (= (vt-inc 7) 8)))))
In other words it substitutes 'clojure.core/unquote' for "~" which is not what I want.
The clojure syntax back-quote:
f1 `(quote (defun ~hlq -inc (n) (+ n 1)))
doesn't have this problem:
(list (quote (casc-gen.core/defun vt casc-gen.core/-inc (casc-gen.core/n) (clojure.core/+ casc-gen.core/n 1))) (quote (ert-deftest vt-inc-test () (should (= (vt-inc 7) 8)))))
It properly escapes and inserts "vt" as I want (I still have to work out to concat to the stem of the name, but I'm not worried about that).
Problem solved, right? Unfortunately syntax quote fully qualifies all the symbols, which I don't want since the code will be running under elisp.
Is there a way to turn off the qualifying of symbols when using the syntax quote (back tick)?
It also seems to me that the syntax quote is more "capable" than the standard quote. Is this true? Or can you, by trickery, always make the standard quote behave the same as the syntax quote? If you cannot turn off qualification with syntax quote, how could I get this working with the standard quote? Would I gain anything by trying to do this as a defmacro instead?
The worst case scenario is I have to run a regex on the generated elisp and manually remove any qualifications.
There is no way to "turn off" the qualifying of symbols when using syntax quote. You can do this however:
(let [hlq 'vt] `(~'quote (~'defun ~hlq ~'-inc (~'n) (~'+ ~'n 1))))
Which is admittedly pretty tedious. The equivalent without syntax quote is:
(let [hlq 'vt] (list 'quote (list 'defun hlq '-inc '(n) '(+ n 1))))
There is no way to get your desired output when using standard quote prefixing the entire form however.
As to the issue of using defmacro instead, as far as I understand your intentions, I don't think you would gain anything by using a macro.
Based on the input from justncon, here is my final solution. I had to do a little extra formatting to get the string concat on the function name right, but everything was pretty much like he recommended:
(defn vt-gen-4 []
(let [hlq 'vt]
(let [
f1 `(~'quote (~'defun ~(symbol (str hlq "-inc")) (~'n) (~'+ ~'n 1)))
f2 `(~'quote (~'defun ~(symbol (str hlq "-inc-test")) () (~'should (~'= (~(symbol (str hlq "-inc")) 7) 8))))
]
`(~'list ~f1 ~f2))))
What I learned:
syntax quote is the way to go, you just have to know how to control unquoting at the elemental level.
~' (tilde quote) is my friend here. Within a syntax quote expression, if you specify ~' before either a function or var it will be passed through to the caller as specified.
Take the expression (+ 1 1)
Here is a synopsis of how this expression will expand within a syntax quote expression based on various levels of escaping:
(defn vt-foo []
(println "(+ 1 1) -> " `(+ 1 1)) --> (clojure.core/+ 1 1)
(println "~(+ 1 1) -> " `~(+ 1 1)) --> 2
(println "~'(+ 1 1) -> " `~'(+ 1 1)) --> (+ 1 1)
)
The last line was what I wanted. The first line was what I was getting.
If you escape a function then do not escape any parameters you want escaped. For instance, here we want
to call the "str" function at macro expand time and to expand the variable "hlq" to it's value 'vt:
;; this works
f1 `(quote (defun ~(str hlq "-inc") ~hlq (n) (+ n 1)))
;; doesn't work if you escape the hlq:
f1 `(quote (defun ~(str ~hlq "-inc") ~hlq (n) (+ n 1)))
I guess an escape spans to everything in the unit your escaping. Typically you escape atoms (like strings or symbols), but if it's a list then everything in the list is automatically escaped as well, so don't double escape.
4) FWIW, I ended writing a regex solution before I got the final answer. It's definitely not as nice:
(defn vt-gen-3 []
(let [hlq "vt"]
(let
[
f0 'list
f1 `(quote (defun ~(symbol (str hlq "-inc")) (n) (+ n 1)))
f2 '(quote (ert-deftest vt-inc-test () (should (= (vt-inc 7) 8))))
]
`(~f0 ~f1 ~f2)
))
)
;; this strips out any qualifiers like "casc-gen.core/"
(defn vt-gen-3-regex []
(clojure.string/replace (str (vt-gen-3)) #"([\( ])([a-zA-Z0-9-\.]+\/)" "$1" ))
Macro expansion is very delicate and requires lots of practice.
I am trying to write a simple parser in Racket, using the parser-tools. I got a behaviour which I could not explain (I am a Racket newbie, perhaps that is trivial).
Consider the following code:
#lang racket
(require parser-tools/yacc
parser-tools/lex
(prefix-in : parser-tools/lex-sre))
(define-tokens value-tokens ;;token which have a value
(STRING-VALUE ))
(define-empty-tokens op-tokens ;;token without a values
(EOF))
(define-lex-abbrevs ;;abbreviation
[STRING (:+ (:or (:/ "a" "z") (:/ "A" "Z") (:/ "0" "9") "." "_" "-"))]
)
(define lex-token
(lexer
[(eof) 'EOF]
;; recursively call the lexer on the remaining input after a tab or space. Returning the "1+1")
;; result of that operation. This effectively skips all whitespace.
[(:or #\tab #\space #\newline)
(lex-token input-port)]
[(:seq STRING) (token-STRING-VALUE lexeme)]
))
(define test-parser
(parser
(start query)
(end EOF)
(tokens value-tokens op-tokens)
(error (λ(ok? name value) (printf "Couldn't parse: ~a\n" name)))
(grammar
(query [(STRING-VALUE) $1])
)))
(define s (open-input-string
"abcd123"))
(define res
(test-parser (lambda () (lex-token s))))
(define str "abcd123")
After those definitions, res is a string:
> (string? res)
#t
and so is str.
If I try to execute a comparison with the "abcd123" string I get two different results:
> (eq? res "abcd123")
#f
> (eq? str "abcd123")
#t
Why is that? What am I missing here?
You should compare strings with equal?.
Like many programming languages there is a difference between same object and two objects that look the same. You probably should have a look at the Question about the difference between eq?, eqv?, equal? and =
Racket has string=? which compares strings specifically and might be faster than the less specific equal?.
I have an expression: (map some-proc some-list)
which evaluates to, say, '(#f #f #f).
I want to check whether all booleans in this list are true. However,
(and '(#f #f #f)) returns '(#f #f #f), while I heed #f.
Now, if I (apply and '(#f #f #f)), I get the error:
and: bad syntax in: and in the DrRacket environment. That is confusing because the Racket Reference gives the example of (apply + '(1 2 3)) which seems to be identical to my problem.
What am I doing wrong and how I get my #f out of '(#f #f #f)?
you can use andmap for this:
> (andmap (lambda (x) x) '(#f #f #f))
#f
The problem stems from the fact that and is not a procedure, but a macro, in order to avoid the evaluation of all of its arguments.
It would work in Lazy Racket, though, where and is a procedure.
Just another way of doing it:
(foldr (lambda(x y) (and x y)) #t '(#f #f #f))
or I will rewrite uselpa's solution (andmap):
(andmap identity '(#f #f #f))