I'm trying to run a Racket file from the console as part of a deployment. Part of this is compiling files with pandoc. I have a command that works when I call it directly from the console, but it doesn't work when I call it from within Racket.
Successful command:
pandoc example.md -H pandoc.css
This gives me a warning about how I'm supposed to include a title in an HTML file, but then it spits out some HTML to stdout.
The Racket code makes three calls, and it displays outputs to stdout and stderr for each call. It's a bit bulky, so the code will be at the bottom. Here I'll just put the calls it makes and the outputs.
> /bin/ls ./
> /usr/bin/pandoc example.md
> /usr/bin/pandoc example.md -H pandoc.css
stdout from first two:
example.md
pandoc.css
pandoc.rkt
<h1 id="first-section">First Section</h1>
<p>This is an example markdown file.</p>
stderr for third:
pandoc: example.md -H pandoc.css: openBinaryFile: does not exist (No such file or directory)
Any clue as to why it can find example.md, but it can't find pandoc.css?
Here's the full code for pandoc.rkt. This is my first time using ports in Racket, so if I'm doing something in a horrible way, feel free to let me know.
#lang racket
(define-values (ls out1 in1 err1)
(subprocess #f #f #f "/bin/ls" "./"))
(subprocess-wait ls)
(define files (port->lines out1))
(define error (port->lines err1))
(display files) (display "\n")
(display error) (display "\n")
(close-input-port out1)
(close-output-port in1)
(close-input-port err1)
(define-values (pandoc out2 in2 err2)
(subprocess #f #f #f "/usr/bin/pandoc" "example.md"))
(subprocess-wait pandoc)
(define html (port->lines out2))
(set! error (port->lines err2))
(display html) (display "\n")
(display error) (display "\n")
(close-input-port out2)
(close-output-port in2)
(close-input-port err2)
(set!-values (pandoc out2 in2 err2)
(subprocess #f #f #f "/usr/bin/pandoc" "example.md -H pandoc.css"))
(subprocess-wait pandoc)
(set! html (port->lines out2))
(set! error (port->lines err2))
(display html) (display "\n")
(display error) (display "\n")
(close-input-port out2)
(close-output-port in2)
(close-input-port err2)
There are several arguments: example.md, -H, and pandoc.css. Therefore, you should separate them according to the documentation.
That is, instead of:
(subprocess #f #f #f "/usr/bin/pandoc" "example.md -H pandoc.css")
Use:
(subprocess #f #f #f "/usr/bin/pandoc" "example.md" "-H" "pandoc.css")
Note that this is also true for subprocess of several other languages (e.g., Python).
Related
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.
At this site: http://www.gigamonkeys.com/book/practical-a-simple-database.html there is user entry function listed as follows:
(defun prompt-read (prompt)
(format *query-io* "~%~a: " prompt)
(force-output *query-io*)
(read-line *query-io*))
Are there any major advantages of above function as compared to following simpler form:
(defun prompt-read2 (prompt)
(format t "~%~a: " prompt)
(setf answer (read-line)))
Is it recommended to always use force-output and *query-io* all the time?
Setting the answer to a global variable like that is bad. You should just return the answer and let the caller do what it wants with it. If you do use special (~global) variables, you should put asterisks around the name (*ANSWER* instead of ANSWER).
FORCE-OUTPUT is needed to ensure that the user actually sees the prompt before having to answer. If I run the second version using SBCL in a terminal, the program just freezes to wait for input without saying anything.
*QUERY-IO* should be used for querying things from the user, because some environment might want to handle that differently from other output. For example, someone might write a GUI wrapper for your program that turns the queries into graphical dialogs. Or maybe they want to run it as a part of a script, providing the input from a string.
(defun prompt-read (prompt)
(format *query-io* "~%~a: " prompt)
(force-output *query-io*)
(read-line *query-io*))
(defun hello ()
(format t "~&Hello ~a!~%" (prompt-read "What's your name")))
(defmacro with-input ((input) &body body)
`(let ((*query-io* (make-two-way-stream (make-string-input-stream ,input)
(make-string-output-stream))))
,#body))
(defun test ()
(with-input ("jkiiski")
(hello))
(with-input ("rnso")
(hello)))
(test)
; Hello jkiiski!
; Hello rnso!
Edit
A more complex example using SBCLs gray streams.
(defclass foo-stream (sb-gray:fundamental-character-input-stream)
((output-input-script :initarg :script :accessor foo-stream-script)
(output-stream :initarg :out :accessor foo-stream-out)
(current-input :initform nil :accessor foo-stream-current-input)))
(defmethod sb-gray:stream-read-char ((stream foo-stream))
(with-accessors ((input foo-stream-current-input)
(out foo-stream-out)
(script foo-stream-script)) stream
(when (or (null input)
(not (listen input)))
(let ((output (string-trim '(#\space #\newline)
(get-output-stream-string out))))
(setf input (make-string-input-stream
(format nil "~a~%"
(cdr (assoc output script :test #'string=)))))))
(read-char input)))
(defun prompt-read (prompt)
(format *query-io* "~%~a: " prompt)
(force-output *query-io*)
(read-line *query-io*))
(defun hello ()
(format t "~&Hello ~a!~%" (prompt-read "What's your name"))
(format t "~&I'm ~a too!" (prompt-read "How are you"))
(format t "~&~a~%" (if (string-equal (prompt-read
"Do you want to delete all your files")
"yes")
"Deleting all files... (not really)"
"Not deleting anything.")))
(defmacro with-input-script ((script) &body body)
(let ((out-sym (gensym "out")))
`(let* ((,out-sym (make-string-output-stream))
(*query-io* (make-two-way-stream
(make-instance 'foo-stream
:out ,out-sym
:script ,script)
,out-sym)))
,#body)))
(defun test ()
(with-input-script ('(("What's your name:" . "jkiiski")
("How are you:" . "great")
("Do you want to delete all your files:" . "No")))
(hello))
(with-input-script ('(("What's your name:" . "Foo Bar")
("How are you:" . "fine")
("Do you want to delete all your files:" . "Yes")))
(hello)))
(test)
; Hello jkiiski!
; I'm great too!
; Not deleting anything.
; Hello Foo Bar!
; I'm fine too!
; Deleting all files... (not really)
Yes you code is easy but the first is more clarifying what are you doing:
*query-io* is a global variable (which you can tell because of the * naming convention for global variables) that contains the input stream
connected to the terminal. The return value of prompt-read will be the
value of the last form, the call to READ-LINE, which returns the
string it read (without the trailing newline.)
This is what they said about *query-io*
And about the streams that you can put there works as follow:
most other I/O functions also accept T and NIL as stream designators
but with a different meaning: as a stream designator, T designates the
bidirectional stream *TERMINAL-IO*, while NIL designates
*STANDARD-OUTPUT* as an output stream and *STANDARD-INPUT* as an input stream
in this case it seems that this is only pointing to *standard-input* and not to the bidirectional stream t
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 writing a program which needs communication between processes.
my code:
#lang racket
(define-values (sp o i e) (subprocess #f #f #f "c://player1.exe" ))
(define count 10)
(for ([c (in-naturals)])
(cond
[(equal? count 0) (error "Province is empty!") ]
[else
(write "server" i)
(set! count (sub1 count))
(flush-output i)
(display (read o))]))
and the player1.exe code:
#lang racket
(define (interact notification)
(cond
[(eq? notification "server") (write "true" (current-output-port))]
[else (write "false" (current-output-port))]))
(for ([c (in-naturals)])
(interact (read (current-input-port)))
(write "player" (current-output-port))
(sleep 0.1)
flush-output (current-output-port))
I am getting output if I run without loops. I am also getting output when only player is sending messages. But with both server and player sending messages the program gets hanged.
What do you think the problem is?
The last line in your player1.exe file looks suspicious. flush-output is not actually being applied as a function. Rather than
flush-output (current-output-port)
you probably mean:
(flush-output (current-output-port))
From a style point of view: the functions read, write, and flush-output all work on the current input and output ports by default, so you don't need to provide them. Take a look at the documentation for those functions, such as flush-output, and you'll see that it mentions that the current-output-port is its default.
So the line that we just looked at can be written as:
(flush-output)
More issues: don't use eq? to compare strings. Use string=? instead. The reason is that there can be two strings that have the same textual content, but for which eq? will still be able to distinguish the two. e.g.:
kui $ racket
Welcome to Racket v5.2.1.
> (eq? "a" (string-copy "a"))
#f
> (string=? "a" (string-copy "a"))
#t
I have this script as an .scm in Gimp:
;MIT license.
(define (script-fu-export-layers img drw path outnameformat)
; credit to Vijay Mathew on Stack Overflow for the expand keywords function
(let ((expand-keywords (lambda(format tokens)
(let loop ((slist (string->list string))
(in-replace-mode #f)
(result ""))
(if (not (null? slist))
(let ((c (car slist)))
(cond (in-replace-mode
(let ((token (car (cdr (assoc c tokens)))))
(loop (cdr slist) #f (string-append result token))))
((char=? c #\~)
(loop (cdr slist) #t result))
(else
(loop (cdr slist) #f (
string-append result (make-string 1 c))))))
result)))))
(for-each (lambda (layer)
(let* (
(name (expand-keywords outnameformat '(
(#\i (car(gimp-image-get-name img)))
(#\l (car(gimp-drawable-get-name layer))))))
(outpath (string-append path "/" name)))
(gimp-file-save RUN-NONINTERACTIVE img layer outpath name)
)) (vector->list(cadr (gimp-image-get-layers img)))))
)
(script-fu-register
"script-fu-export-layers"
"L_ayers"
"Export all layers as individual files."
"Stuart P. Bentley <stuart#testtrack4.com>"
"Copyright 2011 Stuart P. Bentley"
"June 28, 2011"
"*"
SF-IMAGE "The Image" 0
SF-DRAWABLE "The Layer" 0
SF-DIRNAME "Output directory" ""
SF-STRING "Filename Format (~i = image name, ~l = layer name)"
"~i-~l.png"
)
(script-fu-menu-register "script-fu-export-layers" "<Image>/File/E_xport")
With the comment at the top of the file, the script doesn't load at all. When the comment is removed, GIMP throws an error when refreshing scripts that "Error while loading C:\Users\Stuart.gimp-2.6\scripts\export-layers.scm: Error: unmatched parentheses: 1". I don't know what that's about, since it parses fine when I run it on Codepad (it breaks at the script-fu-register point).
This behavior is consistent with what would happen if the line endings were missing (the comments commenting out all subsequent text). Check to make sure your editor isn't doing something ridiculous like saving your file with CR line endings on Windows.