#lang racket
(provide def outer)
(require racket/stxparam
racket/splicing)
(require (for-syntax racket/list))
(define-syntax-parameter current-insides '())
(define-syntax-parameter current-outsides '())
(define-syntax (def stx)
(syntax-case stx ()
[(_ (name args ...) body ...)
(with-syntax ([fun-stx stx])
#'(splicing-syntax-parameterize
([current-outsides
(cons #'fun-stx
(syntax-parameter-value #'current-outsides))])
(define (name args ...)
(splicing-syntax-parameterize
([current-insides (cons #'fun-stx
(syntax-parameter-value
#'current-insides))])
body ...))))]))
(begin-for-syntax
positive-integer -> (U syntax #f)
(define (find-scope id outsides insides n)
(cond
[(empty? insides)
#f]
[(free-identifier=? id
(datum->syntax (first insides) (syntax-e id)))
(cond
[(= n 1)
(first outsides)]
[else
(find-scope (datum->syntax (first outsides) (syntax-e id))
(rest outsides)
(rest insides)
(sub1 n))])]
[else
(find-scope id (rest outsides) (rest insides) n)])))
(define-syntax (outer stx)
(syntax-case stx ()
[(_ n id)
(and (exact-positive-integer? (syntax-e #'n))
(identifier? #'id))
(let ()
(define found-binding
(find-scope #'id
(syntax-parameter-value #'current-outsides)
(syntax-parameter-value #'current-insides)
(syntax-e #'n)))
(datum->syntax found-binding
(syntax-e #'id)
#'id))]
[(_ id)
(identifier? #'id)
#'(outer 1 id)]))