From 9d72acd9a9c701b19914f70164a284086c2f3234 Mon Sep 17 00:00:00 2001 From: Siddhartha Date: Sat, 16 Nov 2024 13:01:34 -0700 Subject: [PATCH 01/22] Use `define-dsl-syntax` instead of `define-qi-syntax` `define-qi-syntax` was written before `define-dsl-syntax` existed, and it's better to use standard utilities from infrastructure libraries like `syntax-spec` where we can. (done in yesterday's Qi meeting) --- qi-lib/macro.rkt | 67 ++++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/qi-lib/macro.rkt b/qi-lib/macro.rkt index 7a0e84040..678063cbc 100644 --- a/qi-lib/macro.rkt +++ b/qi-lib/macro.rkt @@ -15,7 +15,8 @@ esc) qi/flow/space syntax/parse/define - syntax/parse) + syntax/parse + syntax-spec-v2) (begin-for-syntax @@ -62,50 +63,48 @@ (define (make-qi-foreign-syntax-transformer original-macro-id) (define/syntax-parse original-macro original-macro-id) - (qi-macro - (syntax-parser - [(name pre-form ... (~datum __) post-form ...) - (let ([name (syntax->datum #'name)]) - (raise-syntax-error name - (~a "Syntax error in " - `(,name - ,@(syntax->datum #'(pre-form ...)) - "__" - ,@(syntax->datum #'(post-form ...))) - "\n" - " __ templates are not supported for foreign macros.\n" - " Use _'s to indicate a specific number of expected arguments, instead.")))] - [(name pre-form ... (~datum _) post-form ...) - (foreign-macro-template-expand - (datum->syntax this-syntax - (cons #'original-macro - (cdr (syntax->list this-syntax)))))] - [(name form ...) - #:do [(define chirality (syntax-property this-syntax 'chirality))] - (if (and chirality (eq? chirality 'right)) - #'(esc (lambda (v) (original-macro form ... v))) - #'(esc (lambda (v) (original-macro v form ...))))] - [name:id #'(esc (lambda (v) (original-macro v)))])))) + (syntax-parser + [(name pre-form ... (~datum __) post-form ...) + (let ([name (syntax->datum #'name)]) + (raise-syntax-error name + (~a "Syntax error in " + `(,name + ,@(syntax->datum #'(pre-form ...)) + "__" + ,@(syntax->datum #'(post-form ...))) + "\n" + " __ templates are not supported for foreign macros.\n" + " Use _'s to indicate a specific number of expected arguments, instead.")))] + [(name pre-form ... (~datum _) post-form ...) + (foreign-macro-template-expand + (datum->syntax this-syntax + (cons #'original-macro + (cdr (syntax->list this-syntax)))))] + [(name form ...) + #:do [(define chirality (syntax-property this-syntax 'chirality))] + (if (and chirality (eq? chirality 'right)) + #'(esc (lambda (v) (original-macro form ... v))) + #'(esc (lambda (v) (original-macro v form ...))))] + [name:id #'(esc (lambda (v) (original-macro v)))]))) (define-syntax define-qi-syntax-rule (syntax-parser [(_ (name . pat) template) - #'(define-qi-syntax name - (qi-macro - (syntax-parser - [(_ . pat) #'template])))])) + #'(define-dsl-syntax name qi-macro + (syntax-parser + [(_ . pat) #'template]))])) (define-syntax define-qi-syntax-parser (syntax-parser [(_ name clause ...) - #'(define-qi-syntax name - (qi-macro - (syntax-parser - clause ...)))])) + #'(define-dsl-syntax name qi-macro + (syntax-parser + clause ...))])) (define-syntax define-qi-foreign-syntaxes (syntax-parser [(_ form-name ...) #'(begin - (define-qi-syntax form-name (make-qi-foreign-syntax-transformer #'form-name)) + (define-dsl-syntax form-name qi-macro + (make-qi-foreign-syntax-transformer #'form-name)) ...)])) From 0ee3d183da58543b3bee0cee36f98aaa312c8fb9 Mon Sep 17 00:00:00 2001 From: Siddhartha Date: Fri, 22 Nov 2024 09:58:51 -0700 Subject: [PATCH 02/22] Integrate POC extension of deforestation to implement `map` This integrates the proof-of-concept "deep macro" extension scheme for deforestation that we have been working on in the weekly meetings. For now, only `map` is implemented using the new approach, and though user-level tests pass, some compiler tests are currently failing. To keep things simple, this temporarily introduces a parallel `#%deforestable2` form for this purpose so that we can implement list operations one at a time and test things in isolation before going whole hog and replacing `#%deforestable` with the new version. --- qi-lib/flow/aux-syntax.rkt | 14 +++++++-- qi-lib/flow/core/compiler/1000-qi0.rkt | 22 +++++++++++--- qi-lib/flow/core/syntax.rkt | 7 ++++- qi-lib/flow/extended/expander.rkt | 5 +++ qi-lib/list.rkt | 9 ++++-- qi-lib/macro.rkt | 42 +++++++++++++++++++++++++- 6 files changed, 88 insertions(+), 11 deletions(-) diff --git a/qi-lib/flow/aux-syntax.rkt b/qi-lib/flow/aux-syntax.rkt index e5cf653a4..2ef0aa1a3 100644 --- a/qi-lib/flow/aux-syntax.rkt +++ b/qi-lib/flow/aux-syntax.rkt @@ -3,10 +3,12 @@ (provide literal subject clause - starts-with) + starts-with + (struct-out deforestable-info)) (require syntax/parse - racket/string) + racket/string + (for-syntax racket/base)) (define-syntax-class literal (pattern @@ -56,3 +58,11 @@ (symbol->string (syntax-e #'i)) pfx))) + +;; A datatype used at compile time to convey user-defined data through +;; the various stages of compilation for the purposes of extending Qi +;; deforestation to custom list operations. It is currently used to +;; convey a Racket runtime for macros in qi/list through to the code +;; generation stage of Qi compilation (and could be used by any similar +;; "deep" macros written by users). +(struct deforestable-info [codegen]) diff --git a/qi-lib/flow/core/compiler/1000-qi0.rkt b/qi-lib/flow/core/compiler/1000-qi0.rkt index de644c2b7..3d02807d7 100644 --- a/qi-lib/flow/core/compiler/1000-qi0.rkt +++ b/qi-lib/flow/core/compiler/1000-qi0.rkt @@ -6,6 +6,7 @@ racket/function racket/list (for-syntax racket/base + racket/match syntax/parse "../syntax.rkt" "../../aux-syntax.rkt" @@ -13,12 +14,9 @@ )) (begin-for-syntax - (define-and-register-pass 1000 (qi0-wrapper stx) (syntax-parse stx - (ex #'(qi0->racket ex)))) - - ) + (ex #'(qi0->racket ex))))) (define-syntax (qi0->racket stx) ;; this is a macro so it receives the entire expression @@ -96,6 +94,7 @@ [(~datum appleye) #'call] [e:clos-form (clos-parser #'e)] + [e:deforestable2-form (deforestable2-parser #'e)] [e:deforestable-form (deforestable-parser #'e)] ;; escape hatch for racket expressions or anything ;; to be "passed through" @@ -394,6 +393,21 @@ the DSL. (qi0->racket (~> (-< (~> (gen args) △) _) onex))))])) + (define (deforestable2-clause-parser c) + (syntax-parse c + [((~datum f) e) #'(qi0->racket e)] + [((~datum e) e) #'e])) + + (define (deforestable2-parser e) + (syntax-parse e + #:datum-literals (#%optimizable-app) + [((~datum #%deforestable2) op c ...) + (define es^ (map deforestable2-clause-parser (attribute c))) + (define info (syntax-local-value #'op)) + (match info + [(deforestable-info codegen) + (apply codegen es^)])])) + (define (deforestable-parser stx) (syntax-parse stx [((~datum #%deforestable) (~datum filter) (proc:clause)) diff --git a/qi-lib/flow/core/syntax.rkt b/qi-lib/flow/core/syntax.rkt index e70ff09e3..3ba787dc8 100644 --- a/qi-lib/flow/core/syntax.rkt +++ b/qi-lib/flow/core/syntax.rkt @@ -18,7 +18,8 @@ fold-right-form loop-form clos-form - deforestable-form) + deforestable-form + deforestable2-form) (require syntax/parse) @@ -139,3 +140,7 @@ See comments in flow.rkt for more details. (define-syntax-class deforestable-form (pattern ((~datum #%deforestable) arg ...))) + +(define-syntax-class deforestable2-form + (pattern + ((~datum #%deforestable2) arg ...))) diff --git a/qi-lib/flow/extended/expander.rkt b/qi-lib/flow/extended/expander.rkt index 580c138d3..bd6e53bfe 100644 --- a/qi-lib/flow/extended/expander.rkt +++ b/qi-lib/flow/extended/expander.rkt @@ -187,6 +187,7 @@ core language's use of #%app, etc.). (#%deforestable name:id (f:closed-floe ...) (arg:racket-expr ...)) (#%deforestable name:id (f:closed-floe ...+)) (#%deforestable name:id) + (#%deforestable2 name:id e:deforestable-clause ...) ;; backwards compat macro extensibility via Racket macros (~> ((~var ext-form (starts-with "qi:")) expr ...) @@ -242,6 +243,10 @@ core language's use of #%app, etc.). #:with spaced-f (introduce-qi-syntax #'f) #'(esc spaced-f))) + (nonterminal deforestable-clause + ((~datum f) e:closed-floe) + ((~datum e) g:racket-expr)) + (nonterminal arg-stx (~datum _) (~datum __) diff --git a/qi-lib/list.rkt b/qi-lib/list.rkt index 0d96362d3..6925f8f8d 100644 --- a/qi-lib/list.rkt +++ b/qi-lib/list.rkt @@ -9,10 +9,13 @@ "flow/extended/expander.rkt" (only-in "flow/space.rkt" define-qi-alias) - "macro.rkt") + "macro.rkt" + (prefix-in r: racket/base)) -(define-qi-syntax-rule (map f:expr) - (#%deforestable map (f))) +(define-deforestable + (map [f f]) + #'(lambda (vs) ; single list arg + (r:map f vs))) (define-qi-syntax-rule (filter f:expr) (#%deforestable filter (f))) diff --git a/qi-lib/macro.rkt b/qi-lib/macro.rkt index 678063cbc..ef09f3f63 100644 --- a/qi-lib/macro.rkt +++ b/qi-lib/macro.rkt @@ -4,6 +4,7 @@ define-qi-syntax-rule define-qi-syntax-parser define-qi-foreign-syntaxes + define-deforestable (for-syntax qi-macro)) (require (for-syntax racket/base @@ -12,8 +13,10 @@ racket/list) (only-in "flow/extended/expander.rkt" qi-macro - esc) + esc + #%deforestable2) qi/flow/space + (for-syntax qi/flow/aux-syntax) syntax/parse/define syntax/parse syntax-spec-v2) @@ -108,3 +111,40 @@ (define-dsl-syntax form-name qi-macro (make-qi-foreign-syntax-transformer #'form-name)) ...)])) + +(begin-for-syntax + (define (op-transformer info spec) + ;; use the `spec` to rewrite the source expression to expand + ;; to a corresponding number of clauses in the core form, like: + ;; (op e1 e2 e3) → (#%optimizable-app #,info [f e1] [e e2] [f e3]) + (syntax-parse spec + [([tag arg-name] ...) + (syntax-parser + [(_ e ...) (if (= (length (attribute e)) + (length (attribute arg-name))) + #`(#%deforestable2 #,info [tag e] ...) + (raise-syntax-error #f + "Wrong number of arguments!" + this-syntax))])]))) + +(define-syntax define-deforestable + (syntax-parser + [(_ (name spec ...) codegen) + #:with ([typ arg] ...) #'(spec ...) + #:with codegen-f #'(lambda (arg ...) + ;; var bindings vs pattern bindings + ;; arg are syntax objects but we can't + ;; use them as variable bindings, so + ;; we use with-syntax to handle them + ;; as pattern bindings + (with-syntax ([arg arg] ...) + codegen)) + #'(begin + + ;; capture the codegen in an instance of + ;; the compile time struct + (define-syntax info + (deforestable-info codegen-f)) + + (define-dsl-syntax name qi-macro + (op-transformer #'info #'(spec ...))))])) From e84d87f11d34381b8935cf101594663402198c12 Mon Sep 17 00:00:00 2001 From: Siddhartha Date: Fri, 22 Nov 2024 15:36:51 -0700 Subject: [PATCH 03/22] Add a `name` argument to `define-deforestable` The deforestation pass needs a name (e.g. `map`) to match in order to apply optimizations. This updates the syntax class responsible for matching `map` to use the new deforestable core form syntax and fixes the failing compiler tests. (done in today's meeting) --- qi-lib/flow/core/compiler/1000-qi0.rkt | 12 ++++++------ qi-lib/flow/core/compiler/deforest/syntax.rkt | 2 +- qi-lib/flow/extended/expander.rkt | 2 +- qi-lib/macro.rkt | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/qi-lib/flow/core/compiler/1000-qi0.rkt b/qi-lib/flow/core/compiler/1000-qi0.rkt index 3d02807d7..abb584e95 100644 --- a/qi-lib/flow/core/compiler/1000-qi0.rkt +++ b/qi-lib/flow/core/compiler/1000-qi0.rkt @@ -401,12 +401,12 @@ the DSL. (define (deforestable2-parser e) (syntax-parse e #:datum-literals (#%optimizable-app) - [((~datum #%deforestable2) op c ...) - (define es^ (map deforestable2-clause-parser (attribute c))) - (define info (syntax-local-value #'op)) - (match info - [(deforestable-info codegen) - (apply codegen es^)])])) + [((~datum #%deforestable2) _name info c ...) + (let ([es^ (map deforestable2-clause-parser (attribute c))] + [info (syntax-local-value #'info)]) + (match info + [(deforestable-info codegen) + (apply codegen es^)]))])) (define (deforestable-parser stx) (syntax-parse stx diff --git a/qi-lib/flow/core/compiler/deforest/syntax.rkt b/qi-lib/flow/core/compiler/deforest/syntax.rkt index 405c81578..bcc65ac9d 100644 --- a/qi-lib/flow/core/compiler/deforest/syntax.rkt +++ b/qi-lib/flow/core/compiler/deforest/syntax.rkt @@ -85,7 +85,7 @@ #:attributes (f) #:literal-sets (fs-literals) #:datum-literals (map) - (pattern (#%deforestable map (f-uncompiled)) + (pattern (#%deforestable2 map _info ((~datum f) f-uncompiled)) #:attr f (run-passes #'f-uncompiled))) (define-syntax-class fst-filter-map diff --git a/qi-lib/flow/extended/expander.rkt b/qi-lib/flow/extended/expander.rkt index bd6e53bfe..04abd74a1 100644 --- a/qi-lib/flow/extended/expander.rkt +++ b/qi-lib/flow/extended/expander.rkt @@ -187,7 +187,7 @@ core language's use of #%app, etc.). (#%deforestable name:id (f:closed-floe ...) (arg:racket-expr ...)) (#%deforestable name:id (f:closed-floe ...+)) (#%deforestable name:id) - (#%deforestable2 name:id e:deforestable-clause ...) + (#%deforestable2 name:id info:id e:deforestable-clause ...) ;; backwards compat macro extensibility via Racket macros (~> ((~var ext-form (starts-with "qi:")) expr ...) diff --git a/qi-lib/macro.rkt b/qi-lib/macro.rkt index ef09f3f63..eb532bd56 100644 --- a/qi-lib/macro.rkt +++ b/qi-lib/macro.rkt @@ -113,7 +113,7 @@ ...)])) (begin-for-syntax - (define (op-transformer info spec) + (define (op-transformer name info spec) ;; use the `spec` to rewrite the source expression to expand ;; to a corresponding number of clauses in the core form, like: ;; (op e1 e2 e3) → (#%optimizable-app #,info [f e1] [e e2] [f e3]) @@ -122,7 +122,7 @@ (syntax-parser [(_ e ...) (if (= (length (attribute e)) (length (attribute arg-name))) - #`(#%deforestable2 #,info [tag e] ...) + #`(#%deforestable2 #,name #,info [tag e] ...) (raise-syntax-error #f "Wrong number of arguments!" this-syntax))])]))) @@ -147,4 +147,4 @@ (deforestable-info codegen-f)) (define-dsl-syntax name qi-macro - (op-transformer #'info #'(spec ...))))])) + (op-transformer #'name #'info #'(spec ...))))])) From 39d0aba290a4b91200523694e03eac3beb84e310 Mon Sep 17 00:00:00 2001 From: Siddhartha Date: Fri, 22 Nov 2024 16:24:15 -0700 Subject: [PATCH 04/22] `make` target to run just `qi/list` tests --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index fd82677d3..3658b7c1d 100644 --- a/Makefile +++ b/Makefile @@ -125,6 +125,9 @@ test: test-flow: racket -y $(PACKAGE-NAME)-test/tests/flow.rkt +test-list: + racket -y $(PACKAGE-NAME)-test/tests/list.rkt + test-on: racket -y $(PACKAGE-NAME)-test/tests/on.rkt From 4d29edefd363020e1c5854298d7ef4644799573e Mon Sep 17 00:00:00 2001 From: Siddhartha Date: Fri, 22 Nov 2024 16:25:12 -0700 Subject: [PATCH 05/22] translate `filter` to use `define-deforestable` --- qi-lib/flow/core/compiler/deforest/syntax.rkt | 3 ++- qi-lib/list.rkt | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/qi-lib/flow/core/compiler/deforest/syntax.rkt b/qi-lib/flow/core/compiler/deforest/syntax.rkt index bcc65ac9d..ed20dcb4b 100644 --- a/qi-lib/flow/core/compiler/deforest/syntax.rkt +++ b/qi-lib/flow/core/compiler/deforest/syntax.rkt @@ -79,8 +79,9 @@ #:attributes (f) #:literal-sets (fs-literals) #:datum-literals (filter) - (pattern (#%deforestable filter (f-uncompiled)) + (pattern (#%deforestable2 filter _info ((~datum f) f-uncompiled)) #:attr f (run-passes #'f-uncompiled))) + (define-syntax-class fst-map #:attributes (f) #:literal-sets (fs-literals) diff --git a/qi-lib/list.rkt b/qi-lib/list.rkt index 6925f8f8d..d81466136 100644 --- a/qi-lib/list.rkt +++ b/qi-lib/list.rkt @@ -17,8 +17,10 @@ #'(lambda (vs) ; single list arg (r:map f vs))) -(define-qi-syntax-rule (filter f:expr) - (#%deforestable filter (f))) +(define-deforestable + (filter [f f]) + #'(λ (vs) + (r:filter f vs))) (define-qi-syntax-rule (filter-map f:expr) (#%deforestable filter-map (f))) From ce964f07de326c9e1d40781ce790c6ffef463087 Mon Sep 17 00:00:00 2001 From: Siddhartha Date: Fri, 22 Nov 2024 16:44:49 -0700 Subject: [PATCH 06/22] translate `filter-map` to use `define-deforestable` --- qi-lib/flow/core/compiler/deforest/syntax.rkt | 2 +- qi-lib/list.rkt | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/qi-lib/flow/core/compiler/deforest/syntax.rkt b/qi-lib/flow/core/compiler/deforest/syntax.rkt index ed20dcb4b..f42437fea 100644 --- a/qi-lib/flow/core/compiler/deforest/syntax.rkt +++ b/qi-lib/flow/core/compiler/deforest/syntax.rkt @@ -93,7 +93,7 @@ #:attributes (f) #:literal-sets (fs-literals) #:datum-literals (filter-map) - (pattern (#%deforestable filter-map (f-uncompiled)) + (pattern (#%deforestable2 filter-map _info ((~datum f) f-uncompiled)) #:attr f (run-passes #'f-uncompiled))) (define-syntax-class fst-take diff --git a/qi-lib/list.rkt b/qi-lib/list.rkt index d81466136..47fb4d53f 100644 --- a/qi-lib/list.rkt +++ b/qi-lib/list.rkt @@ -10,7 +10,8 @@ (only-in "flow/space.rkt" define-qi-alias) "macro.rkt" - (prefix-in r: racket/base)) + (prefix-in r: racket/base) + (prefix-in r: racket/list)) (define-deforestable (map [f f]) @@ -22,8 +23,10 @@ #'(λ (vs) (r:filter f vs))) -(define-qi-syntax-rule (filter-map f:expr) - (#%deforestable filter-map (f))) +(define-deforestable + (filter-map [f f]) + #'(λ (vs) + (r:filter-map f vs))) (define-qi-syntax-rule (foldl f:expr init:expr) (#%deforestable foldl (f) (init))) From 0db81b0fdd909bc4c587514276cf61863c03d3e4 Mon Sep 17 00:00:00 2001 From: Siddhartha Date: Fri, 22 Nov 2024 16:47:47 -0700 Subject: [PATCH 07/22] translate `foldl` and `foldr` --- qi-lib/flow/core/compiler/deforest/syntax.rkt | 14 ++++++++------ qi-lib/list.rkt | 12 ++++++++---- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/qi-lib/flow/core/compiler/deforest/syntax.rkt b/qi-lib/flow/core/compiler/deforest/syntax.rkt index f42437fea..ed38c671e 100644 --- a/qi-lib/flow/core/compiler/deforest/syntax.rkt +++ b/qi-lib/flow/core/compiler/deforest/syntax.rkt @@ -124,20 +124,22 @@ #:attributes (op init) #:literal-sets (fs-literals) #:datum-literals (foldr) - (pattern (#%deforestable + (pattern (#%deforestable2 foldr - (op-uncompiled) - ((#%host-expression init))) + _name + ((~datum f) op-uncompiled) + ((~datum e) init)) #:attr op (run-passes #'op-uncompiled))) (define-syntax-class fsc-foldl #:attributes (op init) #:literal-sets (fs-literals) #:datum-literals (foldl) - (pattern (#%deforestable + (pattern (#%deforestable2 foldl - (op-uncompiled) - ((#%host-expression init))) + _name + ((~datum f) op-uncompiled) + ((~datum e) init)) #:attr op (run-passes #'op-uncompiled))) (define-syntax-class cad*r-datum diff --git a/qi-lib/list.rkt b/qi-lib/list.rkt index 47fb4d53f..091b31561 100644 --- a/qi-lib/list.rkt +++ b/qi-lib/list.rkt @@ -28,11 +28,15 @@ #'(λ (vs) (r:filter-map f vs))) -(define-qi-syntax-rule (foldl f:expr init:expr) - (#%deforestable foldl (f) (init))) +(define-deforestable + (foldl [f f] [e init]) + #'(λ (vs) + (r:foldl f init vs))) -(define-qi-syntax-rule (foldr f:expr init:expr) - (#%deforestable foldr (f) (init))) +(define-deforestable + (foldr [f f] [e init]) + #'(λ (vs) + (r:foldr f init vs))) (define-qi-syntax-parser range [(_ low:expr high:expr step:expr) #'(#%deforestable range () (low high step))] From 1a5e4d1dcd17b9b597013d4fd8d34168fd21814d Mon Sep 17 00:00:00 2001 From: Siddhartha Date: Fri, 22 Nov 2024 16:59:37 -0700 Subject: [PATCH 08/22] translate `take` to use `define-deforestable` --- qi-lib/flow/core/compiler/deforest/syntax.rkt | 2 +- qi-lib/list.rkt | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/qi-lib/flow/core/compiler/deforest/syntax.rkt b/qi-lib/flow/core/compiler/deforest/syntax.rkt index ed38c671e..826c364be 100644 --- a/qi-lib/flow/core/compiler/deforest/syntax.rkt +++ b/qi-lib/flow/core/compiler/deforest/syntax.rkt @@ -100,7 +100,7 @@ #:attributes (n) #:literal-sets (fs-literals) #:datum-literals (take) - (pattern (#%deforestable take () ((#%host-expression n))))) + (pattern (#%deforestable2 take _info ((~datum e) n)))) (define-syntax-class fst-syntax0 (pattern (~or _:fst-filter diff --git a/qi-lib/list.rkt b/qi-lib/list.rkt index 091b31561..bc42e9a3a 100644 --- a/qi-lib/list.rkt +++ b/qi-lib/list.rkt @@ -49,8 +49,10 @@ "(range arg ...)" "range expects at least one argument")]) -(define-qi-syntax-rule (take n:expr) - (#%deforestable take () (n))) +(define-deforestable + (take [e n]) + #'(λ (vs) + (r:take vs n))) (define-qi-syntax-parser car [_:id #'(#%deforestable car)]) From ae06392225269c1ee4da60b9e7c1abb2c5aadfd8 Mon Sep 17 00:00:00 2001 From: Siddhartha Date: Fri, 22 Nov 2024 17:01:21 -0700 Subject: [PATCH 09/22] format like ordinary definitions --- qi-lib/list.rkt | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/qi-lib/list.rkt b/qi-lib/list.rkt index bc42e9a3a..38ab372ba 100644 --- a/qi-lib/list.rkt +++ b/qi-lib/list.rkt @@ -13,28 +13,23 @@ (prefix-in r: racket/base) (prefix-in r: racket/list)) -(define-deforestable - (map [f f]) +(define-deforestable (map [f f]) #'(lambda (vs) ; single list arg (r:map f vs))) -(define-deforestable - (filter [f f]) +(define-deforestable (filter [f f]) #'(λ (vs) (r:filter f vs))) -(define-deforestable - (filter-map [f f]) +(define-deforestable (filter-map [f f]) #'(λ (vs) (r:filter-map f vs))) -(define-deforestable - (foldl [f f] [e init]) +(define-deforestable (foldl [f f] [e init]) #'(λ (vs) (r:foldl f init vs))) -(define-deforestable - (foldr [f f] [e init]) +(define-deforestable (foldr [f f] [e init]) #'(λ (vs) (r:foldr f init vs))) @@ -49,8 +44,7 @@ "(range arg ...)" "range expects at least one argument")]) -(define-deforestable - (take [e n]) +(define-deforestable (take [e n]) #'(λ (vs) (r:take vs n))) From 2e376f9863391e3f532b6d34b184a98e0ed3c9b4 Mon Sep 17 00:00:00 2001 From: Siddhartha Date: Fri, 22 Nov 2024 17:02:17 -0700 Subject: [PATCH 10/22] fix ignored variable name --- qi-lib/flow/core/compiler/deforest/syntax.rkt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qi-lib/flow/core/compiler/deforest/syntax.rkt b/qi-lib/flow/core/compiler/deforest/syntax.rkt index 826c364be..bcd7b7cba 100644 --- a/qi-lib/flow/core/compiler/deforest/syntax.rkt +++ b/qi-lib/flow/core/compiler/deforest/syntax.rkt @@ -126,7 +126,7 @@ #:datum-literals (foldr) (pattern (#%deforestable2 foldr - _name + _info ((~datum f) op-uncompiled) ((~datum e) init)) #:attr op (run-passes #'op-uncompiled))) @@ -137,7 +137,7 @@ #:datum-literals (foldl) (pattern (#%deforestable2 foldl - _name + _info ((~datum f) op-uncompiled) ((~datum e) init)) #:attr op (run-passes #'op-uncompiled))) From 45f29ec4ab24a6032bbbc9bb088572a725852571 Mon Sep 17 00:00:00 2001 From: Siddhartha Date: Fri, 22 Nov 2024 17:06:04 -0700 Subject: [PATCH 11/22] adopt style suggestion from Ben / resyntax (cr) --- qi-lib/flow/core/compiler/1000-qi0.rkt | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/qi-lib/flow/core/compiler/1000-qi0.rkt b/qi-lib/flow/core/compiler/1000-qi0.rkt index abb584e95..4ddae7e16 100644 --- a/qi-lib/flow/core/compiler/1000-qi0.rkt +++ b/qi-lib/flow/core/compiler/1000-qi0.rkt @@ -400,13 +400,11 @@ the DSL. (define (deforestable2-parser e) (syntax-parse e - #:datum-literals (#%optimizable-app) - [((~datum #%deforestable2) _name info c ...) - (let ([es^ (map deforestable2-clause-parser (attribute c))] - [info (syntax-local-value #'info)]) - (match info - [(deforestable-info codegen) - (apply codegen es^)]))])) + #:datum-literals (#%deforestable2) + [(#%deforestable2 _name info c ...) + (let ([es^ (map deforestable2-clause-parser (attribute c))]) + (match-let ([(deforestable-info codegen) (syntax-local-value #'info)]) + (apply codegen es^)))])) (define (deforestable-parser stx) (syntax-parse stx From 19d50681f639dea19cbc2327b8154ea890ac7383 Mon Sep 17 00:00:00 2001 From: Siddhartha Date: Fri, 22 Nov 2024 19:12:04 -0700 Subject: [PATCH 12/22] translate `car`, `cad*r` etc., and `list-ref` Our extension scheme did not support identifier-based forms like `car`, so a few modifications to it were necessary. --- qi-lib/flow/core/compiler/deforest/syntax.rkt | 13 +++++---- qi-lib/list.rkt | 21 +++++++------- qi-lib/macro.rkt | 28 ++++++++++++++----- 3 files changed, 39 insertions(+), 23 deletions(-) diff --git a/qi-lib/flow/core/compiler/deforest/syntax.rkt b/qi-lib/flow/core/compiler/deforest/syntax.rkt index bcd7b7cba..d08045888 100644 --- a/qi-lib/flow/core/compiler/deforest/syntax.rkt +++ b/qi-lib/flow/core/compiler/deforest/syntax.rkt @@ -144,17 +144,18 @@ (define-syntax-class cad*r-datum #:attributes (countdown) - (pattern (#%deforestable (~datum car)) #:attr countdown #'0) - (pattern (#%deforestable (~datum cadr)) #:attr countdown #'1) - (pattern (#%deforestable (~datum caddr)) #:attr countdown #'2) - (pattern (#%deforestable (~datum cadddr)) #:attr countdown #'3)) + #:datum-literals (#%deforestable2 car cadr caddr cadddr) + (pattern (#%deforestable2 car _info) #:attr countdown #'0) + (pattern (#%deforestable2 cadr _info) #:attr countdown #'1) + (pattern (#%deforestable2 caddr _info) #:attr countdown #'2) + (pattern (#%deforestable2 cadddr _info) #:attr countdown #'3)) (define-syntax-class fsc-list-ref #:attributes (pos name) #:literal-sets (fs-literals) - #:datum-literals (list-ref) + #:datum-literals (#%deforestable2 list-ref) ;; TODO: need #%host-expression wrapping idx? - (pattern (#%deforestable list-ref () (idx)) + (pattern (#%deforestable2 list-ref _info ((~datum e) idx)) #:attr pos #'idx #:attr name #'list-ref) ;; TODO: bring wrapping #%deforestable out here? diff --git a/qi-lib/list.rkt b/qi-lib/list.rkt index 38ab372ba..47d4f553f 100644 --- a/qi-lib/list.rkt +++ b/qi-lib/list.rkt @@ -48,20 +48,21 @@ #'(λ (vs) (r:take vs n))) -(define-qi-syntax-parser car - [_:id #'(#%deforestable car)]) +(define-deforestable car + #'r:car) -(define-qi-syntax-parser cadr - [_:id #'(#%deforestable cadr)]) +(define-deforestable cadr + #'r:cadr) -(define-qi-syntax-parser caddr - [_:id #'(#%deforestable caddr)]) +(define-deforestable caddr + #'r:caddr) -(define-qi-syntax-parser cadddr - [_:id #'(#%deforestable cadddr)]) +(define-deforestable cadddr + #'r:cadddr) -(define-qi-syntax-rule (list-ref n:expr) - (#%deforestable list-ref () (n))) +(define-deforestable (list-ref [e n]) + #'(λ (vs) + (r:list-ref vs n))) (define-qi-syntax-parser length [_:id #'(#%deforestable length)]) diff --git a/qi-lib/macro.rkt b/qi-lib/macro.rkt index eb532bd56..e990569fd 100644 --- a/qi-lib/macro.rkt +++ b/qi-lib/macro.rkt @@ -120,12 +120,15 @@ (syntax-parse spec [([tag arg-name] ...) (syntax-parser - [(_ e ...) (if (= (length (attribute e)) - (length (attribute arg-name))) - #`(#%deforestable2 #,name #,info [tag e] ...) - (raise-syntax-error #f - "Wrong number of arguments!" - this-syntax))])]))) + [(_ e ...+) (if (= (length (attribute e)) + (length (attribute arg-name))) + #`(#%deforestable2 #,name #,info [tag e] ...) + (raise-syntax-error #f + "Wrong number of arguments!" + this-syntax))] + ;; TODO, check: instead of `car`, does `(car)` produce + ;; a useful syntax error? + [_:id #`(#%deforestable2 #,name #,info)])]))) (define-syntax define-deforestable (syntax-parser @@ -147,4 +150,15 @@ (deforestable-info codegen-f)) (define-dsl-syntax name qi-macro - (op-transformer #'name #'info #'(spec ...))))])) + (op-transformer #'name #'info #'(spec ...))))] + [(_ name:id codegen) + #:with codegen-f #'(lambda () codegen) + #'(begin + + ;; capture the codegen in an instance of + ;; the compile time struct + (define-syntax info + (deforestable-info codegen-f)) + + (define-dsl-syntax name qi-macro + (op-transformer #'name #'info #'())))])) From a5f26527adfbd45a494e5d6bafbef116c4cf841f Mon Sep 17 00:00:00 2001 From: Siddhartha Date: Fri, 22 Nov 2024 19:14:56 -0700 Subject: [PATCH 13/22] declare `deforestable2` as a datum literal This avoids an "unused binding" warning --- qi-lib/flow/core/compiler/deforest/syntax.rkt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/qi-lib/flow/core/compiler/deforest/syntax.rkt b/qi-lib/flow/core/compiler/deforest/syntax.rkt index d08045888..8aaedf17a 100644 --- a/qi-lib/flow/core/compiler/deforest/syntax.rkt +++ b/qi-lib/flow/core/compiler/deforest/syntax.rkt @@ -78,28 +78,28 @@ (define-syntax-class fst-filter #:attributes (f) #:literal-sets (fs-literals) - #:datum-literals (filter) + #:datum-literals (#%deforestable2 filter) (pattern (#%deforestable2 filter _info ((~datum f) f-uncompiled)) #:attr f (run-passes #'f-uncompiled))) (define-syntax-class fst-map #:attributes (f) #:literal-sets (fs-literals) - #:datum-literals (map) + #:datum-literals (#%deforestable2 map) (pattern (#%deforestable2 map _info ((~datum f) f-uncompiled)) #:attr f (run-passes #'f-uncompiled))) (define-syntax-class fst-filter-map #:attributes (f) #:literal-sets (fs-literals) - #:datum-literals (filter-map) + #:datum-literals (#%deforestable2 filter-map) (pattern (#%deforestable2 filter-map _info ((~datum f) f-uncompiled)) #:attr f (run-passes #'f-uncompiled))) (define-syntax-class fst-take #:attributes (n) #:literal-sets (fs-literals) - #:datum-literals (take) + #:datum-literals (#%deforestable2 take) (pattern (#%deforestable2 take _info ((~datum e) n)))) (define-syntax-class fst-syntax0 @@ -123,7 +123,7 @@ (define-syntax-class fsc-foldr #:attributes (op init) #:literal-sets (fs-literals) - #:datum-literals (foldr) + #:datum-literals (#%deforestable2 foldr) (pattern (#%deforestable2 foldr _info @@ -134,7 +134,7 @@ (define-syntax-class fsc-foldl #:attributes (op init) #:literal-sets (fs-literals) - #:datum-literals (foldl) + #:datum-literals (#%deforestable2 foldl) (pattern (#%deforestable2 foldl _info From 8a5d650718f2c775ebdddc106df9f5b125f42603 Mon Sep 17 00:00:00 2001 From: Siddhartha Date: Fri, 22 Nov 2024 19:18:41 -0700 Subject: [PATCH 14/22] translate `length` to use `define-deforestable` --- qi-lib/flow/core/compiler/deforest/syntax.rkt | 4 ++-- qi-lib/list.rkt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/qi-lib/flow/core/compiler/deforest/syntax.rkt b/qi-lib/flow/core/compiler/deforest/syntax.rkt index 8aaedf17a..0701b7624 100644 --- a/qi-lib/flow/core/compiler/deforest/syntax.rkt +++ b/qi-lib/flow/core/compiler/deforest/syntax.rkt @@ -165,8 +165,8 @@ (define-syntax-class fsc-length #:literal-sets (fs-literals) - #:datum-literals (length) - (pattern (#%deforestable length))) + #:datum-literals (#%deforestable2 length) + (pattern (#%deforestable2 length _info))) (define-syntax-class fsc-empty? #:literal-sets (fs-literals) diff --git a/qi-lib/list.rkt b/qi-lib/list.rkt index 47d4f553f..42337c96a 100644 --- a/qi-lib/list.rkt +++ b/qi-lib/list.rkt @@ -64,8 +64,8 @@ #'(λ (vs) (r:list-ref vs n))) -(define-qi-syntax-parser length - [_:id #'(#%deforestable length)]) +(define-deforestable length + #'r:length) (define-qi-syntax-parser empty? [_:id #'(#%deforestable empty?)]) From f6cecbb043e88082366dc030e11fb6e7240a1a87 Mon Sep 17 00:00:00 2001 From: Siddhartha Date: Fri, 22 Nov 2024 19:23:40 -0700 Subject: [PATCH 15/22] translate `empty?` and (its alias) `null?` to `define-deforestable` --- qi-lib/flow/core/compiler/deforest/syntax.rkt | 5 ++--- qi-lib/list.rkt | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/qi-lib/flow/core/compiler/deforest/syntax.rkt b/qi-lib/flow/core/compiler/deforest/syntax.rkt index 0701b7624..ef2965ac4 100644 --- a/qi-lib/flow/core/compiler/deforest/syntax.rkt +++ b/qi-lib/flow/core/compiler/deforest/syntax.rkt @@ -170,9 +170,8 @@ (define-syntax-class fsc-empty? #:literal-sets (fs-literals) - #:datum-literals (null? empty?) - (pattern (#%deforestable (~or empty? - null?)))) + #:datum-literals (#%deforestable2 empty?) ; note: null? expands to empty? + (pattern (#%deforestable2 empty? _info))) (define-syntax-class fsc-default #:datum-literals (cstream->list) diff --git a/qi-lib/list.rkt b/qi-lib/list.rkt index 42337c96a..03dc87d9d 100644 --- a/qi-lib/list.rkt +++ b/qi-lib/list.rkt @@ -67,7 +67,7 @@ (define-deforestable length #'r:length) -(define-qi-syntax-parser empty? - [_:id #'(#%deforestable empty?)]) +(define-deforestable empty? + #'r:empty?) (define-qi-alias null? empty?) From c047a0170109813b3388b20873e21a67f6cd8ebf Mon Sep 17 00:00:00 2001 From: Siddhartha Date: Fri, 22 Nov 2024 23:43:06 -0700 Subject: [PATCH 16/22] Use the "clever hack" to translate `range` We'd like to support multiple syntaxes for `range` that all expand to a canonical form, and at the same time, provide a single codegen for that canonical form. We don't already have a way to do this with `define-deforestable` directly, so we just write `range` as an ordinary Qi macro that expands to a use of the canonically-defined `range2`. See today's meeting notes for more on this. --- qi-lib/flow/core/compiler/deforest/syntax.rkt | 4 ++-- qi-lib/list.rkt | 10 +++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/qi-lib/flow/core/compiler/deforest/syntax.rkt b/qi-lib/flow/core/compiler/deforest/syntax.rkt index ef2965ac4..7daf29427 100644 --- a/qi-lib/flow/core/compiler/deforest/syntax.rkt +++ b/qi-lib/flow/core/compiler/deforest/syntax.rkt @@ -49,8 +49,8 @@ (define-syntax-class fsp-range #:attributes (blanket? fine? arg pre-arg post-arg) #:literal-sets (fs-literals) - #:datum-literals (range) - (pattern (#%deforestable range () (the-arg ...)) + #:datum-literals (#%deforestable2 range2) + (pattern (#%deforestable2 range2 _info ((~datum e) the-arg) ...) #:attr arg #'(the-arg ...) #:attr pre-arg #f #:attr post-arg #f diff --git a/qi-lib/list.rkt b/qi-lib/list.rkt index 03dc87d9d..42ef47670 100644 --- a/qi-lib/list.rkt +++ b/qi-lib/list.rkt @@ -33,10 +33,14 @@ #'(λ (vs) (r:foldr f init vs))) +(define-deforestable (range2 [e low] [e high] [e step]) + #'(λ () + (r:range low high step))) + (define-qi-syntax-parser range - [(_ low:expr high:expr step:expr) #'(#%deforestable range () (low high step))] - [(_ low:expr high:expr) #'(#%deforestable range () (low high 1))] - [(_ high:expr) #'(#%deforestable range () (0 high 1))] + [(_ low:expr high:expr step:expr) #'(range2 low high step)] + [(_ low:expr high:expr) #'(range2 low high 1)] + [(_ high:expr) #'(range2 0 high 1)] ;; not strictly necessary but this provides a better error ;; message than simply "range: bad syntax" that's warranted ;; to differentiate from racket/list's `range` From 0df78235558f4482b7ec21ec8f0c3bc8dc116987 Mon Sep 17 00:00:00 2001 From: Siddhartha Date: Fri, 22 Nov 2024 23:56:42 -0700 Subject: [PATCH 17/22] Be cleverer in hiding the "clever hack" Use `range` internally, and re-export the user-facing `range2` so it is also visible as `range` externally, for the best of both worlds. --- qi-lib/flow/core/compiler/deforest/syntax.rkt | 4 ++-- qi-lib/list.rkt | 22 ++++++++++++++----- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/qi-lib/flow/core/compiler/deforest/syntax.rkt b/qi-lib/flow/core/compiler/deforest/syntax.rkt index 7daf29427..2f01e629a 100644 --- a/qi-lib/flow/core/compiler/deforest/syntax.rkt +++ b/qi-lib/flow/core/compiler/deforest/syntax.rkt @@ -49,8 +49,8 @@ (define-syntax-class fsp-range #:attributes (blanket? fine? arg pre-arg post-arg) #:literal-sets (fs-literals) - #:datum-literals (#%deforestable2 range2) - (pattern (#%deforestable2 range2 _info ((~datum e) the-arg) ...) + #:datum-literals (#%deforestable2 range) + (pattern (#%deforestable2 range _info ((~datum e) the-arg) ...) #:attr arg #'(the-arg ...) #:attr pre-arg #f #:attr post-arg #f diff --git a/qi-lib/list.rkt b/qi-lib/list.rkt index 42ef47670..dd0c57fd4 100644 --- a/qi-lib/list.rkt +++ b/qi-lib/list.rkt @@ -1,7 +1,10 @@ #lang racket/base (provide (for-space qi - (all-defined-out))) + (except-out (all-defined-out) + range2 + range) + (rename-out [range2 range]))) (require (for-syntax racket/base "private/util.rkt") @@ -33,14 +36,21 @@ #'(λ (vs) (r:foldr f init vs))) -(define-deforestable (range2 [e low] [e high] [e step]) +(define-deforestable (range [e low] [e high] [e step]) #'(λ () (r:range low high step))) -(define-qi-syntax-parser range - [(_ low:expr high:expr step:expr) #'(range2 low high step)] - [(_ low:expr high:expr) #'(range2 low high 1)] - [(_ high:expr) #'(range2 0 high 1)] +;; We'd like to indicate multiple surface variants for `range` that +;; expand to a canonical form, and provide a single codegen just for the +;; canonical form. +;; Since `define-deforestable` doesn't support indicating multiple cases +;; yet, we use the ordinary macro machinery to expand surface variants of +;; `range` to a canonical form that is defined using +;; `define-deforestable`. +(define-qi-syntax-parser range2 + [(_ low:expr high:expr step:expr) #'(range low high step)] + [(_ low:expr high:expr) #'(range low high 1)] + [(_ high:expr) #'(range 0 high 1)] ;; not strictly necessary but this provides a better error ;; message than simply "range: bad syntax" that's warranted ;; to differentiate from racket/list's `range` From 3f8a7c4579e50db73a40a4b529eb6e7b680e10da Mon Sep 17 00:00:00 2001 From: Siddhartha Date: Sat, 23 Nov 2024 00:05:04 -0700 Subject: [PATCH 18/22] adopt `syntax-parse` idiom for errors (cr) --- qi-lib/macro.rkt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/qi-lib/macro.rkt b/qi-lib/macro.rkt index e990569fd..17d1a019d 100644 --- a/qi-lib/macro.rkt +++ b/qi-lib/macro.rkt @@ -120,12 +120,11 @@ (syntax-parse spec [([tag arg-name] ...) (syntax-parser - [(_ e ...+) (if (= (length (attribute e)) - (length (attribute arg-name))) - #`(#%deforestable2 #,name #,info [tag e] ...) - (raise-syntax-error #f - "Wrong number of arguments!" - this-syntax))] + [(_ e ...+) + #:fail-unless (= (length (attribute e)) + (length (attribute arg-name))) + "Wrong number of arguments!" + #`(#%deforestable2 #,name #,info [tag e] ...)] ;; TODO, check: instead of `car`, does `(car)` produce ;; a useful syntax error? [_:id #`(#%deforestable2 #,name #,info)])]))) From c9db34cf59d59e168abec3239d5d0413742e3f20 Mon Sep 17 00:00:00 2001 From: Siddhartha Date: Sat, 23 Nov 2024 00:20:39 -0700 Subject: [PATCH 19/22] Fully replace `#%deforestable` with the new implementation This replaces the original implementation of the `#%deforestable` core form with the new one that had been named `#%deforestable2` during the process of incrementally integrating the proof-of-concept for this scheme. With that complete, this now gets rid of the hardcoded codegen for the `qi/list` forms in the Qi compiler, instead replacing it with the ability to specify this in the `define-deforestable` macro which then conveys the codegen through to the final stage of compilation via a compile-time datatype. This also removes some tests that are no longer relevant and updates others. The recent Qi meeting notes provide more context on this approach. --- qi-lib/flow/core/compiler/1000-qi0.rkt | 50 ++----------------- qi-lib/flow/core/compiler/deforest/syntax.rkt | 50 +++++++++---------- qi-lib/flow/core/syntax.rkt | 7 +-- qi-lib/flow/extended/expander.rkt | 5 +- qi-lib/macro.rkt | 6 +-- qi-test/tests/expander.rkt | 7 +-- qi-test/tests/flow.rkt | 10 ---- 7 files changed, 39 insertions(+), 96 deletions(-) diff --git a/qi-lib/flow/core/compiler/1000-qi0.rkt b/qi-lib/flow/core/compiler/1000-qi0.rkt index 4ddae7e16..12ca24065 100644 --- a/qi-lib/flow/core/compiler/1000-qi0.rkt +++ b/qi-lib/flow/core/compiler/1000-qi0.rkt @@ -94,7 +94,6 @@ [(~datum appleye) #'call] [e:clos-form (clos-parser #'e)] - [e:deforestable2-form (deforestable2-parser #'e)] [e:deforestable-form (deforestable-parser #'e)] ;; escape hatch for racket expressions or anything ;; to be "passed through" @@ -393,58 +392,19 @@ the DSL. (qi0->racket (~> (-< (~> (gen args) △) _) onex))))])) - (define (deforestable2-clause-parser c) + (define (deforestable-clause-parser c) (syntax-parse c [((~datum f) e) #'(qi0->racket e)] [((~datum e) e) #'e])) - (define (deforestable2-parser e) + (define (deforestable-parser e) (syntax-parse e - #:datum-literals (#%deforestable2) - [(#%deforestable2 _name info c ...) - (let ([es^ (map deforestable2-clause-parser (attribute c))]) + #:datum-literals (#%deforestable) + [(#%deforestable _name info c ...) + (let ([es^ (map deforestable-clause-parser (attribute c))]) (match-let ([(deforestable-info codegen) (syntax-local-value #'info)]) (apply codegen es^)))])) - (define (deforestable-parser stx) - (syntax-parse stx - [((~datum #%deforestable) (~datum filter) (proc:clause)) - #'(lambda (v) - (filter (qi0->racket proc) v))] - [((~datum #%deforestable) (~datum filter-map) (proc:clause)) - #'(lambda (v) - (filter-map (qi0->racket proc) v))] - [((~datum #%deforestable) (~datum map) (proc:clause)) - #'(lambda (v) - (map (qi0->racket proc) v))] - [((~datum #%deforestable) (~datum foldl) (proc:clause) (init:expr)) - #'(lambda (v) - (foldl (qi0->racket proc) init v))] - [((~datum #%deforestable) (~datum foldr) (proc:clause) (init:expr)) - #'(lambda (v) - (foldr (qi0->racket proc) init v))] - [((~datum #%deforestable) (~datum range) () (arg:expr ...)) - #'(lambda () - (range arg ...))] - [((~datum #%deforestable) (~datum take) () (n:expr)) - #'(lambda (v) - (take v n))] - [((~datum #%deforestable) (~datum car)) - #'car] - [((~datum #%deforestable) (~datum cadr)) - #'cadr] - [((~datum #%deforestable) (~datum caddr)) - #'caddr] - [((~datum #%deforestable) (~datum cadddr)) - #'cadddr] - [((~datum #%deforestable) (~datum list-ref) () (n:expr)) - #'(lambda (v) - (list-ref v n))] - [((~datum #%deforestable) (~datum length)) - #'length] - [((~datum #%deforestable) (~or* (~datum empty?) (~datum null?))) - #'empty?])) - (define (blanket-template-form-parser stx) (syntax-parse stx ;; "prarg" = "pre-supplied argument" diff --git a/qi-lib/flow/core/compiler/deforest/syntax.rkt b/qi-lib/flow/core/compiler/deforest/syntax.rkt index 2f01e629a..be2e090b0 100644 --- a/qi-lib/flow/core/compiler/deforest/syntax.rkt +++ b/qi-lib/flow/core/compiler/deforest/syntax.rkt @@ -49,8 +49,8 @@ (define-syntax-class fsp-range #:attributes (blanket? fine? arg pre-arg post-arg) #:literal-sets (fs-literals) - #:datum-literals (#%deforestable2 range) - (pattern (#%deforestable2 range _info ((~datum e) the-arg) ...) + #:datum-literals (#%deforestable range) + (pattern (#%deforestable range _info ((~datum e) the-arg) ...) #:attr arg #'(the-arg ...) #:attr pre-arg #f #:attr post-arg #f @@ -78,29 +78,29 @@ (define-syntax-class fst-filter #:attributes (f) #:literal-sets (fs-literals) - #:datum-literals (#%deforestable2 filter) - (pattern (#%deforestable2 filter _info ((~datum f) f-uncompiled)) + #:datum-literals (#%deforestable filter) + (pattern (#%deforestable filter _info ((~datum f) f-uncompiled)) #:attr f (run-passes #'f-uncompiled))) (define-syntax-class fst-map #:attributes (f) #:literal-sets (fs-literals) - #:datum-literals (#%deforestable2 map) - (pattern (#%deforestable2 map _info ((~datum f) f-uncompiled)) + #:datum-literals (#%deforestable map) + (pattern (#%deforestable map _info ((~datum f) f-uncompiled)) #:attr f (run-passes #'f-uncompiled))) (define-syntax-class fst-filter-map #:attributes (f) #:literal-sets (fs-literals) - #:datum-literals (#%deforestable2 filter-map) - (pattern (#%deforestable2 filter-map _info ((~datum f) f-uncompiled)) + #:datum-literals (#%deforestable filter-map) + (pattern (#%deforestable filter-map _info ((~datum f) f-uncompiled)) #:attr f (run-passes #'f-uncompiled))) (define-syntax-class fst-take #:attributes (n) #:literal-sets (fs-literals) - #:datum-literals (#%deforestable2 take) - (pattern (#%deforestable2 take _info ((~datum e) n)))) + #:datum-literals (#%deforestable take) + (pattern (#%deforestable take _info ((~datum e) n)))) (define-syntax-class fst-syntax0 (pattern (~or _:fst-filter @@ -123,8 +123,8 @@ (define-syntax-class fsc-foldr #:attributes (op init) #:literal-sets (fs-literals) - #:datum-literals (#%deforestable2 foldr) - (pattern (#%deforestable2 + #:datum-literals (#%deforestable foldr) + (pattern (#%deforestable foldr _info ((~datum f) op-uncompiled) @@ -134,8 +134,8 @@ (define-syntax-class fsc-foldl #:attributes (op init) #:literal-sets (fs-literals) - #:datum-literals (#%deforestable2 foldl) - (pattern (#%deforestable2 + #:datum-literals (#%deforestable foldl) + (pattern (#%deforestable foldl _info ((~datum f) op-uncompiled) @@ -144,18 +144,18 @@ (define-syntax-class cad*r-datum #:attributes (countdown) - #:datum-literals (#%deforestable2 car cadr caddr cadddr) - (pattern (#%deforestable2 car _info) #:attr countdown #'0) - (pattern (#%deforestable2 cadr _info) #:attr countdown #'1) - (pattern (#%deforestable2 caddr _info) #:attr countdown #'2) - (pattern (#%deforestable2 cadddr _info) #:attr countdown #'3)) + #:datum-literals (#%deforestable car cadr caddr cadddr) + (pattern (#%deforestable car _info) #:attr countdown #'0) + (pattern (#%deforestable cadr _info) #:attr countdown #'1) + (pattern (#%deforestable caddr _info) #:attr countdown #'2) + (pattern (#%deforestable cadddr _info) #:attr countdown #'3)) (define-syntax-class fsc-list-ref #:attributes (pos name) #:literal-sets (fs-literals) - #:datum-literals (#%deforestable2 list-ref) + #:datum-literals (#%deforestable list-ref) ;; TODO: need #%host-expression wrapping idx? - (pattern (#%deforestable2 list-ref _info ((~datum e) idx)) + (pattern (#%deforestable list-ref _info ((~datum e) idx)) #:attr pos #'idx #:attr name #'list-ref) ;; TODO: bring wrapping #%deforestable out here? @@ -165,13 +165,13 @@ (define-syntax-class fsc-length #:literal-sets (fs-literals) - #:datum-literals (#%deforestable2 length) - (pattern (#%deforestable2 length _info))) + #:datum-literals (#%deforestable length) + (pattern (#%deforestable length _info))) (define-syntax-class fsc-empty? #:literal-sets (fs-literals) - #:datum-literals (#%deforestable2 empty?) ; note: null? expands to empty? - (pattern (#%deforestable2 empty? _info))) + #:datum-literals (#%deforestable empty?) ; note: null? expands to empty? + (pattern (#%deforestable empty? _info))) (define-syntax-class fsc-default #:datum-literals (cstream->list) diff --git a/qi-lib/flow/core/syntax.rkt b/qi-lib/flow/core/syntax.rkt index 3ba787dc8..e70ff09e3 100644 --- a/qi-lib/flow/core/syntax.rkt +++ b/qi-lib/flow/core/syntax.rkt @@ -18,8 +18,7 @@ fold-right-form loop-form clos-form - deforestable-form - deforestable2-form) + deforestable-form) (require syntax/parse) @@ -140,7 +139,3 @@ See comments in flow.rkt for more details. (define-syntax-class deforestable-form (pattern ((~datum #%deforestable) arg ...))) - -(define-syntax-class deforestable2-form - (pattern - ((~datum #%deforestable2) arg ...))) diff --git a/qi-lib/flow/extended/expander.rkt b/qi-lib/flow/extended/expander.rkt index 04abd74a1..c40d1ae1c 100644 --- a/qi-lib/flow/extended/expander.rkt +++ b/qi-lib/flow/extended/expander.rkt @@ -184,10 +184,7 @@ core language's use of #%app, etc.). (esc ex:racket-expr) ;; core form to express deforestable operations - (#%deforestable name:id (f:closed-floe ...) (arg:racket-expr ...)) - (#%deforestable name:id (f:closed-floe ...+)) - (#%deforestable name:id) - (#%deforestable2 name:id info:id e:deforestable-clause ...) + (#%deforestable name:id info:id e:deforestable-clause ...) ;; backwards compat macro extensibility via Racket macros (~> ((~var ext-form (starts-with "qi:")) expr ...) diff --git a/qi-lib/macro.rkt b/qi-lib/macro.rkt index 17d1a019d..512c55996 100644 --- a/qi-lib/macro.rkt +++ b/qi-lib/macro.rkt @@ -14,7 +14,7 @@ (only-in "flow/extended/expander.rkt" qi-macro esc - #%deforestable2) + #%deforestable) qi/flow/space (for-syntax qi/flow/aux-syntax) syntax/parse/define @@ -124,10 +124,10 @@ #:fail-unless (= (length (attribute e)) (length (attribute arg-name))) "Wrong number of arguments!" - #`(#%deforestable2 #,name #,info [tag e] ...)] + #`(#%deforestable #,name #,info [tag e] ...)] ;; TODO, check: instead of `car`, does `(car)` produce ;; a useful syntax error? - [_:id #`(#%deforestable2 #,name #,info)])]))) + [_:id #`(#%deforestable #,name #,info)])]))) (define-syntax define-deforestable (syntax-parser diff --git a/qi-test/tests/expander.rkt b/qi-test/tests/expander.rkt index 76b99c3de..baaf30ffa 100644 --- a/qi-test/tests/expander.rkt +++ b/qi-test/tests/expander.rkt @@ -105,10 +105,11 @@ (#%host-expression 1) __)))) (test-expand "#%deforestable" - #'(#%deforestable name (_) (_)) + #'(#%deforestable name info (f 0) (e 0)) #'(#%deforestable name - (_) - ((#%host-expression _))))) + info + (f (gen (#%host-expression 0))) + (e (#%host-expression 0))))) (test-suite "utils" ;; this is just temporary until we properly track source expressions through diff --git a/qi-test/tests/flow.rkt b/qi-test/tests/flow.rkt index 67cc4f0e8..f7e765380 100644 --- a/qi-test/tests/flow.rkt +++ b/qi-test/tests/flow.rkt @@ -285,16 +285,6 @@ (check-equal? ((☯ (esc (first (list + *)))) 3 7) 10 "normal racket expressions")) - (test-suite - "#%deforestable" - (check-equal? ((☯ (#%deforestable filter (odd?))) (list 1 2 3)) (list 1 3)) - (check-equal? ((☯ (#%deforestable map (sqr))) (list 1 2 3)) (list 1 4 9)) - (check-equal? ((☯ (#%deforestable foldl (+) (0))) (list 1 2 3)) 6) - (check-equal? ((☯ (#%deforestable foldr (+) (0))) (list 1 2 3)) 6) - (check-equal? ((☯ (#%deforestable range () (3)))) (list 0 1 2)) - (check-equal? ((☯ (#%deforestable range () (0 3)))) (list 0 1 2)) - (check-equal? ((☯ (#%deforestable range () (0 5 2)))) (list 0 2 4)) - (check-equal? ((☯ (#%deforestable take () (2))) (list 1 2 3)) (list 1 2))) (test-suite "elementary boolean gates" (test-suite From 607bd65fdf5d485da819162e63f47279defb46e1 Mon Sep 17 00:00:00 2001 From: Siddhartha Date: Fri, 29 Nov 2024 12:53:17 -0700 Subject: [PATCH 20/22] Improve handling of identifier forms of deforestable syntax Ensure that the `spec` contains all the information that's needed to parse the syntax in terms of floe and expr positions, instead of having a parallel path for identifier forms. (done in today's meeting) --- qi-lib/macro.rkt | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/qi-lib/macro.rkt b/qi-lib/macro.rkt index 512c55996..d2d3e4618 100644 --- a/qi-lib/macro.rkt +++ b/qi-lib/macro.rkt @@ -118,20 +118,24 @@ ;; to a corresponding number of clauses in the core form, like: ;; (op e1 e2 e3) → (#%optimizable-app #,info [f e1] [e e2] [f e3]) (syntax-parse spec - [([tag arg-name] ...) + #:datum-literals (op) + [(op [tag arg-name] ...) (syntax-parser [(_ e ...+) #:fail-unless (= (length (attribute e)) (length (attribute arg-name))) "Wrong number of arguments!" - #`(#%deforestable #,name #,info [tag e] ...)] - ;; TODO, check: instead of `car`, does `(car)` produce - ;; a useful syntax error? - [_:id #`(#%deforestable #,name #,info)])]))) + #`(#%deforestable #,name #,info [tag e] ...)])] + ;; TODO, check: instead of `car`, does `(car)` produce + ;; a useful syntax error? + ;; TODO: can add complementary error clauses in these two + ;; patterns; test that default errors are good enough. But if not + ;; can add the error clauses. + [op (syntax-parser [_:id #`(#%deforestable #,name #,info)])]))) (define-syntax define-deforestable (syntax-parser - [(_ (name spec ...) codegen) + [(_ (name spec ...+) codegen) #:with ([typ arg] ...) #'(spec ...) #:with codegen-f #'(lambda (arg ...) ;; var bindings vs pattern bindings @@ -149,7 +153,7 @@ (deforestable-info codegen-f)) (define-dsl-syntax name qi-macro - (op-transformer #'name #'info #'(spec ...))))] + (op-transformer #'name #'info #'(op spec ...))))] [(_ name:id codegen) #:with codegen-f #'(lambda () codegen) #'(begin @@ -160,4 +164,4 @@ (deforestable-info codegen-f)) (define-dsl-syntax name qi-macro - (op-transformer #'name #'info #'())))])) + (op-transformer #'name #'info #'op)))])) From 85ef9e7aff861b498cd0a7f1128019ce8e4961a2 Mon Sep 17 00:00:00 2001 From: Siddhartha Date: Sat, 30 Nov 2024 03:58:26 -0700 Subject: [PATCH 21/22] declare `#%deforestable` as a literal in `fs-literals` (cr) --- qi-lib/flow/core/compiler/deforest/syntax.rkt | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/qi-lib/flow/core/compiler/deforest/syntax.rkt b/qi-lib/flow/core/compiler/deforest/syntax.rkt index be2e090b0..a3fe6d845 100644 --- a/qi-lib/flow/core/compiler/deforest/syntax.rkt +++ b/qi-lib/flow/core/compiler/deforest/syntax.rkt @@ -34,7 +34,7 @@ ;; Literals set used for matching Fusable Stream Literals (define-literal-set fs-literals - #:datum-literals (esc #%host-expression #%fine-template #%blanket-template _ __) + #:datum-literals (esc #%host-expression #%fine-template #%blanket-template #%deforestable _ __) ()) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -49,7 +49,7 @@ (define-syntax-class fsp-range #:attributes (blanket? fine? arg pre-arg post-arg) #:literal-sets (fs-literals) - #:datum-literals (#%deforestable range) + #:datum-literals (range) (pattern (#%deforestable range _info ((~datum e) the-arg) ...) #:attr arg #'(the-arg ...) #:attr pre-arg #f @@ -78,28 +78,28 @@ (define-syntax-class fst-filter #:attributes (f) #:literal-sets (fs-literals) - #:datum-literals (#%deforestable filter) + #:datum-literals (filter) (pattern (#%deforestable filter _info ((~datum f) f-uncompiled)) #:attr f (run-passes #'f-uncompiled))) (define-syntax-class fst-map #:attributes (f) #:literal-sets (fs-literals) - #:datum-literals (#%deforestable map) + #:datum-literals (map) (pattern (#%deforestable map _info ((~datum f) f-uncompiled)) #:attr f (run-passes #'f-uncompiled))) (define-syntax-class fst-filter-map #:attributes (f) #:literal-sets (fs-literals) - #:datum-literals (#%deforestable filter-map) + #:datum-literals (filter-map) (pattern (#%deforestable filter-map _info ((~datum f) f-uncompiled)) #:attr f (run-passes #'f-uncompiled))) (define-syntax-class fst-take #:attributes (n) #:literal-sets (fs-literals) - #:datum-literals (#%deforestable take) + #:datum-literals (take) (pattern (#%deforestable take _info ((~datum e) n)))) (define-syntax-class fst-syntax0 @@ -123,7 +123,7 @@ (define-syntax-class fsc-foldr #:attributes (op init) #:literal-sets (fs-literals) - #:datum-literals (#%deforestable foldr) + #:datum-literals (foldr) (pattern (#%deforestable foldr _info @@ -134,7 +134,7 @@ (define-syntax-class fsc-foldl #:attributes (op init) #:literal-sets (fs-literals) - #:datum-literals (#%deforestable foldl) + #:datum-literals (foldl) (pattern (#%deforestable foldl _info @@ -153,7 +153,7 @@ (define-syntax-class fsc-list-ref #:attributes (pos name) #:literal-sets (fs-literals) - #:datum-literals (#%deforestable list-ref) + #:datum-literals (list-ref) ;; TODO: need #%host-expression wrapping idx? (pattern (#%deforestable list-ref _info ((~datum e) idx)) #:attr pos #'idx @@ -165,12 +165,12 @@ (define-syntax-class fsc-length #:literal-sets (fs-literals) - #:datum-literals (#%deforestable length) + #:datum-literals (length) (pattern (#%deforestable length _info))) (define-syntax-class fsc-empty? #:literal-sets (fs-literals) - #:datum-literals (#%deforestable empty?) ; note: null? expands to empty? + #:datum-literals (empty?) ; note: null? expands to empty? (pattern (#%deforestable empty? _info))) (define-syntax-class fsc-default From 61f6561d95d1e4600737ccdb333bee492f54e165 Mon Sep 17 00:00:00 2001 From: Siddhartha Date: Sat, 30 Nov 2024 04:24:52 -0700 Subject: [PATCH 22/22] Improve error message when a form is used as an identifier E.g. `filter` is a syntax error but `car` isn't. This ensures that the former provides a helpful error message (syntax-parse's default for the latter is already fine). --- qi-lib/macro.rkt | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/qi-lib/macro.rkt b/qi-lib/macro.rkt index d2d3e4618..e9601cb93 100644 --- a/qi-lib/macro.rkt +++ b/qi-lib/macro.rkt @@ -125,13 +125,18 @@ #:fail-unless (= (length (attribute e)) (length (attribute arg-name))) "Wrong number of arguments!" - #`(#%deforestable #,name #,info [tag e] ...)])] - ;; TODO, check: instead of `car`, does `(car)` produce - ;; a useful syntax error? - ;; TODO: can add complementary error clauses in these two - ;; patterns; test that default errors are good enough. But if not - ;; can add the error clauses. - [op (syntax-parser [_:id #`(#%deforestable #,name #,info)])]))) + #`(#%deforestable #,name #,info [tag e] ...)] + [_:id + (raise-syntax-error #f + (format "Bad syntax. Usage: (~a arg ...)" + (syntax->datum this-syntax)) + this-syntax)])] + [op + (syntax-parser + ;; already raises a good error if used as a + ;; form with arguments (rather than as an identifier) + ;; so no special error handling needed here + [_:id #`(#%deforestable #,name #,info)])]))) (define-syntax define-deforestable (syntax-parser