#lang racket/base (provide schema->module-stx (struct-out schema-compiler-options) (struct-out schema-translation-paths) fold-Schema-definitions map-Schema-definitions) (require preserves) (require racket/match) (require (only-in racket/list append-map)) (require (only-in racket/string string-join)) (require (only-in racket/format ~a)) (require (only-in racket/syntax format-symbol)) (require "type.rkt") (require "parser.rkt") (require "unparser.rkt") (require "checker.rkt") (require "gen/schema.rkt") (struct schema-compiler-options (name lookup-module-path translation-paths) #:transparent) (struct schema-translation-paths (full-input-path relative-input-path full-output-path relative-output-path) #:transparent) (define (struct-stx name field-names more) `(struct ,name ,field-names #:transparent ,@more)) (define (ty->struct-field-names ty) (match ty [(ty-record fields) (map ty-field-name fields)] ;; not escaped here [(ty-unit) '()] [_ '(value)])) (define (fold-Schema-definitions kc kn schema) (foldr (lambda (entry acc) (kc (car entry) (cdr entry) acc)) kn (sorted-dict-entries (Definitions-value (Schema-definitions schema))))) (define (map-Schema-definitions proc schema) (fold-Schema-definitions (lambda (n p acc) (cons (proc n p) acc)) '() schema)) (define (module-imports name schema lookup-module-path translation-paths) (define imports (make-hash)) (define (import-ref! r) (match-define (Ref (ModulePath module-path) _name) r) (when (not (null? module-path)) (hash-set! imports module-path #t))) (define (walk x) (match (unwrap x) [(Definition-Pattern p) (walk p)] [(Definition-or p0 p1 pN) (for-each walk (list* p0 p1 pN))] [(Definition-and p0 p1 pN) (for-each walk (list* p0 p1 pN))] [(Binding n p) (walk p)] [(NamedAlternative _ p) (walk p)] [(SimplePattern-seqof p) (walk p)] [(SimplePattern-setof p) (walk p)] [(SimplePattern-dictof kp vp) (walk kp) (walk vp)] [(SimplePattern-Ref r) (import-ref! r)] [(? SimplePattern?) (void)] [(CompoundPattern-rec label-named-pat fields-named-pat) (walk label-named-pat) (walk fields-named-pat)] [(CompoundPattern-tuple named-pats) (for-each walk named-pats)] [(CompoundPattern-tuplePrefix fixed-named-pats variable-named-pat) (for-each walk fixed-named-pats) (walk variable-named-pat)] [(CompoundPattern-dict (DictionaryEntries entries)) (for-each walk (map cdr (sorted-dict-entries entries)))] [x (error 'module-imports "Unimplemented: ~v" x)])) (match (Schema-embeddedType schema) [(EmbeddedTypeName-false) (void)] [(EmbeddedTypeName-Ref r) (import-ref! r)]) (map-Schema-definitions (lambda (n p) (walk p)) schema) (for/list [(import (in-hash-keys imports))] (match (lookup-module-path import) [#f (error 'module-imports "Reference to unknown module ~a in module ~a~a" (string-join (map symbol->string import) ".") name (match translation-paths [(schema-translation-paths _ (? path-string? p) _ _) (format " (~a)" p)] [_ ""]))] [path `(require (prefix-in ,(module-path-prefix import) ,path))]))) (define (embedded-defs schema) (match (Schema-embeddedType schema) [(EmbeddedTypeName-false) `((define :decode-embedded values) (define :encode-embedded values))] [(EmbeddedTypeName-Ref r) `((define :decode-embedded ,(Ref-parser!-name r)) (define :encode-embedded ->preserve))])) (define (parse!-definition name) `(define ,(format-symbol "parse-~a!" name) (parse-success-or-error ',(format-symbol "parse-~a" name) ,(format-symbol "parse-~a" name)))) (define ((compile-definition plugins) name def acc) (define ty (definition-ty def)) (match def [(? Definition-or?) (define variants (ty-union-variants ty)) `[ (define (,(format-symbol "~a?" name) p) (or ,@(for/list [(variant (in-list variants))] `(,(format-symbol "~a-~a?" name (ty-variant-name variant)) p)))) ,@(for/list [(variant (in-list variants))] (match-define (ty-variant variant-name variant-ty variant-pat) variant) (define full-name (format-symbol "~a-~a" name variant-name)) (struct-stx full-name (ty->struct-field-names variant-ty) `[ #:methods gen:preservable [(define/generic *->preserve ->preserve) (define (->preserve preservable) (match preservable [,(deconstruct full-name variant-ty) ,(pattern->unparser variant-pat 'src)]))] ])) (define (,(format-symbol "parse-~a" name) input) (match input ,@(for/list [(variant (in-list variants))] (match-define (ty-variant variant-name variant-ty variant-pat) variant) `[,(pattern->match-pattern variant-pat 'dest) ,(construct (format-symbol "~a-~a" name variant-name) variant-ty)]) [_ eof])) ,(parse!-definition name) ,@acc ]] [(Definition-and p0 p1 pN) (define facets (list* p0 p1 pN)) `[ ,(struct-stx name (ty->struct-field-names ty) `[ #:methods gen:preservable [(define/generic *->preserve ->preserve) (define (->preserve preservable) (match preservable [,(deconstruct name ty) (merge-preserves (lambda (a b) (if (equal? a b) a (error 'merge-preserves "Cannot merge"))) ,@(append-map (lambda (named-pat) (match named-pat [(NamedPattern-anonymous (Pattern-SimplePattern _)) '()] [_ (list (pattern->unparser named-pat 'src))])) facets))]))] ]) (define (,(format-symbol "parse-~a" name) input) (match input [(and ,@(for/list [(named-pat facets)] (pattern->match-pattern named-pat '_))) ,(construct name ty)] [_ eof])) ,(parse!-definition name) ,@acc ]] [(Definition-Pattern pattern) `[ ,(struct-stx name (ty->struct-field-names ty) `[ #:methods gen:preservable [(define/generic *->preserve ->preserve) (define (->preserve preservable) (match preservable [,(deconstruct name ty) ,(pattern->unparser pattern 'src)]))] ]) (define (,(format-symbol "parse-~a" name) input) (match input [,(pattern->match-pattern pattern 'dest) ,(construct name ty)] [_ eof])) ,(parse!-definition name) ,@acc ]])) (define (deconstruct name ty) (match ty [(ty-record fields) `(,name ,@(map escape (map ty-field-name fields)))] [(ty-unit) `(,name)] [_ `(,name src)])) (define (construct name ty) (match ty [(ty-record fields) `(,name ,@(map escape (map ty-field-name fields)))] [(ty-unit) `(,name)] [_ `(,name dest)])) (define (schema->module-stx name lookup-module-path schema #:translation-paths [translation-paths #f] #:plugins [plugins '()] ) (schema-check-problems! schema #:name name) (define options (schema-compiler-options name lookup-module-path translation-paths)) `(module ,name racket/base (provide (except-out (all-defined-out) :decode-embedded :encode-embedded) (rename-out [:decode-embedded ,(format-symbol "decode-embedded:~a" name)] [:encode-embedded ,(format-symbol "encode-embedded:~a" name)])) (require preserves) (require preserves-schema/methods) (require preserves-schema/support) (require racket/match) (require racket/set) (require racket/dict) (require (only-in racket/generic define/generic)) ,@(module-imports name schema lookup-module-path translation-paths) ,@(embedded-defs schema) ,@(fold-Schema-definitions (compile-definition plugins) '() schema) ,@(for/list [(plugin (in-list plugins))] ((plugin 'schema) schema options)) )) (module+ main (require racket/pretty) (require racket/runtime-path) (require racket/path) (define-runtime-path schema-dir "../../../../schema") (define schema-file "schema.bin") (define schema-binary (build-path schema-dir schema-file)) (define target "gen/schema.rkt") (define metaschema (parse-Schema (with-input-from-file schema-binary read-preserve))) (define metaschema-module-source (schema->module-stx 'gen-schema (lambda (module-path) #f) metaschema #:translation-paths (schema-translation-paths schema-binary schema-file (simple-form-path target) target))) (if #t (with-output-to-file target #:exists 'replace (lambda () (pretty-write metaschema-module-source))) (pretty-write metaschema-module-source)))