0
votes

I have the following function in Scheme (Using Dr. Racket), it's a vague translator from Scheme to Javascript.

I can't seem to figure out why #void is being printed.

I'm guessing it has something to do with the return value of fprintf, but I'm not sure how can this behavior be overcome.

Any ideas?

(define unparse->js
(lambda (ast output-port)
(cond ((def-exp? ast) (fprintf output-port "const ~a = ~a;" (unparse->js 
(def-exp->var ast) output-port) (unparse->js (def-exp->val ast) output-port)))
      ((cexp? ast)
       (cond ((num-exp? ast) (number->string (num-exp->val ast)))
             ((bool-exp? ast) (if (eq? (bool-exp->val ast) #t) "true" "false"))
             ((str-exp? ast)  (str-exp->val ast))
             ((var-exp? ast)  (symbol->string (var-exp->var ast)))
             ((literal-exp? ast) (list 'quote (literal-exp->val ast)))
             ((proc-exp? ast) (fprintf output-port "(~a) => { ~a }"
                                       (string-join
                                        (map (lambda (b)
                                             (unparse->js b output-port)
                                             )
                                           (proc-exp->params ast)
                                       )
                                      ",")
                                       (string-join
                                      (map (lambda (ast)
                                             (unparse->js ast output-port)
                                             )
                                           (proc-exp->body ast))
                                      ";")))

             ((if-exp? ast) (fprintf output-port "~a ? ~a : ~a"
                                     (unparse->js (if-exp->test ast) output-port)
                                     (unparse->js (if-exp->then ast) output-port)
                                     (unparse->js (if-exp->else ast) output-port)))
             ((let-exp? ast) (fprintf output-port "let ~a; ~a;"
                                     (string-join
                                      (map (lambda (b)
                                             (fprintf output-port "~a = ~a"
                                                      (unparse->js (binding->var b) output-port)
                                                      (unparse->js (binding->val b) output-port)
                                                      )
                                             )
                                           (let-exp->bindings ast)
                                       )
                                      ",")
                                     (string-join
                                      (map (lambda (b)
                                             (unparse->js b output-port)
                                             )
                                           (let-exp->body ast))
                                      ";")

                              )
              )
             ((app-exp? ast) (fprintf output-port "~a(~a)"
                                      (unparse->js (app-exp->rator ast) output-port)
                                      (string-join
                                        (map (
                                              lambda (rand)
                                               (unparse->js rand output-port)
                                              )
                                         (app-exp->rands ast)
                                        )
                                        ",")
                              )
              )
             (else (error "Unknown exp type: " ast))))
      (else (error "Unknown exp type: " ast)))
  )
)

I'm calling it this way:

(unparse->js (parse '(if (eq? 5 3) 1 0)) (current-output-port))

The expected result is:

eq?(5,3) ? 1 : 0

However, the result I'm getting is:

eq?(5,3) #< void > ? 1 : 0

1

1 Answers

1
votes

try printing to a string output port and see if that solves the problem. If not produce a complete example we can test. The bug might be in, say, unparse->js or another function.

To figure where the void is printed, insert

(displayln ast)

Right after

 (lambda (ast output-port)

Such that the current ast print each time unparse->js is called. Then you can find the clause of the cond that is the culprit.

Regarding the comment more "voids" pop up.

There are two problems: first your output-port is the same as the repl output port. That means that values you don't print (such as in the num-exp clause) are printed by the repl.

Try this

(with-output-to-string (lambda () (unparse->js (parse '(if (eq? 5 3) 1 0)) (current-output-port))))

This will make a string containing everything printed.

The second problem is due to this way of printing:

 (fprintf output-port "~a ? ~a : ~a"
     (unparse->js (if-exp->test ast) output-port)
     (unparse->js (if-exp->then ast) output-port)
     (unparse->js (if-exp->else ast) output-port)))

Since unparse-js returns void in all clauses where you use fprintf (remember that fprintf returns void) you will print void if one of the subexpressions returns void.

The question is now how to avoid the problem. A common solution is split the printing in two parts. The first phase is to built a tree of strings. Printing the strings in the string will produce the correct output.

So instead of:

 (fprintf output-port "~a ? ~a : ~a"
     (unparse->js (if-exp->test ast) output-port)
     (unparse->js (if-exp->then ast) output-port)
     (unparse->js (if-exp->else ast) output-port)))

you write

 (list (unparse->js (if-exp->test ast))
       " ? " 
       (unparse->js (if-exp->then ast))
       " : "
       (unparse->js (if-exp->else ast))))

This will make unparse->js return a tree of strings.

Then simply make a printer function that recurses through the tree and prints the individual strings.