使脚本在 DrRacket 和 (x)repl 中工作

Make script work both in DrRacket and (x)repl

我正在尝试从 DrRacket 和 repl 制作一个脚本,以此作为我的起点:Racket calculator

这是我当前的代码:

#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) )
           ((expr start) ))

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


(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))

此处显示了来自 DrRacket 的测试:

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>

来自回复:

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]

它只显示每秒的计算。看起来 read-line returns 在等待用户输入之前是一个新行,我试图用 (eof-object? input)(eq? input #\newline) 检查,但现在我只得到每隔一秒的结果。

(calc (read-line)) 替换为 (calc input)

有两个问题:

首先,您正在读取一行,(let ((input (read-line))),但您没有将该输入发送到计算器,而是发送了另一行 – (calc (read-line))

您应该将 input 传递给 calc 进行评估。

其次,您的输出中有很多 #<void>

这是因为 calc 假设您的解析器生成了一个可以打印的值:

(displayln result)

但解析器不产生任何值,它只打印一个。
要么删除 result 的输出,要么将解析器重写为 return 其调用者的值。