Search code examples
parsingschemeracketlex

Make script work both in DrRacket and (x)repl


I am trying to make a script work from both DrRacket and the repl, having this as my starting point: Racket calculator

Here is my current code:

#lang racket


(provide (all-defined-out))

(require parser-tools/lex
         (prefix-in re: parser-tools/lex-sre)
         parser-tools/yacc)


(define-tokens value-tokens (INT ANY))
(define-empty-tokens empty-tokens
  (PLUS MINUS MULTIPLY DIVIDE NEWLINE EOF))


(define basic-lexer
  (lexer
   ((re:+ numeric) (token-INT lexeme))
   (#\+ (token-PLUS))
   (#\- (token-MINUS))
   (#\* (token-MULTIPLY))
   (#\/ (token-DIVIDE))   
   ((re:or #\tab #\space) (basic-lexer input-port))
   (#\newline (token-NEWLINE))
   ((eof) (token-EOF))
   (any-char (token-ANY lexeme))))


(define (display-plus expr)
  (display "Result: ")
  (let ((left (string->number (first expr)))
        (right (string->number (last expr))))
    (display (+ left right)))
  (newline))


(define (display-minus expr)
  (display "Result: ")
  (let ((left (string->number (first expr)))
        (right (string->number (last expr))))
    (display (- left right)))
  (newline))


(define (display-multiply expr)
  (display "Result: ")
  (let ((left (string->number (first expr)))
        (right (string->number (last expr))))
    (display (* left right)))
  (newline))


(define (display-divide expr)
  (display "Result: ")
  (let ((left (string->number (first expr)))
        (right (string->number (last expr))))
    (display (/ left right)))
  (newline))


(define basic-parser
  (parser

   (start start)
   (end NEWLINE EOF)
   (tokens value-tokens empty-tokens)
   (error (lambda (ok? name value)
            (printf "Couldn't parse: ~a\n" name)))

   (grammar

    (start ((expr) $1)
           ((expr start) $2))

    (expr ((INT PLUS INT) (display-plus (list $1 $3)))
          ((INT MINUS INT) (display-minus (list $1 $3)))
          ((INT MULTIPLY INT) (display-multiply (list $1 $3)))
          ((INT DIVIDE INT) (display-divide (list $1 $3)))
          ((ANY) (displayln $1))))))


(define input1 (open-input-string "123 + 456")) 
(define input2 (open-input-string "123 *456")) 


(basic-parser (lambda() (basic-lexer input1)))
(basic-parser (lambda() (basic-lexer input2)))



;(define (my-repl)
;    (display ">>> ")
;    (let* ((input (read-line))
;           (input-port (open-input-string
;                          (list->string
;                           (drop-right
;                            (string->list input) 1)))))
;      (cond
;        ((not (equal? "\r" input)
;              (print (basic-parser
;                      (lambda () (basic-lexer input-port))))))))
;    (my-repl))



(define (calc str)
  (let* ([port (open-input-string str)]
         [result (basic-parser (lambda() (basic-lexer port)))])
    (displayln result)))


(define (repl)
  (display ">>> ")
  (let ((input (read-line)))
    (print input)
    (cond
      ((eof-object? input)  (displayln "eof"))
      ((eq? input #\newline) (displayln "new line"))
      (else (calc (read-line))))
    (newline))
  (repl))

A test from DrRacket is shown here:

Welcome to DrRacket, version 7.1 [3m].
Language: racket, with debugging; memory limit: 512 MB.
Result: 579
Result: 56088
> (repl)
>>> 1+1
"1+1"2+2
Result: 4
#<void>

>>> 3+3
"3+3"4+4
Result: 8
#<void>

And from the repl:

Welcome to Racket v7.1.
> (require "untitled7.rkt")
Result: 579
Result: 56088
> (repl)
>>> "\r"

#<void>

>>> 1+1
"1+1\r"2+2
Result: 4

#<void>

>>> 3+3
"3+3\r"4+4
Result: 8

#<void>

>>> #<eof>eof

>>> ; user break [,bt for context]

It only displays every second calculation. It appears that read-line returns a new line before waiting for user input, which I tried to check with (eof-object? input) and (eq? input #\newline) but now I get only every second result.


Solution

  • There are two problems:

    First, you're reading a line, (let ((input (read-line))), but you're not sending that input to the calculator, you'r sending another one – (calc (read-line)).

    You should pass input to calc for evaluation instead.

    Second, you have a lot of #<void>s in your output.

    This is because calc assumes that your parser produces a value that it can print:

    (displayln result)
    

    but the parser does not produce any value, it only prints one.
    Either remove the output of result, or rewrite the parser to return the value to its caller.