Reputation: 11110
As an exercise in Racket, I am trying to create a C-like function, accepting the return
keyword.
When I define the function
macro:
(require (for-syntax syntax/parse))
(define-syntax (function stx)
(syntax-parse stx
#:datum-literals (return)
[(_ (func-id arg-id ...) body ...)
#'(define (func-id arg-id ...)
(call/cc (lambda (return) body ...)))]))
and create a function f
with a return
statement:
(function (f x) (return 100) (+ 1 x))
(f 2)
I get:
return: undefined;
cannot reference an identifier before its definition
However, expanding the function
macro:
(require macro-debugger/expand)
(syntax->datum (expand-only
#'(function (f x) (return 100) (+ 1 x))
(list #'function)))
returns:
'(define (f x) (call/cc (lambda (return) (return 100) (+ 1 x))))
and in fact:
(define (f x) (call/cc (lambda (return) (return 100) (+ 1 x))))
(f 2)
> 100
works as expected.
For my dummy case, it is possible to replace the pattern matching in function
definition like so:
[(_ (func-id arg-id ...) (return x-expr) body ...)
#'(define (func-id arg-id ...)
(call/cc (lambda (return) (return x-expr) body ...)))]
But in general the return
keyword can occur anywhere in the function body.
How can I do this?
Upvotes: 2
Views: 342
Reputation: 11110
This seems to work:
(require (for-syntax syntax/parse))
(require racket/stxparam)
(define-syntax-parameter return
(lambda (stx)
(raise-syntax-error (syntax-e stx) "can only be used inside aif")))
(define-syntax (function stx)
(syntax-parse stx
[(_ (func-id arg-id ...) body ...)
#'(define (func-id arg-id ...)
(call/cc (lambda (return_k)
(syntax-parameterize ([return (syntax-rules () [(_ val) (return_k val)])])
body ...))))]))
(function (f x) (return 100) (+ 1 x))
(f 2)
I suppose syntax-parameterize
can be improved with make-rename-transformer
, but I did not succeed in that.
Upvotes: 0
Reputation: 6502
I think you misunderstand what datum-literals
means. datum-literals
is for pattern matching input syntax, not for output template/syntax.
What you attempt to do couldn't be done purely by rewriting rules because Racket's hygienic macro system will rename your return
into something else to avoid name collision.
The usual way to do this is using syntax parameter. Fear of Macros explains this really well already, so I won't repeat it here.
Upvotes: 5