Как мне сделать, чтобы мой Racket разработал язык для запуска скриптов из внешних файлов? - PullRequest
2 голосов
/ 30 июня 2019

У меня есть собственный язык, который я проектирую в Racket, давайте назовем его waffle.

Допустим, у меня есть

(define (printstr . input)
   (if (string? (car input)) 
       (write (string-join input ""))
       (write input)))

; ... a whole bunch of other definitions

(command-line
 #:multi
 [("-v" "--verbose")    "more verbose"      (set! loglevel (add1 loglevel))]
 [("-q" "--quiet")      "be quiet"          (set! loglevel 0)]
 #:once-any
 [("-i" "--in-place")   "edit in-place"     (set! mode 'in-place)]
 [("-c" "--create-new") "create a new file" (set! mode 'new)]
 [("-n" "--dry-run")    "do nothing"        (set! mode #f)]
 #:once-each
 [("-d" "--directory") dir "work in a given directory" (set! root dir)]
 #:help-labels "operations to perform:"
 #:multi
 [("+l" "++line") "add a line"    (set! ops `(,@ops "add"))]
 [("-l" "--line") "delete a line" (set! ops `(,@ops "delete"))]
 [("-e" "--edit") "edit a line"   (set! ops `(,@ops "edit"))]
 #:args (file)
 (define in (open-input-file file))

 ; This is probably where I'm going wrong with how my language REPL evaluates files passed to it.
 (eval (file->list file) ns))

Затем я создаю исполняемый файл из DrRacket, используя 'Racket [меню] -> Создать исполняемый файл ... -> [Тип] Launcher'. Имя например waffle-test.

У меня есть файл, написанный на моем языке вафель, hello.waffle:

(printstr "Hello!")

Я ожидаю, что это напечатает «Привет!» в командной строке, а затем выйти без ошибок. Но я получаю странную ошибку, которую не понимаю, и получаю приглашение без перевода строки.

$ ./waffle-test hello.waffle 
application: not a procedure;
 expected a procedure that can be applied to arguments
  given: #<void>
  arguments...: [none]
  context...:
   eval-one-top12
   "/home/connie/Desktop/racket-ffgardfghf/waffle": [running body]
   temp37_0
   for-loop
   run-module-instance!125
   perform-require!78
"Hello!" $

Я знаю, что вы не должны использовать eval, но я не могу понять, как заставить мой исполняемый язык читать и запускать файлы, которые я передаю ему. Каков наилучший подход к этому?

Ответы [ 2 ]

2 голосов
/ 30 июня 2019

Только с помощью этого простого теста я понял пару вещей:

#!racket
(file->list "test.waffle")

С test.waffle как:

(waffle me)
(waffle it)

Отпечатано:

((waffle me)
 (waffle it))

Однако это недопустимый код, даже если waffle является допустимой процедурой. Вам нужно, чтобы это выглядело так:

(begin
  (waffle me)
  (waffle it))

Теперь вы можете сделать это, требуя, чтобы ваш язык имел его, но вы также можете просто cons a begin к результирующей структуре, и eval будет оценивать каждую форму верхнего уровня по порядку.

Вы столкнетесь с проблемами при использовании eval. Вы узнаете это достаточно скоро. Правильный способ создать интерпретатор - создать собственный eval, который реализует синтаксис вашего языка и использует среду с примитивами. У него есть интерфейс для использования хоста, но не независимо. С eval waffle программами есть доступ ко всем внутренним компонентам, и вы на самом деле не создаете переводчика, поскольку вы просто выставляете Racket.

Я помню, что кто-то делал то же самое с Ruby и имел веб-страницу, а кто-то просто пытался ввести команду, которая удалила все файлы в системе, и веб-служба ушла.

1 голос
/ 19 июля 2019

Исправив несколько ошибок в моем коде, мне удалось решить мою проблему!

Я изменил

(eval (file->list file) ns))

на

(define program (cons 'begin (file->list file)))

(eval program ns))

Я также изменил функцию 'printstr' с

 (define (printstr . input)
   (if (string? (car input)) 
       (write (string-join input ""))
       (write input)))

на

 (define (printstr input)
   (displayln input))
...