2012-03-02 4 views
3

Я работаю над переводчиком языка в схеме guile и должен обрабатывать основной случай, когда вы пытаетесь преобразовать одно слово.Можно ли просто напечатать строку, которая была передана в макрос схемы?

(define var 5) 
(translate var) 

Это должно вернуть строку var, а не число 5.
Как это сделать с использованием макросов схемы R5RS (стиль define-syntax)?

Редактировать:
Я переводил с Схемы в Coffeescript.

+1

Бьюсь об заклад, если вы предоставили более подробную информацию о том, на каком языке переводчик вы работаете, вы получите гораздо более полезную помощь. Что вы переводите с и на что и почему? –

ответ

5
(define-syntax translate 
    (syntax-rules() 
    [(_ v) 'v])) 

И если вы хотите строку: компилятор

(define-syntax translate 
    (syntax-rules() 
    [(_ v) (symbol->string 'v)])) 

Будет надеяться хитростью является достаточно умен, чтобы сложить полученное выражение, так что по существу становится постоянной строкой.

+0

Спасибо за ответ. Я имел в виду, что он должен возвращать фактическую строку (так что я могу использовать (string-append) на ней. – chustar

+0

Было бы приемлемо для вас использовать строку 'symbol-> string' вокруг' 'v'? – dyoo

+0

Это работает с оберткой символов -> string. Теперь мне нужно найти работу для голых чисел. Спасибо. – chustar

4

С syntax-case и его охранник поддержка:

(define-syntax translate 
    (lambda (stx) 
    (syntax-case stx() 
     [(_ v) (identifier? #'v) 
     #'(symbol->string 'v)] 
     [(_ v) (number? (syntax-e #'v)) 
     #'(number->string v)]))) 

(. Я использовал квадратные скобки для облегчения сравнения с ответом Эли, однако, это не мой обычный стиль ;-))

Но если вы используете syntax-case, то вы можете точно также сделать преобразование на уровне синтаксиса, а не производить код, который делает это во время выполнения:

(define-syntax translate 
    (lambda (stx) 
    (syntax-case stx() 
     [(_ v) (identifier? #'v) 
     (datum->syntax stx (symbol->string (syntax->datum #'v)))] 
     [(_ v) (number? (syntax-e #'v)) 
     (datum->syntax stx (number->string (syntax->datum #'v)))]))) 

Главное здесь в том, что код макроса теперь обычная схема, например, вы могли бы абстрагировать общие части в качестве помощника:

(define-syntax translate 
    (lambda (stx) 
    (define (rewrap convert x) 
     (datum->syntax stx (convert (syntax->datum x)))) 
    (syntax-case stx() 
     [(_ v) (identifier? #'v) (rewrap symbol->string #'v)] 
     [(_ v) (number? (syntax-e #'v)) (rewrap number->string #'v)]))) 

В том же ключе, если этот макрос так просто, то никакой реальной потребность в syntax-case, кроме вытаскивания подвыражению:

(define-syntax translate 
    (lambda (stx) 
    (syntax-case stx() 
     [(_ v) (let ([d (syntax->datum #'v)]) 
       (datum->syntax 
       stx 
       ((cond [(number? d) number->string] 
         [(symbol? d) symbol->string]) 
       d)))]))) 

Примечания, кстати, что нет никакой магии в syntax-case - и в случае этой простой модели, вы могли бы просто вытащить значение себя:

(define-syntax translate 
    (lambda (stx) 
    (let ([d (cadr (syntax->datum #'v))]) 
     (datum->syntax 
     stx 
     ((cond [(number? d) number->string] 
       [(symbol? d) symbol->string]) 
     d))))) 

Существует несколько шаблонного материала, который syntax-case делает, что эта последнюю версия проигрывает:

  • Если вы используете макрос неожиданным образом, как (translate) то эта версия будет выдавать ошибку о cadr вместо более понятного синтаксиса ошибка

  • Аналогично, если вы используете (translate 1 2), тогда эта версия просто молча игнорирует 2 вместо ошибки.

  • И если он используется с чем-то, что не является ни идентификатор, ни число (например, (translate (+ 1 2))), то это будет зависеть от неопределенного значения, cond возвращается, а не бросать ошибку синтаксиса.

+1

Я хотел добавить: с небольшой модификацией макрос Chris's синтаксис-фрейм может сделать немного больше работы во время компиляции, так что даже строки 'symbol-> string' и' number-> string' не происходят во время выполнения. – dyoo

+1

Бах. Это не имеет смысла делать * это * ... Если вы идете с «синтаксисом», вы также можете конвертировать туда, а не расширять код, который их делает. –

+1

@ Эли: Я согласен. Я просто хотел сопоставить свой код (который я знаю, работает, потому что ваши сообщения всегда качественны :-)), поэтому мне не нужно беспокоиться о тестировании моего. Но теперь я отредактирую код. –

0

других ответов полезен уже достаточно, но я думал, что просто указать на то, что можно обобщить эту технику в очень удобном виде: макросы для распечатки выражения и их результатов для отладки:

(define-syntax log-expr 
    (syntax-rules() 
    ((_ expr) 
    (let ((result expr)) 
     (write (quote expr)) 
     (display " evaluates to ") 
     (write result) 
     (newline) 
     result))))