将Racket源代码字符串转换为Racket代码



我正在编写一个系统,其中客户端将代码发送到服务器(在球拍中实现(以供执行。代码以字符串形式接收。我需要将该字符串转换为它所代表的球拍代码并执行它。代码中的任何定义都需要在提取代码后可用。当我运行以下操作时,我会返回1,正如预期的那样。

(define-syntax-parser expand
((_) (datum->syntax this-syntax (format-datum '~a "(define (f x) x)"))))
(expand)
(f 1)

但是,如果我试图将字符串保存在变量source中,我会返回source: undefined; cannot reference an identifier before its definition。我理解这是因为宏是在编译时展开的,而source还没有定义。其他人对我如何实现这一目标有什么想法吗?

如果你想计算一个字符串,那么你需要readeval。你当然不想要宏。

请注意,这是非常危险的,几乎可以肯定是一个非常糟糕的想法:运行不受控制的代码(甚至调用read,除非你对read的安全有绝对的信心(,只有当你对发送代码的东西有<em]隐含的、完全的信任>时,才有意义。由于人们滥用这种信任,出现了许多著名的问题。至少你应该阅读Racket文档中的Reflection和Security:尤其是关于沙盒的内容。但沙盒评估者也曾遭受过许多著名的功绩。

;;; May explode if exposed to light.  Hypergolic in air.
;;;
(define string-to-evaluate "
(define-syntax-rule (bind [v val] form ...)
(let ([v val]) form ...))
(bind (foo 1)
(+ foo foo))
(define bar 1)
")
(define (evaluate-string s (n (make-base-namespace)))
(values
(call-with-input-string
s
(λ (p)
(for/list ([form (in-port read p)])
(eval form n))))
n))
> (evaluate-string string-to-evaluate)
'(#<void> 2 #<void>)
#<namespace>
> bar
bar: undefined;
cannot reference an identifier before its definition
> (evaluate-string string-to-evaluate (current-namespace))
'(#<void> 2 #<void>)
#<namespace:'anonymous-module>
> bar
1 

但是

> (evaluate-string "(require racket/system) (system "echo now I own your computer")")
now I own your computer
'(#<void> #t)
#<namespace>

哎呀。

最新更新