Poly definitions in separate files by output type #111

Open
opened 3 years ago by bluebear94 · 9 comments
bluebear94 commented 3 years ago (Migrated from github.com)

Is it possible to define tags that depend on the output format by using a separate file for each format?

For example, if I specify html and tex output formats, then can I put the HTML-specific definitions in pollen-html.rkt and the LaTeX-specific definitions in pollen-latex.rkt, including whichever file is appropriate for the output?

Is it possible to define tags that depend on the output format by using a separate file for each format? For example, if I specify `html` and `tex` output formats, then can I put the HTML-specific definitions in `pollen-html.rkt` and the LaTeX-specific definitions in `pollen-latex.rkt`, including whichever file is appropriate for the output?
bluebear94 commented 3 years ago (Migrated from github.com)

I'd imagine that to do this, you'd need to:

  1. Inspect the exports of both pollen-html.rkt and pollen-latex.rkt at phase n+1
  2. For each symbol that's exported from either one, use a macro to define a function that dispatches to the appropriate implementation, or a stub if an implementation is unavailable for the current output format
  3. Re-export those functions

The disadvantage is that the generated functions in the main pollen.rkt wouldn't be able to know what signature to expect, so errors having to do with signatures would blame pollen.rkt and not the calling code. Exported non-function variables (such as (define br '(br)) wouldn't play well with this approach as well; they'd have to be re-exported as functions, requiring existing code to be changed.

Alternatively:

  1. Inspect the exports of both pollen-html.rkt and pollen-latex.rkt at phase n+1
  2. For each symbol that's exported from either one, use a macro to define a function that returns a stub tag that's replaced with the real implementation during post-processing

but this requires the arguments to be valid X-exprs, so I'm not too keen on that.

I'd imagine that to do this, you'd need to: 1. Inspect the exports of both `pollen-html.rkt` and `pollen-latex.rkt` at phase n+1 2. For each symbol that's exported from either one, use a macro to define a function that dispatches to the appropriate implementation, or a stub if an implementation is unavailable for the current output format 3. Re-export those functions The disadvantage is that the generated functions in the main `pollen.rkt` wouldn't be able to know what signature to expect, so errors having to do with signatures would blame `pollen.rkt` and not the calling code. Exported non-function variables (such as `(define br '(br))` wouldn't play well with this approach as well; they'd have to be re-exported as functions, requiring existing code to be changed. Alternatively: 1. Inspect the exports of both `pollen-html.rkt` and `pollen-latex.rkt` at phase n+1 2. For each symbol that's exported from either one, use a macro to define a function that returns a stub tag that's replaced with the real implementation during post-processing but this requires the arguments to be valid X-exprs, so I'm not too keen on that.
sorawee commented 3 years ago (Migrated from github.com)

If stuff that you are gonna import are run-time definitions, you can use dynamic-require, FWIW.

If stuff that you are gonna import are run-time definitions, you can use `dynamic-require`, FWIW.
bluebear94 commented 3 years ago (Migrated from github.com)

I'm not getting much luck using dynamic-require from Pollen:

pollen.rkt:

#lang racket

(displayln (dynamic-require "pollen/common.rkt" 'sei))

pollen/common.rkt:

#lang racket

(provide sei)

(define sei "○")

This works fine when imported from a .pm file in the project root, but from a file in a subdirectory such as grammar/index.html.pm, the dynamic-require tries to require the module at grammar/pollen/common.rkt.

I'm not getting much luck using `dynamic-require` from Pollen: pollen.rkt: ```racket #lang racket (displayln (dynamic-require "pollen/common.rkt" 'sei)) ``` pollen/common.rkt: ```racket #lang racket (provide sei) (define sei "○") ``` This works fine when imported from a .pm file in the project root, but from a file in a subdirectory such as `grammar/index.html.pm`, the `dynamic-require` tries to require the module at `grammar/pollen/common.rkt`.
bluebear94 commented 3 years ago (Migrated from github.com)

And now I have a proof of concept. Perhaps the grossest thing I've done in Racket.

(begin-for-syntax
  (define (target->module-path target)
    (format "pollen/~a.rkt" (symbol->string target)))
  (define (exports-from-target target)
    (define path (target->module-path target))
    (dynamic-require path #f)
    (define-values (vars syntaxes) (module->exports path))
    (define (get-ids-from-export-list el)
      (define el0 (assoc 0 el))
      (if el0
          (map car (cdr el0))
          '()))
    (append (get-ids-from-export-list vars)
            (get-ids-from-export-list syntaxes)))
  (define (exports-from-all-targets targets)
    (parameterize ([current-load-relative-directory "/home/kozet/ncv9/src"])
      (define exports (make-hash))
      (for ([target targets])
        (define target-exports (exports-from-target target))
        (for ([item-id target-exports])
          (define item (dynamic-require (target->module-path target) item-id))
          (hash-update! exports
                        item-id
                        (lambda (l) (cons (list target item) l))
                        '())
          )
        )
      exports))
  (define EXPORTS (exports-from-all-targets '(html tex)))
  )

(define-syntax (generate-reexports obj)
  (syntax-case obj ()
    [(_)
     (with-syntax
         ([(statements ...)
           (for/list ([(item-id items) (in-hash EXPORTS)])
             (with-syntax
                 ([item-id-stx (datum->syntax #'obj item-id)]
                  [(cases ...)
                   (for/list ([target+item items])
                     (define target (car target+item))
                     (define item (cadr target+item))
                     (with-syntax ([target-stx (datum->syntax #'obj target)]
                                   [item-stx (datum->syntax #'obj item)])
                       #'[(target-stx)
                          (keyword-apply item-stx kws kw-args rest)]))])
               #'(begin
                   (provide item-id-stx)
                   (define item-id-stx
                     (make-keyword-procedure
                      (lambda (kws kw-args . rest)
                        (case (current-poly-target)
                          cases ...
                          [else (error (quote item-id-stx) "not defined for ~a" (current-poly-target))]))))))
             )])
       #'(begin statements ...))
     ]))

(generate-reexports)

current-load-relative-directory is currently parameterized to a hard-coded path; I'm still looking for a way to avoid the hard-coding.

And now I have a proof of concept. Perhaps the grossest thing I've done in Racket. ```racket (begin-for-syntax (define (target->module-path target) (format "pollen/~a.rkt" (symbol->string target))) (define (exports-from-target target) (define path (target->module-path target)) (dynamic-require path #f) (define-values (vars syntaxes) (module->exports path)) (define (get-ids-from-export-list el) (define el0 (assoc 0 el)) (if el0 (map car (cdr el0)) '())) (append (get-ids-from-export-list vars) (get-ids-from-export-list syntaxes))) (define (exports-from-all-targets targets) (parameterize ([current-load-relative-directory "/home/kozet/ncv9/src"]) (define exports (make-hash)) (for ([target targets]) (define target-exports (exports-from-target target)) (for ([item-id target-exports]) (define item (dynamic-require (target->module-path target) item-id)) (hash-update! exports item-id (lambda (l) (cons (list target item) l)) '()) ) ) exports)) (define EXPORTS (exports-from-all-targets '(html tex))) ) (define-syntax (generate-reexports obj) (syntax-case obj () [(_) (with-syntax ([(statements ...) (for/list ([(item-id items) (in-hash EXPORTS)]) (with-syntax ([item-id-stx (datum->syntax #'obj item-id)] [(cases ...) (for/list ([target+item items]) (define target (car target+item)) (define item (cadr target+item)) (with-syntax ([target-stx (datum->syntax #'obj target)] [item-stx (datum->syntax #'obj item)]) #'[(target-stx) (keyword-apply item-stx kws kw-args rest)]))]) #'(begin (provide item-id-stx) (define item-id-stx (make-keyword-procedure (lambda (kws kw-args . rest) (case (current-poly-target) cases ... [else (error (quote item-id-stx) "not defined for ~a" (current-poly-target))])))))) )]) #'(begin statements ...)) ])) (generate-reexports) ``` `current-load-relative-directory` is currently parameterized to a hard-coded path; I'm still looking for a way to avoid the hard-coding.
bluebear94 commented 3 years ago (Migrated from github.com)

As I've mentioned earlier, we can't require pollen/setup for syntax because if we did, then loading pollen.rkt would require pollen/setup when compiling which depends on pollen.rkt for the setup parameters ad infinitum. This also prevents us from require-ing any Pollen-related modules in pollen/html.rkt or pollen/tex.rkt.

As I've mentioned earlier, we can't `require` `pollen/setup` for syntax because if we did, then loading `pollen.rkt` would require `pollen/setup` when compiling which depends on `pollen.rkt` for the setup parameters *ad infinitum*. This also prevents us from `require`-ing any Pollen-related modules in `pollen/html.rkt` or `pollen/tex.rkt`.
sorawee commented 3 years ago (Migrated from github.com)

Can you use define-runtime-path to solve the wrong directory problem?

Can you use `define-runtime-path` to solve the wrong directory problem?
bluebear94 commented 3 years ago (Migrated from github.com)

Perhaps, but I don't think it'll solve the import cycle.

Perhaps, but I don't think it'll solve the import cycle.
jnboehm commented 3 years ago (Migrated from github.com)

I think this is the most promising approach that I have come across: https://github.com/otherjoel/thenotepad/tree/master/pollen-local

It does some trickery with defining the functions according to a naming scheme, so it doesn't go by file itself but allows for separating it that way. I'd suggest at polytag.rkt, where the macro is defined, then the actual tag functions are in other files.

I think this is the most promising approach that I have come across: https://github.com/otherjoel/thenotepad/tree/master/pollen-local It does some trickery with defining the functions according to a naming scheme, so it doesn't go by file itself but allows for separating it that way. I'd suggest at `polytag.rkt`, where the macro is defined, then the actual tag functions are in other files.
otherjoel commented 3 years ago (Migrated from github.com)

I have a slightly more evolved version of that approach here: https://thelocalyarn.com/code/file?name=pollen.rkt&ci=tip

I have a slightly more evolved version of that approach here: https://thelocalyarn.com/code/file?name=pollen.rkt&ci=tip
This repo is archived. You cannot comment on issues.
No Milestone
No project
No Assignees
1 Participants
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: mbutterick/pollen-users#111
Loading…
There is no content yet.