├── .gitignore ├── README.md ├── demo.rkt ├── enforest ├── hier-name-parse.rkt ├── implicit.rkt ├── info.rkt ├── main.rkt ├── name-parse.rkt ├── name-root.rkt ├── operator.rkt ├── private │ ├── check.rkt │ ├── name-path-op.rkt │ └── transform.rkt ├── proc-name.rkt ├── property.rkt ├── scribblings │ ├── api.scrbl │ ├── common.rhm │ ├── enforest-algorithm.scrbl │ ├── enforest.scrbl │ ├── example.scrbl │ ├── hierarchical-naming.scrbl │ ├── implicit-operator.scrbl │ ├── macro-protocol.scrbl │ ├── motivation.scrbl │ ├── precedence.scrbl │ ├── racket.rkt │ ├── syntactic-categories.scrbl │ └── transformer.scrbl ├── sequence.rkt ├── syntax-local.rkt ├── transformer-result.rkt └── transformer.rkt ├── info.rkt ├── rhombus ├── info.rkt ├── macro.rkt ├── main.rkt ├── parse.rkt ├── private │ ├── annotation-syntax.rkt │ ├── annotation.rkt │ ├── arithmetic.rkt │ ├── array.rkt │ ├── assign.rkt │ ├── begin.rkt │ ├── binding-syntax.rkt │ ├── binding.rkt │ ├── bounce.rkt │ ├── call-result-key.rkt │ ├── class.rkt │ ├── composite.rkt │ ├── cond.rkt │ ├── consistent.rkt │ ├── declaration-syntax.rkt │ ├── declaration.rkt │ ├── define-operator.rkt │ ├── define.rkt │ ├── definition-syntax.rkt │ ├── definition.rkt │ ├── dot-provider-key.rkt │ ├── dot-syntax.rkt │ ├── dot.rkt │ ├── empty-group.rkt │ ├── equal.rkt │ ├── export.rkt │ ├── expression+binding.rkt │ ├── expression+definition.rkt │ ├── expression-syntax.rkt │ ├── expression.rkt │ ├── for-meta.rkt │ ├── forwarding-sequence.rkt │ ├── function.rkt │ ├── implicit.rkt │ ├── import-syntax.rkt │ ├── import.rkt │ ├── infer-name.rkt │ ├── introducer.rkt │ ├── keyword.rkt │ ├── list.rkt │ ├── literal.rkt │ ├── lower-require.rkt │ ├── map-ref-set-key.rkt │ ├── map-ref.rkt │ ├── map.rkt │ ├── match.rkt │ ├── misuse.rkt │ ├── module-path.rkt │ ├── name-path-op.rkt │ ├── name-root-ref.rkt │ ├── name-root.rkt │ ├── nested-bindings.rkt │ ├── operator-parse.rkt │ ├── operator.rkt │ ├── parens.rkt │ ├── parse.rkt │ ├── parsed.rkt │ ├── print.rkt │ ├── quasiquote.rkt │ ├── ref-result-key.rkt │ ├── set.rkt │ ├── setmap.rkt │ ├── srcloc.rkt │ ├── static-info-pack.rkt │ ├── static-info-syntax.rkt │ ├── static-info.rkt │ ├── string.rkt │ ├── symbol.rkt │ ├── syntax-class-mixin.rkt │ ├── syntax-class.rkt │ ├── syntax-error.rkt │ ├── syntax-list.rkt │ ├── syntax-meta-value.rkt │ ├── syntax-object.rkt │ ├── syntax.rkt │ ├── tail.rkt │ ├── underscore.rkt │ ├── value.rkt │ ├── values.rkt │ ├── with-syntax.rkt │ └── wrap-expression.rkt ├── runtime-config.rkt └── scribblings │ ├── annotation-macro.scrbl │ ├── annotation-vs-bind.scrbl │ ├── annotation.scrbl │ ├── bind-and-static.scrbl │ ├── bind-macro-protocol.scrbl │ ├── bind-macro.scrbl │ ├── common.rhm │ ├── conditional.scrbl │ ├── definition.scrbl │ ├── defn-macro.scrbl │ ├── expr-macro.scrbl │ ├── function.scrbl │ ├── keyword-argument.scrbl │ ├── list.scrbl │ ├── macro.rhm │ ├── map.scrbl │ ├── module.scrbl │ ├── multiple-value.scrbl │ ├── mutable-var.scrbl │ ├── notation.scrbl │ ├── operator.scrbl │ ├── overview.scrbl │ ├── ref-annotation.scrbl │ ├── ref-array.scrbl │ ├── ref-begin.scrbl │ ├── ref-boolean.scrbl │ ├── ref-class.scrbl │ ├── ref-cond.scrbl │ ├── ref-def.scrbl │ ├── ref-defn-macro.scrbl │ ├── ref-dot.scrbl │ ├── ref-equal.scrbl │ ├── ref-export.scrbl │ ├── ref-expr-macro.scrbl │ ├── ref-function.scrbl │ ├── ref-import.scrbl │ ├── ref-io.scrbl │ ├── ref-list.scrbl │ ├── ref-macro.scrbl │ ├── ref-map.scrbl │ ├── ref-match.scrbl │ ├── ref-mutable.scrbl │ ├── ref-number.scrbl │ ├── ref-set.scrbl │ ├── ref-string.scrbl │ ├── ref-stxobj.scrbl │ ├── ref-values.scrbl │ ├── ref-void.scrbl │ ├── reference.scrbl │ ├── rhombus.scrbl │ ├── set.scrbl │ ├── static-info.scrbl │ ├── syntax.scrbl │ └── util.rhm ├── scribble ├── private │ ├── doc.rhm │ ├── docmodule.rhm │ ├── example.rhm │ ├── include.rkt │ ├── property.rkt │ ├── rhombus-spacer.rkt │ ├── rhombus.rhm │ ├── typeset-doc.rkt │ ├── typeset-example.rkt │ ├── typeset-help.rkt │ ├── typeset-rhombus.rkt │ └── typeset_meta.rhm ├── rhombus.rkt └── rhombus │ └── manual.rkt └── shrubbery ├── armor.rkt ├── indentation.rkt ├── info.rkt ├── interaction.rkt ├── keystroke.rkt ├── lex-comment.rkt ├── lex.rkt ├── main.rkt ├── navigation.rkt ├── parse.rkt ├── print.rkt ├── private ├── at-space.rkt ├── delta-text.rkt ├── edit-help.rkt ├── paren.rkt ├── peek-port.rkt └── property.rkt ├── property.rkt ├── scribblings ├── at-notation.scrbl ├── example.scrbl ├── grammar-s-exp.rkt ├── grammar.rhm ├── group-and-block.scrbl ├── lexeme-parsing.scrbl ├── meta.scrbl ├── parsed-representation.scrbl ├── prior-art.scrbl ├── rationale.scrbl └── shrubbery.scrbl ├── srcloc.rkt ├── syntax-color.rkt ├── tests ├── armor.rkt ├── color.rkt ├── delta-text.rkt ├── incremental.rkt ├── indent.rkt ├── input.rkt ├── like-text.rkt └── parse.rkt └── write.rkt /.gitignore: -------------------------------------------------------------------------------- 1 | # these directories are generated by the build 2 | compiled/ 3 | doc/ 4 | *~ 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This was a Rhombus experiment with Shrubbery notation and DrRacket 2 | editor support. 3 | 4 | Continued at [https://github.com/racket/rhombus-prototype](https://github.com/racket/rhombus-prototype) 5 | -------------------------------------------------------------------------------- /enforest/hier-name-parse.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require syntax/parse 3 | "name-parse.rkt" 4 | "syntax-local.rkt" 5 | (submod "name-root.rkt" for-parse)) 6 | 7 | (provide :hier-name-seq) 8 | 9 | (define-syntax-class (:hier-name-seq in-space name-path-op name-root-ref) 10 | #:datum-literals (op) 11 | (pattern (~and stxes (root::name (op sep) . _)) 12 | #:when (eq? name-path-op (syntax-e #'sep)) 13 | #:do [(define head-id (in-space #'root.name)) 14 | (define lxc (syntax-local-value* head-id name-root-ref))] 15 | #:when lxc 16 | #:do [(define-values (head tail) (apply-name-root head-id lxc #'stxes))] 17 | #:with (~var hname (:hier-name-seq in-space name-path-op name-root-ref)) (cons head tail) 18 | #:attr name #'hname.name 19 | #:attr tail #'hname.tail) 20 | (pattern (base::name . tail) 21 | #:attr name #'base.name)) 22 | -------------------------------------------------------------------------------- /enforest/implicit.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require syntax/parse) 3 | 4 | ;; Default implicit handling for `define-enforest` 5 | 6 | (provide select-prefix-implicit 7 | select-infix-implicit 8 | juxtapose-implicit-name) 9 | 10 | ;; implicit prefix operator names: 11 | (define tuple-implicit-name '#%tuple) ; parentheses not after an expression 12 | (define array-implicit-name '#%array) ; square brackets not after an expression 13 | (define set-implicit-name '#%set) ; curly braces not after an expression 14 | (define block-implicit-name '#%block) ; colon 15 | (define alts-implicit-name '#%alts) ; vertical bars 16 | (define literal-implicit-name '#%literal) ; numbers, strings, etc. 17 | 18 | ;; implicit infix operator names: 19 | (define call-implicit-name '#%call) ; parentheses adjacent to preceding expression 20 | (define ref-implicit-name '#%ref) ; square brackets adjacent to preceding expression 21 | (define comp-implicit-name '#%comp) ; curly braces adjacent to preceding expression 22 | (define juxtapose-implicit-name '#%juxtapose) ; other exprs with no operator between 23 | 24 | ;; A selector function takes a term that determines the implicit, and 25 | ;; it returns a symbol for the implicit name plus a syntax object to 26 | ;; provide lexical context. 27 | 28 | (define (select-prefix-implicit head) 29 | (syntax-parse head 30 | [((~and tag (~datum parens)) . _) 31 | (values tuple-implicit-name #'tag)] 32 | [((~and tag (~datum brackets)) . _) 33 | (values array-implicit-name #'tag)] 34 | [((~and tag (~datum braces)) . _) 35 | (values set-implicit-name #'tag)] 36 | [((~and tag (~datum block)) . _) 37 | (values block-implicit-name #'tag)] 38 | [((~and tag (~datum alts)) . _) 39 | (values alts-implicit-name #'tag)] 40 | [_ 41 | (values literal-implicit-name head)])) 42 | 43 | (define (select-infix-implicit head) 44 | (syntax-parse head 45 | [((~and tag (~datum parens)) . _) 46 | (values call-implicit-name #'tag)] 47 | [((~and tag (~datum brackets)) . _) 48 | (values ref-implicit-name #'tag)] 49 | [((~and tag (~datum braces)) . _) 50 | (values comp-implicit-name #'tag)] 51 | [((~and tag (~datum block)) . _) 52 | (values juxtapose-implicit-name #'tag)] 53 | [((~and tag (~datum alts)) . _) 54 | (values juxtapose-implicit-name #'tag)] 55 | [_ 56 | (values juxtapose-implicit-name head)])) 57 | -------------------------------------------------------------------------------- /enforest/info.rkt: -------------------------------------------------------------------------------- 1 | #lang info 2 | 3 | (define scribblings '(("scribblings/enforest.scrbl" (multi-page)))) 4 | -------------------------------------------------------------------------------- /enforest/name-parse.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require syntax/parse) 3 | 4 | (provide :name) 5 | 6 | (define-syntax-class :name 7 | (pattern ((~datum op) name)) 8 | (pattern name:identifier)) 9 | -------------------------------------------------------------------------------- /enforest/name-root.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require racket/base 3 | syntax/stx 4 | "property.rkt" 5 | "syntax-local.rkt" 6 | "proc-name.rkt" 7 | "private/transform.rkt") 8 | 9 | (provide (property-out name-root) 10 | name-root-proc) 11 | 12 | (module+ for-parse 13 | (provide apply-name-root)) 14 | 15 | (property name-root (proc)) 16 | 17 | (define (apply-name-root op-stx lxc stxes) 18 | (define proc (name-root-proc lxc)) 19 | (call-as-transformer 20 | op-stx 21 | (lambda (in out) 22 | (define-values (target tail) (proc (in stxes))) 23 | (unless (or (identifier? target) 24 | (and (syntax? target) 25 | (pair? (syntax-e target)) 26 | (eq? 'op (syntax-e (car (syntax-e target)))))) 27 | (raise-result-error (proc-name proc) "identifier-or-operator?" target)) 28 | (check-transformer-result (out target) (out tail) proc)))) 29 | -------------------------------------------------------------------------------- /enforest/private/check.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require "../proc-name.rkt") 3 | 4 | (provide check-is-syntax) 5 | 6 | (define (check-is-syntax val proc) 7 | (unless (syntax? val) 8 | (raise-result-error (proc-name proc) "syntax?" val)) 9 | val) 10 | -------------------------------------------------------------------------------- /enforest/private/name-path-op.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide name-path-op) 4 | 5 | (define name-path-op '|.|) 6 | -------------------------------------------------------------------------------- /enforest/private/transform.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require syntax/stx 3 | "../proc-name.rkt") 4 | 5 | (provide transform-in 6 | transform-out 7 | call-as-transformer 8 | check-transformer-result) 9 | 10 | (define no-props (datum->syntax #f #f)) 11 | 12 | (define current-transformer-introduce (make-parameter (lambda (stx) stx))) 13 | (define (transform-in stx) 14 | ((current-transformer-introduce) stx)) 15 | (define (transform-out stx) 16 | ((current-transformer-introduce) stx)) 17 | 18 | (define (call-as-transformer id thunk) 19 | (define intro (make-syntax-introducer)) 20 | (parameterize ([current-transformer-introduce intro]) 21 | (thunk intro 22 | (lambda (stx) 23 | (let loop ([stx stx]) 24 | (cond 25 | [(syntax? stx) 26 | (syntax-track-origin (intro stx) 27 | no-props 28 | id)] 29 | [(pair? stx) (cons (loop (car stx)) 30 | (loop (cdr stx)))] 31 | [else stx])))))) 32 | 33 | (define (check-transformer-result form tail proc) 34 | (unless (syntax? form) (raise-result-error (proc-name proc) "syntax?" form)) 35 | ;; we'd like to check for a syntax list in `tail`, but that's not constant-time 36 | (unless (or (pair? tail) 37 | (null? tail) 38 | (and (syntax? tail) 39 | (let ([e (syntax-e tail)]) 40 | (or (pair? e) (null? e))))) 41 | (raise-result-error (proc-name proc) "stx-list?" tail)) 42 | (values form tail)) 43 | -------------------------------------------------------------------------------- /enforest/proc-name.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide proc-name) 4 | 5 | (define (proc-name proc) 6 | (define s (object-name proc)) 7 | (if (symbol? s) 8 | s 9 | 'transformer-procedure)) 10 | -------------------------------------------------------------------------------- /enforest/property.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse) 4 | racket/provide-syntax) 5 | 6 | ;; The `property` form is useful for defining a new structure-type 7 | ;; property for operators or transformers that apply to a specific 8 | ;; context. Besides a property `prop:name`, it defines a convenience 9 | ;; function `name` that creates an instance of a strcuture type that 10 | ;; implements the property and is a subtype of an indicated 11 | ;; `base-name`. 12 | 13 | (provide property 14 | property-out) 15 | 16 | ;; Defines: 17 | ;; `prop:name` - a property whose value should be a procedure 18 | ;; that takes an instance of the property 19 | ;; `name?` - detects instances of `prop:name` 20 | ;; `name-ref` - uses the property value on the instance, returns 21 | ;; #f if `name?` would return #f 22 | ;; `name` - convenience constructor for an instance of `prop:name` 23 | (define-syntax (property stx) 24 | (syntax-parse stx 25 | [(_ name 26 | (~optional base-name:identifier) 27 | (~optional (field:identifier ...) 28 | #:defaults ([(field 1) '()])) 29 | (~optional (~seq #:super prop-super)) 30 | (~seq #:property prop-expr prop-val-expr) 31 | ...) 32 | (with-syntax ([prop:name (format-id "prop:~a" #'name)] 33 | [name-ref (format-id "~a-ref" #'name)] 34 | [name? (format-id "~a?" #'name)] 35 | [convenience-name (car (generate-temporaries (list #'name)))] 36 | [super-spec (if (attribute prop-super) 37 | #'(list (cons prop-super values)) 38 | #'null)]) 39 | (with-syntax ([(base-name ...) (if (attribute base-name) 40 | (list #'base-name) 41 | (list))] 42 | [(define-accessor ...) (for/list ([field (in-list (syntax->list #'(field ...)))]) 43 | (define (acc name) 44 | (datum->syntax name 45 | (string->symbol (format "~a-~a" 46 | (syntax-e name) 47 | (syntax-e field))))) 48 | #`(define #,(acc #'name) #,(acc #'convenience-name)))]) 49 | #'(begin 50 | (define-values (prop:name name? ref) 51 | (make-struct-type-property 'name #f super-spec)) 52 | (define (name-ref v) 53 | (define acc (ref v (lambda () #f))) 54 | (and acc (acc v))) 55 | (struct convenience-name base-name ... (field ...) 56 | #:property prop:name (lambda (self) self) 57 | #:reflection-name 'name 58 | (~@ #:property prop-expr prop-val-expr) ...) 59 | (define-syntax name (make-rename-transformer #'convenience-name)) 60 | define-accessor ...)))])) 61 | 62 | (define-provide-syntax (property-out stx) 63 | (syntax-parse stx 64 | [(_ name:identifier) 65 | (with-syntax ([prop:name (format-id "prop:~a" #'name)] 66 | [name-ref (format-id "~a-ref" #'name)] 67 | [name? (format-id "~a?" #'name)]) 68 | #'(combine-out prop:name 69 | name 70 | name? 71 | name-ref))])) 72 | 73 | (define-for-syntax (format-id str ctx) 74 | (datum->syntax ctx 75 | (string->symbol (format str (syntax-e ctx))) 76 | ctx)) 77 | -------------------------------------------------------------------------------- /enforest/scribblings/common.rhm: -------------------------------------------------------------------------------- 1 | #lang rhombus 2 | 3 | import: 4 | scribble/rhombus/manual open 5 | "racket.rkt" open 6 | 7 | export: 8 | all_from("racket.rkt") 9 | shrubbery_doc 10 | rhombus_doc 11 | Rhombus 12 | 13 | def shrubbery_doc: [symbol(lib), "shrubbery/scribblings/shrubbery.scrbl"] 14 | def rhombus_doc: [symbol(lib), "rhombus/scribblings/rhombus.scrbl"] 15 | 16 | def Rhombus: @seclink[~doc: rhombus_doc, "top"]{Rhombus} 17 | -------------------------------------------------------------------------------- /enforest/scribblings/enforest-algorithm.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/manual 2 | 3 | @title{Enforestation Algorithm} 4 | 5 | The Rhombus parsing algorithm is similar to figure 1 of 6 | @hyperlink["http://www.cs.utah.edu/plt/publications/gpce12-rf.pdf"]{the 7 | Honu paper}, but with some key differences: 8 | 9 | @itemlist[ 10 | 11 | @item{When the inital term is an identifier followed by a name-path 12 | operator and @tt{lookup} produces a name root for the identifier, the 13 | name-root transformer is applied. Its result, a new term and tail, are 14 | used for the new state, without changing the current operator (if any).} 15 | 16 | @item{A prefix or infix operator has an associated transformer 17 | procedure to produce a Racket form, instead of always making a @tt{bin} 18 | or @tt{un} AST node. Whether the Racket form is an expression or some 19 | other form (such as pieces of a binding) depends on the context.} 20 | 21 | @item{Operators using the _automatic_ protocol are dispatched as in the 22 | figure. If a prefix operator's protocol is @emph{macro}, it behaves 23 | the same as the figure's case of an identifier that is mapped to a 24 | transformer. A macro infix operator's treatment is analogous.} 25 | 26 | @item{Function calls, array references, and list construction are not 27 | built-in in the same way as Honu. Instead, those positions 28 | correspond to the use of implicit operators, such as @racket[#%call].} 29 | 30 | @item{The figure's prefix-operator case seems wrong; the old operator and 31 | combiner should be pushed onto the stack.} 32 | 33 | @item{Already-parsed forms that are encoded with @racket['parsed] 34 | (which are ``term''s in the figure's terminology) are immediately 35 | converted to parsed form (i.e., ``tree term''s in the figure) by 36 | removing the @racket['parsed] wrapper.} 37 | 38 | ] 39 | 40 | The implementation represents pending operators during enforestation 41 | through the Racket continuation, instead of using an explicit stack; 42 | that is, instead of pushing on the stack, a recursive call is made to 43 | the enforestation function. A transformer can call back into the 44 | enforestation function, and in that case, it provides a current operator 45 | for precedence purposes, and the enforestation function will stop when 46 | it encounters either the end of the input or an operator at lower 47 | precedence. When enforestation stops at an operator with lower 48 | precedence, the enforestation function returns both the parsed form and 49 | the remaining terms. 50 | 51 | An identifier/operator is connected to a transformer using Racket's 52 | mapping machinery (@racket[define-syntax], etc.). The enforestation 53 | algorithm is parameterized over the space (in the sense of 54 | @racket[for-space]) it should consult and accessor–predicate functions 55 | that extract infix and prefix transformers from compile-time bindings. 56 | 57 | When a context includes only prefix macro operators that are 58 | constrained to consume all available terms, then enforestation is not 59 | really relevant. In that case, the context just needs a way to find 60 | and invoke operator transformers. The Rhombus expander provides a 61 | shortcut that skips the full enforestation algorithm for that simpler 62 | kind of context. 63 | -------------------------------------------------------------------------------- /enforest/scribblings/enforest.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm" open) 3 | 4 | @title{Enforestation with Macro Expansion} 5 | 6 | @seclink[~doc: shrubbery_doc, "top"]{Shrubbery notation} specifies 7 | how to parse a sequence of characters into a coarse-grained block 8 | structure, but it leaves the interpretation of that block structure to 9 | another layer of parsing---not to mention more fine-grained grouping in 10 | between lexing and block structure. The @filepath{enforest} collection 11 | is an adaptation of the Racket and Honu parsing techniques of 12 | @emph{expansion} and @emph{enforestation} targeted to shrubbery 13 | notation. 14 | 15 | For brevity, we call this parsing layer @deftech{Rhombus expansion}, 16 | even though it does not define a full candidate Rhombus language, and 17 | although it's in many ways independent of a specific language. That's 18 | similar to referring to @emph{Racket expansion}, by which we do not 19 | necessarily mean something involving @litchar{#lang racket}. 20 | 21 | @table_of_contents[] 22 | 23 | @include_section["motivation.scrbl"] 24 | @include_section["syntactic-categories.scrbl"] 25 | @include_section["hierarchical-naming.scrbl"] 26 | @include_section["transformer.scrbl"] 27 | @include_section["precedence.scrbl"] 28 | @include_section["implicit-operator.scrbl"] 29 | @include_section["enforest-algorithm.scrbl"] 30 | @include_section["api.scrbl"] 31 | @include_section["example.scrbl"] 32 | -------------------------------------------------------------------------------- /enforest/scribblings/hierarchical-naming.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm" open) 3 | 4 | @title{Hierarchical Naming} 5 | 6 | A language implemented with the Rhombus expander may have another 7 | dimension of name resolution that is orthogonal to different mapping 8 | spaces. For example, a language include a hierarchical naming strategy 9 | to reach a binding through sequence of identifiers separated by 10 | @rhombus[.], and hierarchical references might be used to reach mappings 11 | for expressions, bindings, or more. In the initial example in this 12 | proposal @rhombus[weather.currently_raining] is that kind of access, as 13 | is @rhombus[expr.macro] and @rhombus[bind.macro]. 14 | 15 | The example language overloads @rhombus[.] for hierarchical namespace 16 | use as well as field access, but the Rhombus expander minimizes any 17 | assumptions about the form of hierarchical names. A hierarchical 18 | reference must start with an identifier or operator that is mapped in 19 | the default space to a @deftech{name root}, and that identifier must be 20 | followed with the use of a designated name-path operator. A name root is 21 | implemented by a transformer that is similar to a prefix macro 22 | transformer (as explained in the next section), but it ``expands'' to a 23 | new identifier whose binding is checked in a space that's suitable to 24 | the context—or it expands to a reference to another name root, in which 25 | case the new root is expanded recursively. 26 | -------------------------------------------------------------------------------- /enforest/scribblings/implicit-operator.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/manual 2 | 3 | @title[#:tag "implicit-ops"]{Implicit Operators} 4 | 5 | In much the same way that @racket[#%app] and @racket[#%datum] are 6 | implicitly used in many Racket expressions, Rhombus enforestation needs 7 | at least two implicit forms: an implicit prefix operator for a 8 | non-identifier form by itself (somewhat like @racket[#%datum]), and an 9 | implicit infix operator for the juxtaposition of a parsed form and 10 | another form without a binary operator in between (somewhat like 11 | @racket[#%app]). To help enforestation applications avoid a level of 12 | indirection between those minimal implicit forms, however, enforestation 13 | is parameterized over functions that select implicit prefix and infix 14 | forms. The default selection function generates references to the 15 | following forms: 16 | 17 | @itemlist[ 18 | 19 | @item{@racket[#%tuple]: implicit prefix for a parenthesized term that is not 20 | immediately after a parsed form} 21 | 22 | @item{@racket[#%call]: implicit infix for a parsed form followed by a 23 | parenthesized term} 24 | 25 | @item{@racket[#%array]: implicit prefix for a square-bracketed term that is not 26 | immediately after a parsed form} 27 | 28 | @item{@racket[#%ref]: implicit infix for a parsed form followed by a 29 | square-bracketed term} 30 | 31 | @item{@racket[#%set]: implicit prefix for a curly-braced term that is not 32 | immediately after a parsed form} 33 | 34 | @item{@racket[#%comp]: implicit infix for a parsed form followed by a 35 | curly-braced term} 36 | 37 | @item{@racket[#%juxtapose]: implicit infix for adjacent expressions with no 38 | operator between them when @racket[#%call], @racket[#%ref], and @racket[#%comp] do not 39 | apply} 40 | 41 | @item{@racket[#%block]: implicit prefix for a block (written with @racket[:]) not 42 | immediately after a parsed form} 43 | 44 | @item{@racket[#%alts]: implicit prefix for a block of alternatives 45 | (written with @litchar{|} notation) not immediately after a parsed form} 46 | 47 | @item{@racket[#%literal]: implicit prefix for a literal, such as a number or 48 | boolean, not immediately after a parsed form} 49 | 50 | ] 51 | 52 | In an expression context, a Rhombus language's @racket[#%call] implementation 53 | most likely creates a function call, @racket[#%tuple] most likely does 54 | nothing for a single expression in parentheses (so parentheses can be 55 | used for grouping) and might otherwise create a tuple value or return 56 | multiple values, @racket[#%juxtapose] probably reports an error. Implicit 57 | operators are likely to have the highest possible precedence and be 58 | left-associative, but they are not constrained to those conventions by 59 | the Rhombus expander. Implicit operators are likely to be implemented 60 | using the macro operator protocol instead of the automatic operator 61 | protocol. 62 | -------------------------------------------------------------------------------- /enforest/scribblings/macro-protocol.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm": no_prefix) 3 | 4 | @title{Macro protocol} 5 | 6 | When an operator's mapping indicates a macro protocol, then the 7 | operator's transformer procedure is called with a parsed left-hand 8 | argument (in the case of an infix operator) and all of the remaining 9 | terms in the operator's group starting with the operator. The 10 | transformer procedure must return two values: the parsed form, which 11 | normally incorporates the left-hand argument plus some consumed 12 | additional terms, and the remaining terms that were not consumed. The 13 | Rhombus expander does not check that the second result is a tail of the 14 | original remaining terms, so a transformer could replace or rearrange 15 | them, but that's probably not a good idea. The invocation of a 16 | transformer for an implicit operator includes a synthesized term for the 17 | implicit operator. 18 | 19 | The enforestation process recognizes a superset of S-expressions 20 | compared to those used to represent shrubberies. Specifically, it 21 | recognizes a two-element list term that starts with the symbol 22 | @racket_q_parsed. The S-expression after @racket_q_parsed is treated 23 | as an already-parsed term and need not conform to the shrubbery 24 | grammar's representation. Since the list starting @racket_q_parsed 25 | itself has no shrubbery representation, it's conveniently opaque to a 26 | shrubbery layer of patterning matching. For example, in the earlier 27 | example implementing the @rhombus[->] macro infix operator, 28 | 29 | @(rhombusblock: 30 | expr.macro '($x -> $y $tail ...): 31 | values('($x . $y), tail) 32 | ) 33 | 34 | the macro transformer receives an syntax-object representing the 35 | already-parsed left-hand argument @rhombus[x] as a @racket_q_parsed 36 | list. The macro therefore has no way to pull the expression apart, 37 | inspect it, or rearrange it. Of course, such facilities could be made 38 | available to the macro transformer in lower-level form. Meanwhile, 39 | @rhombus[y] and @rhombus[tail] are likely unparsed terms, that can be 40 | inspected—although it's possible that some other macro constructs a 41 | @rhombus[->] expression using already-parsed terms, in which case they 42 | are similarly opaque to the @rhombus[->] transformer. 43 | 44 | For binding-operator macros, @Rhombus includes @rhombus[bind.unpack] to 45 | expose certain pieces of a binding's implementation, which allows the 46 | macro to compose or adjust other binding expansions. New binding pieces 47 | can be put back together into a parsed form using @rhombus[bind.pack]. 48 | 49 | Some contexts may oblige a macro transformer to consume all of the 50 | remaining terms in a group. For example, a definition or declaration 51 | context based on prefix identifiers like @rhombus[import], 52 | @rhombus[val], @rhombus[fun], and @rhombus[struct] might report an error 53 | if a transformer does not consume all available terms (and that's the 54 | case in @Rhombus). 55 | -------------------------------------------------------------------------------- /enforest/scribblings/precedence.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm" open) 3 | 4 | @title{Operator Precedence and Associativity} 5 | 6 | Instead of attaching a numeric precedence to each operator, as in Honu, 7 | each operator declares its precedence relative to other operators. That 8 | is, an operator can declare that its precedence is stronger than certain 9 | other operators, weaker than certain other operators, the same as 10 | certain other operators (implicitly including the operator itself), and 11 | the same as certain other operators when in a specific order (e.g., 12 | @rhombus[*] is not allowed to the right of @rhombus[/]). An infix 13 | operator's associativity is relevant only for operators at the same 14 | precedence (including the operator itself), as either left-associative, 15 | right-associative, or non-associative. 16 | 17 | Our example @rhombus[<>] definition did not specify any precedence 18 | relationships, so it cannot be used next to @rhombus[*]: 19 | 20 | @(rhombusblock: 21 | 1 <> 2 * 3 // not allowed 22 | ) 23 | 24 | In this example, enforestation would report that @rhombus[<>] and 25 | @rhombus[*] are unrelated, so parentheses are needed somewhere. @Rhombus 26 | supports precedence declarations through a @rhombus[~weaker_than] keyword: 27 | 28 | @(rhombusblock: 29 | operator (x <> y): 30 | ~weaker_than': * / + - 31 | Posn(x, y) 32 | 33 | 1 <> 2 * 3 // same as Posn(1, 6) 34 | ) 35 | 36 | Unlike a system based on numeric precedence levels, precedence in the 37 | Rhombus expander is not a complete or partial order. It's not even 38 | transitive. Operators @math{A} and @math{B} have a precedence 39 | relationship only if either @math{A} indicates a relationship to 40 | @math{B} or @math{B} indicates a relationship to @math{A}. This approach 41 | turns out to work fine with a Pratt-style parser (as used in Honu), 42 | which only ever needs to work with precedence when it has two specific 43 | operators to compare. A potential advantage of non-transitive precedence 44 | is avoiding an order among operands that make no sense next to each 45 | other. An operator can declare a default precedence relationship to 46 | other operators, but it must declare the default explicitly. 47 | 48 | If two operators both claim a precedence relationship to each other, the 49 | relationship must be consistent; for example, @math{A} cannot claim to 50 | have stronger precedence than @math{B} if @math{B} claims stronger 51 | precedence than @math{A}. Also, if one or both operators claim a same 52 | precedence strength to the other, the operators must have the same 53 | associativity. The consistency of precedence claims between @math{A} and 54 | @math{B} is checked only at the point where @math{A} and @math{B} are 55 | compared by the enforesting expander, and inconsistent claims trigger a 56 | syntax error at that point. 57 | 58 | Procedurally, precedence is relevant when enforestation encounters an 59 | infix operator on the right-hand side of some other operator. Precedence 60 | determines whether the terms in between the two operators form an 61 | argument to the earlier operator (on the way to producing an argument 62 | for the later operator) or whether those terms form an argument to the 63 | newly encountered operator (on the way to producing an argument for the 64 | earlier operator). Note that when a prefix operator is involved in a 65 | precedence comparison, the other operator is always a later infix 66 | operator. 67 | -------------------------------------------------------------------------------- /enforest/scribblings/racket.rkt: -------------------------------------------------------------------------------- 1 | #lang at-exp racket/base 2 | (require scribble/manual 3 | (for-label racket/base 4 | racket/match)) 5 | 6 | (provide racket_define_syntax 7 | racket_syntax_local_context 8 | racket_provide_for_space 9 | racket_match 10 | racket_q_parsed) 11 | 12 | (define racket_define_syntax @racket[define-syntax]) 13 | (define racket_syntax_local_context @racket[syntax-local-context]) 14 | (define racket_match @racket[match]) 15 | (define racket_provide_for_space @racket[(provide (for-space ....))]) 16 | (define racket_q_parsed @racket['parsed]) -------------------------------------------------------------------------------- /enforest/scribblings/syntactic-categories.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm" open) 3 | 4 | @title{Syntactic Categories} 5 | 6 | Rhombus expansion involves various syntactic categories that determine 7 | different kinds of expansion contexts. The specific set of contexts 8 | depends on the language, and not the Rhombus expander, but here are some 9 | possible contexts: 10 | 11 | @itemlist[ 12 | @item{declarations (in a module's immediate body or at the top level)}, 13 | @item{definitions}, 14 | @item{expressions}, 15 | @item{bindings (like @rhombus[match] patterns, but everywhere)} 16 | ] 17 | 18 | In Racket's expander, a few core contexts are reflected by 19 | @racket_syntax_local_context, but the Racket expander has only one kind 20 | of transformer that is represented by one kind of compile-time value: a 21 | procedure of arity 1. Nevertheless, some macros work only in, say, 22 | definition positions or in a module body. Rhombus expansion instead 23 | expects different kinds of compile-time values for different expansion 24 | contexts, so a mapping can declare where it's meant to be used. 25 | 26 | The Rhombus expander is parameterized over the way that different 27 | kinds of compile-time values for different contexts are recognized, 28 | but they are expected to be implemented through structure-type 29 | properties. A compile-time value can then implement multiple kinds of 30 | transformers to create a mapping that is works in multiple contexts. 31 | For example, the example @rhombus[<>] operator is useful in both expression 32 | and binding contexts, with a suitable meaning in each context. 33 | 34 | Different contexts may also consult different mapping spaces in the 35 | sense of @racket_provide_for_space. Contexts like declarations, 36 | definitions, and expressions are likely to use the default space, while 37 | binding, require, and provide contexts might use their own spaces. For 38 | example, in the prototype language supplied with this proposal, 39 | @rhombus[operator] and @rhombus[bind.macro] can both bind @rhombus[<>] 40 | because the former binds in the default space and the latter in the 41 | binding space. The Rhombus expander itself is, again, parameterized over 42 | the way that mapping spaces are used. 43 | 44 | The relevant syntactic category for a shrubbery is determined by its 45 | surrounding forms, and not inherent to the shrubbery. For example, 46 | @rhombus[Posn(x, y)] or @rhombus[x <> y] in the example mean one thing 47 | as an expression and another as a binding. Exactly where the contexts 48 | reside in a module depends on a specific Rhombus language that is built 49 | on the Rhombus expander. Meanwhile, a full Rhombus language can have 50 | different or more syntactic categories than the ones listed above. A 51 | Rhombus language likely allows extensions to create even more contexts, 52 | just like Racket program can have more contexts through syntactic 53 | extensions, such as @racket_match or Typed Racket. 54 | -------------------------------------------------------------------------------- /enforest/scribblings/transformer.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm" open) 3 | 4 | @title{Operator and Macro Transformers} 5 | 6 | Some contexts in a Rhombus language (likely including expression 7 | contexts) will support infix, prefix, and postfix operators. The Rhombus 8 | expander provides an _enforestation_ framework for parsing forms that 9 | involve multiple operators, each with a declared precedence and 10 | associativity. The enforestation process also allows an operator 11 | transformer to completely take over parsing of terms that follow the 12 | operator within a shrubbery group. An ``operator'' in this sense can be 13 | named by either a shrubbery operator or a shrubbery identifier, so the 14 | prefix-operator protocol suffices for defining macros in a traditional 15 | sense. 16 | 17 | For an infix operator, enforestation always parses the left-hand 18 | argument (i.e., the part before the operator) in the same context as the 19 | operator's context. For example, the left-hand argument to an infix 20 | expression operator @rhombus[+] or @rhombus[.] is always parsed as an 21 | expression. For the right-hand (or only, in the case of prefix) 22 | argument, the operator's mapping selects one of two protocols: 23 | _automatic_, where the right-hand argument is also parsed in the same 24 | context, or _macro_, where the operator's transformer receives the full 25 | sequence of terms remaining in the enclosing group. An operator using 26 | the macro protocol parses remaining terms as it sees fit, and then it 27 | returns the still-remaining terms that it does not consume. For example, 28 | @rhombus[+] for expressions is likely implemented as an automatic infix 29 | operator, since both of its arguments are also expressions, while 30 | @rhombus[.] is likely implemented as a macro infix operator so that it's 31 | right-hand ``argument'' is always parsed as a field identifier. In the 32 | earlier @rhombus[<>] and @rhombus[->] examples, @rhombus[<>] is 33 | implemented as an automatic infix operator for expressions, while 34 | @rhombus[<>] for bindings and @rhombus[->] for expressions were 35 | implemented as macro infix operators. 36 | 37 | Roughly, an operator that uses the macro protocol takes on some of the 38 | burden of dealing with precedence, at least for terms after the 39 | operator. For operators like @rhombus[.] or @rhombus[->], this is no 40 | problem, because the right-hand side has a fixed shape. Other operators 41 | may need to call back into the enforestation algorithm, and the Rhombus 42 | expander provides facilities to enable that. 43 | 44 | A postfix operator is implemented as a macro infix operator that 45 | consumes no additional terms after the operator. For example, a postfix 46 | @rhombus[!] might be defined (shadowing the normal @rhombus[!] for 47 | ``not'') as follows: 48 | 49 | @(rhombusblock: 50 | fun 51 | | factorial(0): 1 52 | | factorial(n): n*factorial(n-1) 53 | 54 | expr.macro '($a ! $tail ...): 55 | values('(factorial($a)), tail) 56 | 57 | 10! + 1 // = 3628801 58 | ) 59 | 60 | Since the Rhombus expander provides a way for macro transformers to 61 | resume enforestation, all operators could be implemented with the 62 | macro protocol. The automatic protocol is just a convenient shortcut. 63 | 64 | Some contexts might constrain the allowed forms of operators to prefix 65 | or infix, constrain the names used for operators, and/or eschew one of 66 | the operator protocols. For example, declaration and definition contexts 67 | might allow only macro prefix operators with identifier names. The 68 | @Rhombus implementation makes that choice, and it also allows 69 | expression forms with operators to appear in the same places as 70 | declaration and definition forms. 71 | -------------------------------------------------------------------------------- /enforest/sequence.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse) 4 | syntax/parse 5 | "syntax-local.rkt" 6 | "private/transform.rkt" 7 | "hier-name-parse.rkt" 8 | "name-root.rkt" 9 | "private/name-path-op.rkt" 10 | "private/check.rkt") 11 | 12 | ;; Degenerate variant of enforestation with only prefix operators that 13 | ;; must consume the full group, but also with the opportinuty consume 14 | ;; subsequent groups 15 | 16 | (provide sequence-transformer? 17 | sequence-transformer-proc 18 | sequence-transformer 19 | 20 | define-sequence-transform) 21 | 22 | (struct sequence-transformer (proc)) 23 | 24 | (define-syntax (define-sequence-transform stx) 25 | (syntax-parse stx 26 | [(_ (~alt (~optional (~seq #:syntax-class form) 27 | #:defaults ([form #':form])) 28 | (~optional (~seq #:apply-transformer apply-transformer) 29 | #:defaults ([apply-transformer #'apply-transformer])) 30 | (~optional (~seq #:predicate form?) 31 | #:defaults ([form? #'#f])) 32 | (~optional (~seq #:desc form-kind-str) 33 | #:defaults ([form-kind-str "form"])) 34 | (~optional (~seq #:in-space in-space) 35 | #:defaults ([in-space #'values])) 36 | (~optional (~seq #:name-path-op name-path-op) 37 | #:defaults ([name-path-op #'name-path-op])) 38 | (~optional (~seq #:name-root-ref name-root-ref) 39 | #:defaults ([name-root-ref #'name-root-ref])) 40 | (~optional (~seq #:transformer-ref transformer-ref) 41 | #:defaults ([transformer-ref #'transformer-ref])) 42 | (~optional (~seq #:check-result check-result) 43 | #:defaults ([check-result #'check-is-syntax]))) 44 | ...) 45 | #`(begin 46 | (define-syntax-class form 47 | (pattern ((~datum group) . (~var hname (:hier-name-seq values name-path-op name-root-ref))) 48 | #:do [(define head-id #'hname.name)] 49 | #:do [(define t (syntax-local-value* (in-space head-id) transformer-ref))] 50 | #:when t 51 | #:attr id head-id 52 | #:attr tail #'hname.tail)) 53 | (define (apply-transformer head-id head-tail tail-tail) 54 | (define t (syntax-local-value* (in-space head-id) transformer-ref)) 55 | (apply-sequence-transformer t head-id 56 | (datum->syntax #f (cons head-id head-tail)) 57 | tail-tail 58 | check-result)) 59 | #,@(if (syntax-e #'form?) 60 | #`((define (form? e) 61 | (syntax-parse e 62 | [((~datum group) . (~var hname (:hier-name-seq values name-path-op name-root-ref))) 63 | (and (syntax-local-value* (in-space #'hname.name) transformer-ref) 64 | #t)] 65 | [_ #f]))) 66 | '()))])) 67 | 68 | (define (apply-sequence-transformer t id stx tail checker) 69 | (define proc (sequence-transformer-proc t)) 70 | (call-as-transformer 71 | id 72 | (lambda (in out) 73 | (define-values (forms new-tail) (proc (in stx) (in tail))) 74 | (check-transformer-result (out (datum->syntax #f (checker forms proc))) 75 | (out new-tail) 76 | proc)))) 77 | -------------------------------------------------------------------------------- /enforest/syntax-local.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide syntax-local-value*) 4 | 5 | (define (syntax-local-value* id ref) 6 | (define-values (v next) (syntax-local-value/immediate id (lambda () (values #f #f)))) 7 | (cond 8 | [(ref v) => (lambda (v) v)] 9 | [next (syntax-local-value* next ref)] 10 | [else #f])) 11 | -------------------------------------------------------------------------------- /enforest/transformer-result.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require "private/transform.rkt") 3 | 4 | (provide check-transformer-result) 5 | -------------------------------------------------------------------------------- /enforest/transformer.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse) 4 | syntax/parse 5 | "syntax-local.rkt" 6 | "private/transform.rkt" 7 | "hier-name-parse.rkt" 8 | "name-root.rkt" 9 | "private/name-path-op.rkt" 10 | "private/check.rkt") 11 | 12 | ;; Degenerate variant of enforestation with only prefix operators 13 | ;; that must consume the full group 14 | 15 | (provide transformer? 16 | transformer-proc 17 | transformer 18 | 19 | transform-in 20 | transform-out 21 | call-as-transformer 22 | 23 | define-transform) 24 | 25 | (struct transformer (proc)) 26 | 27 | (define-syntax (define-transform stx) 28 | (syntax-parse stx 29 | [(_ (~alt (~optional (~seq #:syntax-class form) 30 | #:defaults ([form #':form])) 31 | (~optional (~seq #:predicate form?) 32 | #:defaults ([form? #'#f])) 33 | (~optional (~seq #:desc form-kind-str) 34 | #:defaults ([form-kind-str "form"])) 35 | (~optional (~seq #:in-space in-space) 36 | #:defaults ([in-space #'values])) 37 | (~optional (~seq #:name-path-op name-path-op) 38 | #:defaults ([name-path-op #'name-path-op])) 39 | (~optional (~seq #:name-root-ref name-root-ref) 40 | #:defaults ([name-root-ref #'name-root-ref])) 41 | (~optional (~seq #:transformer-ref transformer-ref) 42 | #:defaults ([transformer-ref #'transformer-ref])) 43 | (~optional (~seq #:check-result check-result) 44 | #:defaults ([check-result #'check-is-syntax]))) 45 | ...) 46 | #`(begin 47 | (define-syntax-class form 48 | (pattern ((~datum group) . (~var hname (:hier-name-seq values name-path-op name-root-ref))) 49 | #:do [(define head-id #'hname.name)] 50 | #:do [(define t (syntax-local-value* (in-space head-id) transformer-ref))] 51 | #:when t 52 | #:attr parsed (apply-transformer t head-id 53 | (datum->syntax #f (cons head-id #'hname.tail)) 54 | check-result))) 55 | #,@(if (syntax-e #'form?) 56 | #`((define (form? e) 57 | (syntax-parse e 58 | [((~datum group) . (~var hname (:hier-name-seq values name-path-op name-root-ref))) 59 | (and (syntax-local-value* (in-space #'hname.name) transformer-ref) 60 | #t)] 61 | [_ #f]))) 62 | '()))])) 63 | 64 | (define (apply-transformer t id stx checker) 65 | (define proc (transformer-proc t)) 66 | (call-as-transformer 67 | id 68 | (lambda (in out) 69 | (define forms (checker (proc (in stx)) proc)) 70 | (out (datum->syntax #f forms))))) 71 | -------------------------------------------------------------------------------- /info.rkt: -------------------------------------------------------------------------------- 1 | #lang info 2 | 3 | (define collection 'multi) 4 | 5 | (define deps 6 | '(["base" #:version "8.3.0.8"] 7 | "syntax-color-lib" 8 | "parser-tools-lib" 9 | ["scribble-lib" #:version "1.43"] 10 | "sandbox-lib")) 11 | 12 | (define build-deps 13 | '("at-exp-lib" 14 | "racket-doc")) 15 | -------------------------------------------------------------------------------- /rhombus/info.rkt: -------------------------------------------------------------------------------- 1 | #lang info 2 | 3 | (define module-suffixes '(#"rhm")) 4 | 5 | (define scribblings '(("scribblings/rhombus.scrbl" (multi-page) (language)))) 6 | 7 | -------------------------------------------------------------------------------- /rhombus/macro.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require "private/bounce.rkt") 3 | 4 | (require (for-syntax "main.rkt")) 5 | (provide (for-syntax (all-from-out "main.rkt"))) 6 | 7 | (bounce "private/for-meta.rkt" 8 | "private/expression-syntax.rkt" 9 | "private/binding-syntax.rkt" 10 | "private/definition-syntax.rkt" 11 | "private/declaration-syntax.rkt" 12 | "private/annotation-syntax.rkt" 13 | "private/static-info-syntax.rkt" 14 | "private/dot-syntax.rkt" 15 | "private/import-syntax.rkt" 16 | "private/syntax-error.rkt" 17 | "private/parsed.rkt" 18 | "private/syntax-meta-value.rkt" 19 | (submod "private/syntax-class.rkt" for-macro)) 20 | 21 | (require (only-in "private/import.rkt" for_meta)) 22 | (provide (for-space rhombus/import for_meta)) 23 | -------------------------------------------------------------------------------- /rhombus/main.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse 4 | shrubbery/print) 5 | racket/interaction-info 6 | "private/bounce.rkt" 7 | "private/parse.rkt" 8 | "private/forwarding-sequence.rkt") 9 | 10 | (provide (rename-out [rhombus-module-begin #%module-begin]) 11 | #%top-interaction) 12 | 13 | (bounce "private/implicit.rkt" 14 | "private/underscore.rkt" 15 | "private/arithmetic.rkt" 16 | "private/string.rkt" 17 | "private/dot.rkt" 18 | "private/class.rkt" 19 | "private/define.rkt" 20 | "private/value.rkt" 21 | "private/import.rkt" 22 | "private/export.rkt" 23 | "private/module-path.rkt" 24 | "private/operator.rkt" 25 | "private/annotation.rkt" 26 | "private/list.rkt" 27 | "private/array.rkt" 28 | "private/map.rkt" 29 | "private/set.rkt" 30 | "private/map-ref.rkt" 31 | "private/assign.rkt" 32 | "private/equal.rkt" 33 | "private/function.rkt" 34 | "private/begin.rkt" 35 | "private/cond.rkt" 36 | "private/match.rkt" 37 | "private/quasiquote.rkt" 38 | "private/keyword.rkt" 39 | "private/symbol.rkt" 40 | "private/values.rkt" 41 | "private/print.rkt" 42 | "private/syntax-object.rkt" 43 | "private/syntax-class.rkt") 44 | 45 | (module reader syntax/module-reader 46 | #:language 'rhombus 47 | #:read (lambda (in) (list (syntax->datum (parse-all in)))) 48 | #:read-syntax (lambda (src in) (list (parse-all in #:source src))) 49 | #:info (lambda (key default make-default) 50 | (case key 51 | [(drracket:default-extension) "rhm"] 52 | [else (get-info-proc key default make-default)])) 53 | #:whole-body-readers? #t 54 | (require shrubbery/parse 55 | (only-in (submod shrubbery reader) 56 | get-info-proc))) 57 | 58 | (module configure-runtime racket/base 59 | (require rhombus/runtime-config)) 60 | 61 | (define-syntax (rhombus-module-begin stx) 62 | (error-syntax->string-handler 63 | (lambda (s len) 64 | (shrubbery-syntax->string s #:max-length len))) 65 | (syntax-parse stx 66 | [(_ (top . content)) 67 | (unless (eq? 'top (syntax-e #'top)) 68 | (raise-syntax-error #f "ill-formed body" stx)) 69 | #`(#%module-begin 70 | (module configure-runtime racket/base (require rhombus/runtime-config)) 71 | (rhombus-forwarding-sequence 72 | (rhombus-top . content)))])) 73 | 74 | ;; splices content of any block as its own top-level group: 75 | (define-syntax (#%top-interaction stx) 76 | (syntax-parse stx 77 | #:datum-literals (group block) 78 | [(form-id . (top form ... (group (block inner-form ...)) . content)) 79 | #'(form-id . (top form ... inner-form ... . content))] 80 | [(_ . (top . content)) 81 | #'(rhombus-top . content)])) 82 | -------------------------------------------------------------------------------- /rhombus/parse.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require "private/parse.rkt") 4 | 5 | (provide (all-from-out "private/parse.rkt")) 6 | -------------------------------------------------------------------------------- /rhombus/private/annotation-syntax.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse 4 | enforest/transformer-result 5 | enforest/proc-name 6 | "tail.rkt" 7 | "static-info-pack.rkt") 8 | "definition.rkt" 9 | (submod "annotation.rkt" for-class) 10 | "syntax.rkt" 11 | "name-root.rkt" 12 | (for-syntax "name-root.rkt") 13 | "parse.rkt") 14 | 15 | (provide annotation 16 | (for-syntax annotation_ct)) 17 | 18 | (define-simple-name-root annotation 19 | macro) 20 | 21 | (begin-for-syntax 22 | (define-simple-name-root annotation_ct 23 | pack_predicate)) 24 | 25 | (define-syntax macro 26 | (make-operator-definition-transformer 'macro 27 | in-annotation-space 28 | #'make-annotation-prefix-operator 29 | #'make-annotation-infix-operator 30 | #'annotation-prefix+infix-operator)) 31 | 32 | (begin-for-syntax 33 | (struct annotation-prefix+infix-operator (prefix infix) 34 | #:property prop:annotation-prefix-operator (lambda (self) (annotation-prefix+infix-operator-prefix self)) 35 | #:property prop:annotation-infix-operator (lambda (self) (annotation-prefix+infix-operator-infix self)))) 36 | 37 | (define-for-syntax (parse-annotation-macro-result form proc) 38 | (unless (syntax? form) 39 | (raise-result-error (proc-name proc) "syntax?" form)) 40 | (syntax-parse #`(group #,form) 41 | [c::annotation #'c.parsed])) 42 | 43 | (define-for-syntax (make-annotation-infix-operator name prec protocol proc assc) 44 | (annotation-infix-operator 45 | name 46 | prec 47 | protocol 48 | (lambda (form1 tail) 49 | (define-values (form new-tail) (syntax-parse tail 50 | [(head . tail) (proc #`(parsed #,form1) (pack-tail #'tail) #'head)])) 51 | (check-transformer-result (parse-annotation-macro-result form proc) 52 | (unpack-tail new-tail proc) 53 | proc)) 54 | assc)) 55 | 56 | (define-for-syntax (make-annotation-prefix-operator name prec protocol proc) 57 | (annotation-prefix-operator 58 | name 59 | prec 60 | protocol 61 | (lambda (tail) 62 | (define-values (form new-tail) (syntax-parse tail 63 | [(head . tail) (proc (pack-tail #'tail) #'head)])) 64 | (check-transformer-result (parse-annotation-macro-result form proc) 65 | (unpack-tail new-tail proc) 66 | proc)))) 67 | 68 | (define-for-syntax (pack_predicate predicate [static-infos #'(parens)]) 69 | #`(parsed #,(annotation-form #`(rhombus-expression (group #,predicate)) 70 | (pack-static-infos static-infos 'annotation.pack_predicate)))) 71 | -------------------------------------------------------------------------------- /rhombus/private/arithmetic.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse 4 | "srcloc.rkt") 5 | "expression.rkt" 6 | "define-operator.rkt" 7 | (only-in "dot.rkt" 8 | |.|)) 9 | 10 | (provide (rename-out [rhombus+ +] 11 | [rhombus- -] 12 | [rhombus* *] 13 | [rhombus/ /] 14 | [rhombus< <] 15 | [rhombus<= <=] 16 | [rhombus>= >=] 17 | [rhombus> >]) 18 | .= 19 | sqrt cos sin tan log exp expt 20 | floor ceiling round 21 | 22 | ! 23 | && 24 | \|\| 25 | 26 | == 27 | ===) 28 | 29 | (define-infix rhombus+ + 30 | #:weaker-than (rhombus* rhombus/) 31 | #:same-as (rhombus-)) 32 | 33 | (define-syntax rhombus- 34 | (expression-prefix+infix-operator 35 | (prefix rhombus- - #:weaker-than (rhombus* rhombus/)) 36 | (infix rhombus- - #:weaker-than (rhombus* rhombus/)))) 37 | 38 | (define-infix rhombus* * 39 | #:same-on-left-as (rhombus/)) 40 | 41 | (define-infix rhombus/ /) 42 | 43 | (define-prefix ! not 44 | #:stronger-than (&& \|\|)) 45 | 46 | (define-infix && and 47 | #:weaker-than (rhombus+ rhombus- rhombus* rhombus/) 48 | #:stronger-than (\|\|)) 49 | 50 | (define-infix \|\| or 51 | #:weaker-than (rhombus+ rhombus- rhombus* rhombus/)) 52 | 53 | (define-syntax-rule (define-comp-infix name racket-name) 54 | (define-infix name racket-name 55 | #:weaker-than (rhombus+ rhombus- rhombus* rhombus/) 56 | #:same-as (rhombus> rhombus>= .= rhombus<=) 57 | #:stronger-than (\|\| &&) 58 | #:associate 'none)) 59 | 60 | (define-comp-infix rhombus< <) 61 | (define-comp-infix rhombus<= <=) 62 | (define-comp-infix .= =) 63 | (define-comp-infix rhombus>= >=) 64 | (define-comp-infix rhombus> >) 65 | 66 | (define-syntax-rule (define-eql-infix name racket-name) 67 | (define-infix name racket-name 68 | #:weaker-than (rhombus+ rhombus- rhombus* rhombus/ |.|) 69 | #:stronger-than (\|\| &&) 70 | #:associate 'none)) 71 | 72 | (define-eql-infix == equal?) 73 | (define-eql-infix === eq?) 74 | -------------------------------------------------------------------------------- /rhombus/private/array.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse 4 | syntax/stx) 5 | "binding.rkt" 6 | (submod "annotation.rkt" for-class) 7 | "static-info.rkt" 8 | "map-ref-set-key.rkt" 9 | "call-result-key.rkt" 10 | "composite.rkt") 11 | 12 | (provide Array 13 | (for-space rhombus/binding Array) 14 | (for-space rhombus/annotation Array) 15 | (for-space rhombus/static-info Array)) 16 | 17 | (define Array vector) 18 | 19 | (define-annotation-syntax Array 20 | (annotation-constructor #'Array #'vector? #'((#%map-ref vector-ref) 21 | (#%map-set! vector-set!)) 22 | 1 23 | (lambda (arg-id predicate-stxs) 24 | #`(for/and ([e (in-vector #,arg-id)]) 25 | (#,(car predicate-stxs) e))) 26 | (lambda (static-infoss) 27 | #`((#%ref-result #,(car static-infoss)))))) 28 | 29 | (define-static-info-syntax Array 30 | (#%call-result ((#%map-ref vector-ref) 31 | (#%map-set! vector-set!)))) 32 | 33 | (define-binding-syntax Array 34 | (binding-prefix-operator 35 | #'Array 36 | '((default . stronger)) 37 | 'macro 38 | (lambda (stx) 39 | (syntax-parse stx 40 | [(form-id ((~and tag (~datum parens)) arg ...) . tail) 41 | (define args (syntax->list #'(arg ...))) 42 | (define len (length args)) 43 | (define pred #`(lambda (v) 44 | (and (vector? v) 45 | (= (vector-length v) #,len)))) 46 | ((make-composite-binding-transformer pred 47 | (for/list ([arg (in-list args)] 48 | [i (in-naturals)]) 49 | #`(lambda (v) (vector-ref v #,i))) 50 | (for/list ([arg (in-list args)]) 51 | #'()) 52 | #:ref-result-info? #t) 53 | stx)])))) 54 | -------------------------------------------------------------------------------- /rhombus/private/assign.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse 4 | enforest/syntax-local) 5 | "binding.rkt" 6 | "expression.rkt") 7 | 8 | (provide := 9 | mutable) 10 | 11 | (define-syntax mutable 12 | (binding-transformer 13 | #'mutable 14 | (lambda (stx) 15 | (syntax-parse stx 16 | [(_ id:identifier . new-tail) 17 | (values 18 | (binding-form 19 | #'mutable-info 20 | #'id) 21 | #'new-tail)])))) 22 | 23 | (define-syntax (mutable-info stx) 24 | (syntax-parse stx 25 | [(_ static-infos id) 26 | (binding-info #'id 27 | #'() ; mutable => don't claim input's static info 28 | #'((id)) 29 | #'mutable-identifier-succeed 30 | #'mutable-bind 31 | #'id)])) 32 | 33 | (define-syntax (mutable-identifier-succeed stx) 34 | (syntax-parse stx 35 | [(_ arg-id bind-id IF success fail) 36 | #'(IF #t success fail)])) 37 | 38 | (define-syntax (mutable-bind stx) 39 | (syntax-parse stx 40 | [(_ arg-id bind-id) 41 | #'(begin 42 | (define mutable-id arg-id) 43 | (begin (set! mutable-id mutable-id)) 44 | (define-syntax bind-id 45 | (mutable-variable #'mutable-id)))])) 46 | 47 | (begin-for-syntax 48 | (struct mutable-variable (id) 49 | #:property prop:rename-transformer (struct-field-index id)) 50 | (define (mutable-variable-ref v) (and (mutable-variable? v) v))) 51 | 52 | (define-syntax := 53 | (expression-infix-operator 54 | #':= 55 | '((default . weaker)) 56 | 'automatic 57 | (lambda (form1 form2 self-stx) 58 | (define mv (and (identifier? form1) 59 | (syntax-local-value* form1 mutable-variable-ref))) 60 | (unless mv 61 | (raise-syntax-error #f 62 | "left-hand argument is not a mutable identifier" 63 | self-stx)) 64 | #`(let ([#,form1 #,form2]) 65 | (set! #,(mutable-variable-id mv) #,form1) 66 | #,form1)) 67 | 'left)) 68 | -------------------------------------------------------------------------------- /rhombus/private/begin.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse) 4 | "expression.rkt" 5 | "parse.rkt") 6 | 7 | (provide (rename-out [rhombus-begin begin])) 8 | 9 | (define-syntax rhombus-begin 10 | (expression-transformer 11 | #'rhombus-begin 12 | (lambda (stx) 13 | (syntax-parse stx 14 | #:datum-literals (alts block group) 15 | [(form-id ((~and tag block) form ...) 16 | . tail) 17 | (values 18 | #'(let () 19 | (rhombus-body-at tag form ...)) 20 | #'tail)])))) 21 | -------------------------------------------------------------------------------- /rhombus/private/bounce.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse)) 4 | 5 | (provide bounce) 6 | 7 | (define-syntax (bounce stx) 8 | (syntax-case stx () 9 | [(_ mod ...) 10 | (with-syntax ([(mod ...) ((make-syntax-introducer) #'(mod ...))]) 11 | #'(begin (begin (require mod) 12 | (provide (all-from-out mod))) 13 | ...))])) 14 | -------------------------------------------------------------------------------- /rhombus/private/call-result-key.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide #%call-result) 4 | 5 | (define #%call-result #f) 6 | -------------------------------------------------------------------------------- /rhombus/private/cond.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse) 4 | "expression.rkt" 5 | "parse.rkt" 6 | (only-in "underscore.rkt" 7 | [_ rhombus-_])) 8 | 9 | (provide (rename-out [rhombus-if if] 10 | [rhombus-cond cond])) 11 | 12 | (define-syntax rhombus-if 13 | (expression-transformer 14 | #'rhombus-if 15 | (lambda (stx) 16 | (syntax-parse stx 17 | #:datum-literals (alts) 18 | [(form-id test ... (alts alt ...) 19 | . tail) 20 | (syntax-parse #'(alt ...) 21 | #:datum-literals (block) 22 | [(((~and tag-thn block) thn ...) 23 | ((~and tag-els block) els ...)) 24 | (values 25 | #'(if (rhombus-expression (group test ...)) 26 | (rhombus-body-at tag-thn thn ...) 27 | (rhombus-body-at tag-els els ...)) 28 | #'tail)] 29 | [_ 30 | (raise-syntax-error #f 31 | "expected two alternatives" 32 | stx)])])))) 33 | 34 | (define-syntax rhombus-cond 35 | (expression-transformer 36 | #'rhombus-cond 37 | (lambda (stx) 38 | (syntax-parse stx 39 | #:datum-literals (alts block group) 40 | [(form-id (alts 41 | (block (group pred ... ((~and tag block) rhs ...))) 42 | ... 43 | (block (group (~or #:else (~literal rhombus-_)) 44 | ((~and else-tag block) else-rhs ...)))) 45 | . tail) 46 | (values 47 | #'(cond 48 | [(rhombus-expression (group pred ...)) 49 | (rhombus-body-at tag rhs ...)] 50 | ... 51 | [else 52 | (rhombus-body-at else-tag else-rhs ...)]) 53 | #'tail)] 54 | [(form-id (alts 55 | (block (group pred ... ((~and tag block) rhs ...))) 56 | ...) 57 | . tail) 58 | (values 59 | #'(cond 60 | [(rhombus-expression (group pred ...)) 61 | (rhombus-body-at tag rhs ...)] 62 | ... 63 | [else (cond-fallthrough #'form-id)]) 64 | #'tail)] 65 | [(form-id (block) . tail) 66 | (values 67 | #'(cond-fallthrough 'form-id) 68 | #'tail)])))) 69 | 70 | (define (cond-fallthrough who) 71 | (error who "no matching case")) 72 | -------------------------------------------------------------------------------- /rhombus/private/consistent.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require "srcloc.rkt") 3 | 4 | (provide check-consistent) 5 | 6 | (define (check-consistent stx ids what) 7 | (define the-id (car ids)) 8 | (for ([another-id (in-list (cdr ids))]) 9 | (unless (free-identifier=? the-id another-id) 10 | (raise-syntax-error #f 11 | (format "case ~a does not match initial case ~a" 12 | what 13 | what) 14 | stx 15 | another-id)))) 16 | -------------------------------------------------------------------------------- /rhombus/private/declaration-syntax.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse 4 | syntax/boundmap 5 | enforest/proc-name 6 | "srcloc.rkt" 7 | "tail.rkt") 8 | "name-root.rkt" 9 | "declaration.rkt" 10 | "syntax.rkt" 11 | "parse.rkt" 12 | "implicit.rkt") 13 | 14 | (provide decl) 15 | 16 | (define-simple-name-root decl 17 | macro) 18 | 19 | (define-syntax macro 20 | (make-identifier-syntax-definition-transformer (lambda (x) x) 21 | #'make-declaration-transformer)) 22 | 23 | (define-for-syntax (make-declaration-transformer proc) 24 | (declaration-transformer 25 | (lambda (tail) 26 | (syntax-parse tail 27 | [(head . tail) 28 | (unpack-declarations (proc (pack-tail #'tail) #'head) proc)])))) 29 | 30 | (define-for-syntax (unpack-declarations form proc) 31 | (syntax-parse form 32 | #:datum-literals (parens block group) 33 | [(parens (group (block (group d ...) ...))) 34 | #`((rhombus-top (group d ...)) 35 | ...)] 36 | [_ (raise-result-error (proc-name proc) "declaration-list?" form)])) 37 | -------------------------------------------------------------------------------- /rhombus/private/declaration.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/stx 4 | enforest/transformer 5 | enforest/property 6 | enforest/proc-name)) 7 | 8 | (begin-for-syntax 9 | (provide (property-out declaration-transformer) 10 | 11 | check-declaration-result) 12 | 13 | (property declaration-transformer transformer) 14 | 15 | (define (check-declaration-result forms proc) 16 | (unless (stx-list? forms) (raise-result-error (proc-name proc) "stx-list?" forms)) 17 | forms)) 18 | -------------------------------------------------------------------------------- /rhombus/private/definition-syntax.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse 4 | syntax/boundmap 5 | enforest/proc-name 6 | "srcloc.rkt" 7 | "tail.rkt") 8 | "name-root.rkt" 9 | "definition.rkt" 10 | "syntax.rkt" 11 | "parse.rkt") 12 | 13 | (provide defn) 14 | 15 | (define-simple-name-root defn 16 | macro 17 | sequence_macro) 18 | 19 | ;; ---------------------------------------- 20 | 21 | (define-syntax macro 22 | (make-identifier-syntax-definition-transformer (lambda (x) x) 23 | #'make-definition-transformer)) 24 | 25 | (define-for-syntax (make-definition-transformer proc) 26 | (definition-transformer 27 | (lambda (stx) 28 | (define defns (syntax-parse stx 29 | [(head . tail) (proc (pack-tail #'tail) #'head)])) 30 | (unpack-definitions defns proc)))) 31 | 32 | (define-for-syntax (unpack-definitions form proc) 33 | (syntax-parse form 34 | #:datum-literals (parens block group) 35 | [(parens (group (block (group d ...) ...))) 36 | #`((rhombus-definition (group d ...)) 37 | ...)] 38 | [_ (raise-result-error (proc-name proc) "definition-list?" form)])) 39 | 40 | ;; ---------------------------------------- 41 | 42 | (define-syntax sequence_macro 43 | (make-identifier-syntax-definition-sequence-transformer 44 | (lambda (x) x) 45 | #'make-definition-sequence-transformer)) 46 | 47 | (define-for-syntax (make-definition-sequence-transformer proc) 48 | (definition-sequence-transformer 49 | (lambda (stx tail) 50 | (define-values (defns new-tail) 51 | (syntax-parse stx 52 | [(head . h-tail) (proc (pack-tail #'h-tail) (pack-block-tail tail) #'head)])) 53 | (values (unpack-definitions defns proc) 54 | (unpack-block-tail new-tail proc))))) 55 | 56 | (begin-for-syntax 57 | (define block-with-raw (syntax-property 58 | (syntax-property (datum->syntax #f 'block) 'raw "{ ") 59 | 'raw-tail " }")) 60 | (define parens-with-raw (syntax-property 61 | (syntax-property (datum->syntax #f 'parens) 'raw "(") 62 | 'raw-tail ")")) 63 | (define group-with-raw (syntax-property (datum->syntax #f 'group) 'raw '())) 64 | 65 | (define (pack-block-tail tail) 66 | #`(#,parens-with-raw (#,group-with-raw (#,block-with-raw . #,tail)))) 67 | 68 | (define (unpack-block-tail packed-tail proc) 69 | (syntax-parse packed-tail 70 | #:datum-literals (block group parens) 71 | [(parens (group (block . tail))) #'tail] 72 | [else 73 | (raise-result-error (if (symbol? proc) proc (proc-name proc)) 74 | "rhombus-syntax-block?" 75 | packed-tail)]))) 76 | -------------------------------------------------------------------------------- /rhombus/private/definition.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/stx 4 | enforest/transformer 5 | enforest/sequence 6 | enforest/property 7 | enforest/proc-name)) 8 | 9 | (begin-for-syntax 10 | (provide (property-out definition-transformer) 11 | (property-out definition-sequence-transformer) 12 | 13 | check-definition-result) 14 | 15 | (property definition-transformer transformer) 16 | (property definition-sequence-transformer sequence-transformer) 17 | 18 | (define (check-definition-result forms proc) 19 | (unless (stx-list? forms) (raise-result-error (proc-name proc) "stx-list?" forms)) 20 | forms)) 21 | -------------------------------------------------------------------------------- /rhombus/private/dot-provider-key.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide #%dot-provider) 4 | 5 | (define #%dot-provider #f) 6 | -------------------------------------------------------------------------------- /rhombus/private/dot-syntax.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse 4 | "tail.rkt") 5 | "name-root.rkt" 6 | (for-syntax "name-root.rkt") 7 | (submod "dot.rkt" for-dot-provider) 8 | "syntax.rkt" 9 | "parse.rkt") 10 | 11 | (provide dot 12 | (for-syntax dot_ct)) 13 | 14 | (define-simple-name-root dot 15 | macro) 16 | 17 | (begin-for-syntax 18 | (define-simple-name-root dot_ct 19 | provider_key)) 20 | 21 | (define-for-syntax provider_key #'#%dot-provider) 22 | 23 | (define-syntax macro 24 | (make-identifier-syntax-definition-transformer 25 | (lambda (x) x) 26 | #'make-dot-provider-transformer)) 27 | 28 | (define-for-syntax (make-dot-provider-transformer proc) 29 | (dot-provider 30 | (lambda (left dot right) 31 | (define e (proc (pack-tail #`((parsed #,left) #,dot #,right)) dot)) 32 | #`(rhombus-expression (group #,e))))) 33 | -------------------------------------------------------------------------------- /rhombus/private/empty-group.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide convert-empty-group 4 | convert-empty-alts 5 | error-empty-group) 6 | 7 | (define (convert-empty-group at-depth l) 8 | (cond 9 | [(zero? at-depth) 10 | (define u (cdr (syntax-e l))) 11 | (if (or (null? u) 12 | (and (syntax? u) (null? (syntax-e u)))) 13 | null 14 | (list l))] 15 | [else (for/list ([g (in-list (syntax->list l))]) 16 | (convert-empty-group (sub1 at-depth) g))])) 17 | 18 | (define (convert-empty-alts at-depth l) 19 | (cond 20 | [(zero? at-depth) 21 | (define u (cdr (syntax-e l))) 22 | (cond 23 | [(or (null? u) 24 | (and (syntax? u) (null? (syntax-e u)))) 25 | (define a (car (syntax-e l))) 26 | (list (datum->syntax a 'block a a))] 27 | [else l])] 28 | [else (for/list ([g (in-list (syntax->list l))]) 29 | (convert-empty-alts (sub1 at-depth) g))])) 30 | 31 | (define (error-empty-group at-depth l) 32 | (cond 33 | [(zero? at-depth) 34 | (define u (cdr (syntax-e l))) 35 | (when (or (null? u) 36 | (and (syntax? u) (null? (syntax-e u)))) 37 | (error '|'| "generated an empty group")) 38 | l] 39 | [else (for/list ([g (in-list (syntax->list l))]) 40 | (error-empty-group (sub1 at-depth) g))])) 41 | -------------------------------------------------------------------------------- /rhombus/private/equal.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse) 4 | "expression+binding.rkt") 5 | 6 | (provide (rename-out [rhombus= =])) 7 | 8 | (define-syntax rhombus= 9 | (make-expression+binding-infix-operator 10 | #'rhombus= 11 | '((default . weaker)) 12 | 'macro 13 | 'none 14 | ;; expression 15 | (lambda (form tail) 16 | (syntax-parse tail 17 | #:datum-literals (op) 18 | [((op o) . _) 19 | (raise-syntax-error #f 20 | "not an expression operator" 21 | #'o)])) 22 | ;; binding 23 | (lambda (form tail) 24 | (syntax-parse tail 25 | #:datum-literals (op) 26 | [((op o) . _) 27 | (raise-syntax-error #f 28 | "not a binding operator" 29 | #'o)])))) 30 | -------------------------------------------------------------------------------- /rhombus/private/expression+binding.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base) 3 | "expression.rkt" 4 | "binding.rkt") 5 | 6 | (provide (for-syntax make-expression+binding-prefix-operator 7 | make-expression+binding-infix-operator)) 8 | 9 | (begin-for-syntax 10 | (struct expression+binding-prefix-operator (exp-op bind-op) 11 | #:property prop:expression-prefix-operator (lambda (self) (expression+binding-prefix-operator-exp-op self)) 12 | #:property prop:binding-prefix-operator (lambda (self) (expression+binding-prefix-operator-bind-op self))) 13 | (define (make-expression+binding-prefix-operator name prec protocol exp bind) 14 | (expression+binding-prefix-operator 15 | (expression-prefix-operator name prec protocol exp) 16 | (binding-prefix-operator name prec protocol bind)))) 17 | 18 | (begin-for-syntax 19 | (struct expression+binding-infix-operator (exp-op bind-op) 20 | #:property prop:expression-infix-operator (lambda (self) (expression+binding-infix-operator-exp-op self)) 21 | #:property prop:binding-infix-operator (lambda (self) (expression+binding-infix-operator-bind-op self))) 22 | (define (make-expression+binding-infix-operator name prec protocol assc exp bind) 23 | (expression+binding-infix-operator 24 | (expression-infix-operator name prec protocol exp assc) 25 | (binding-infix-operator name prec protocol bind assc)))) 26 | -------------------------------------------------------------------------------- /rhombus/private/expression+definition.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base) 3 | "expression.rkt" 4 | "definition.rkt") 5 | 6 | (provide (for-syntax make-expression+definition-transformer)) 7 | 8 | (begin-for-syntax 9 | (struct expression+definition-transformer (exp def) 10 | #:property prop:expression-prefix-operator (lambda (self) (expression+definition-transformer-exp self)) 11 | #:property prop:definition-transformer (lambda (self) (expression+definition-transformer-def self))) 12 | (define (make-expression+definition-transformer exp def) 13 | (expression+definition-transformer exp def))) 14 | -------------------------------------------------------------------------------- /rhombus/private/expression-syntax.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse 4 | enforest/transformer-result 5 | "srcloc.rkt" 6 | "tail.rkt") 7 | "name-root.rkt" 8 | "syntax.rkt" 9 | "expression.rkt" 10 | "parse.rkt" 11 | "call-result-key.rkt" 12 | "wrap-expression.rkt" 13 | (for-syntax "name-root.rkt")) 14 | 15 | (provide expr 16 | (for-syntax expr_ct)) 17 | 18 | (module+ for-define 19 | (provide (for-syntax make-expression-infix-operator 20 | make-expression-prefix-operator))) 21 | 22 | (define-simple-name-root expr 23 | macro 24 | rule) 25 | 26 | (begin-for-syntax 27 | (define-simple-name-root expr_ct 28 | call_result_key)) 29 | 30 | (define-syntax macro 31 | (make-operator-definition-transformer 'macro 32 | (lambda (x) x) 33 | #'make-expression-prefix-operator 34 | #'make-expression-infix-operator 35 | #'expression-prefix+infix-operator)) 36 | 37 | (define-syntax rule 38 | (make-operator-definition-transformer 'rule 39 | (lambda (x) x) 40 | #'make-expression-prefix-operator 41 | #'make-expression-infix-operator 42 | #'expression-prefix+infix-operator)) 43 | 44 | (define-for-syntax (make-expression-infix-operator name prec protocol proc assc) 45 | (expression-infix-operator 46 | name 47 | prec 48 | protocol 49 | (if (eq? protocol 'automatic) 50 | (lambda (form1 form2 stx) 51 | (wrap-expression (check-expression-result 52 | (proc #`(parsed #,form1) #`(parsed #,form2) stx) 53 | proc))) 54 | (lambda (form1 tail) 55 | (define-values (form new-tail) (syntax-parse tail 56 | [(head . tail) (proc #`(parsed #,form1) (pack-tail #'tail #:after #'head) #'head)])) 57 | (check-transformer-result (wrap-expression (check-expression-result form proc)) 58 | (unpack-tail new-tail proc) 59 | proc))) 60 | assc)) 61 | 62 | (define-for-syntax (make-expression-prefix-operator name prec protocol proc) 63 | (expression-prefix-operator 64 | name 65 | prec 66 | protocol 67 | (if (eq? protocol 'automatic) 68 | (lambda (form stx) 69 | (wrap-expression (check-expression-result 70 | (proc #`(parsed #,form) stx) 71 | proc))) 72 | (lambda (tail) 73 | (define-values (form new-tail) (syntax-parse tail 74 | [(head . tail) (proc (pack-tail #'tail #:after #'head) #'head)])) 75 | (check-transformer-result (wrap-expression (check-expression-result form proc)) 76 | (unpack-tail new-tail proc) 77 | proc))))) 78 | 79 | (define-for-syntax call_result_key #'#%call-result) 80 | -------------------------------------------------------------------------------- /rhombus/private/expression.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse 4 | enforest/operator 5 | enforest/property 6 | enforest/proc-name 7 | "introducer.rkt")) 8 | 9 | (begin-for-syntax 10 | (provide (property-out expression-prefix-operator) 11 | (property-out expression-infix-operator) 12 | 13 | expression-transformer 14 | 15 | make-identifier-expression 16 | 17 | check-expression-result 18 | 19 | in-expression-space 20 | 21 | (struct-out expression-prefix+infix-operator))) 22 | 23 | (provide define-expression-syntax) 24 | 25 | (begin-for-syntax 26 | (property expression-prefix-operator prefix-operator) 27 | (property expression-infix-operator infix-operator) 28 | 29 | (define (expression-transformer name proc) 30 | (expression-prefix-operator name '((default . stronger)) 'macro proc)) 31 | 32 | (define (make-identifier-expression id) 33 | id) 34 | 35 | (define (check-expression-result form proc) 36 | (unless (syntax? form) (raise-result-error (proc-name proc) "syntax?" form)) 37 | form) 38 | 39 | (define in-expression-space (make-interned-syntax-introducer/add 'rhombus/expression))) 40 | 41 | (define-syntax (define-expression-syntax stx) 42 | (syntax-parse stx 43 | [(_ name:id rhs) 44 | (quasisyntax/loc stx 45 | (define-syntax #,(in-expression-space #'name) rhs))])) 46 | 47 | (begin-for-syntax 48 | (struct expression-prefix+infix-operator (prefix infix) 49 | #:property prop:expression-prefix-operator (lambda (self) (expression-prefix+infix-operator-prefix self)) 50 | #:property prop:expression-infix-operator (lambda (self) (expression-prefix+infix-operator-infix self)))) 51 | -------------------------------------------------------------------------------- /rhombus/private/for-meta.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse) 4 | "declaration.rkt" 5 | "parse.rkt" 6 | (for-syntax "parse.rkt")) 7 | 8 | (provide for_meta) 9 | 10 | (define-syntax for_meta 11 | (declaration-transformer 12 | (lambda (stx) 13 | (syntax-parse stx 14 | #:datum-literals (block) 15 | [(form-id (block form ...)) 16 | #'((begin-for-syntax 17 | (rhombus-top form ...)))])))) 18 | -------------------------------------------------------------------------------- /rhombus/private/forwarding-sequence.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse)) 4 | 5 | (provide rhombus-forwarding-sequence 6 | 7 | ;; wrap `rhombus-forward` around a sequence of declarations 8 | ;; to make any bindings among the declarations visible only 9 | ;; after the declarations 10 | rhombus-forward) 11 | 12 | (define-syntax (rhombus-forwarding-sequence stx) 13 | (syntax-parse stx 14 | [(_ #:need-end-expr orig . tail) 15 | #'(sequence #:need-end-expr orig base-ctx add-ctx remove-ctx . tail)] 16 | [(_ . tail) 17 | #'(sequence #f #f base-ctx add-ctx remove-ctx . tail)])) 18 | 19 | (define-syntax (sequence stx) 20 | (let loop ([stx stx] [accum null]) 21 | (syntax-parse stx 22 | [(_ mode orig base-ctx add-ctx remove-ctx) 23 | (when (and (eq? (syntax-e #'mode) '#:need-end-expr) 24 | (syntax-e #'orig)) 25 | (raise-syntax-error #f "block does not end with an expression" #'orig)) 26 | #`(begin #,@(reverse accum))] 27 | [(_ mode orig base-ctx add-ctx remove-ctx (~and form ((~literal quote) v)) . forms) 28 | (loop #'(_ mode orig base-ctx add-ctx remove-ctx . forms) 29 | (cons #'form accum))] 30 | [(_ mode orig base-ctx add-ctx remove-ctx form . forms) 31 | (define exp-form (local-expand #'form 32 | (syntax-local-context) 33 | (list #'rhombus-forward 34 | #'define-values 35 | #'define-syntaxes 36 | ;; etc. 37 | #'begin 38 | #'provide 39 | #'require 40 | #'#%require 41 | #'#%provide 42 | #'begin-for-syntax) 43 | #f)) 44 | (syntax-parse exp-form 45 | #:literals (begin define-values define-syntaxes rhombus-forward) 46 | [(rhombus-forward . sub-forms) 47 | (define introducer (make-syntax-introducer #t)) 48 | #`(begin 49 | #,@(reverse accum) 50 | (sequence #f #f base-ctx #,(introducer #'add-ctx) base-ctx . sub-forms) 51 | (sequence mode orig base-ctx add-ctx #,(introducer #'remove-ctx) . #,(introducer #'forms)))] 52 | [(begin form ...) 53 | (define seq #`(sequence mode orig base-ctx add-ctx remove-ctx form ... . forms)) 54 | (if (null? accum) 55 | seq 56 | #`(begin #,@(reverse accum) #,seq))] 57 | [((~and def (~or define-values define-syntaxes)) (id ...) rhs) 58 | #:with (new-id ...) ((make-syntax-delta-introducer #'remove-ctx #'base-ctx) 59 | ((make-syntax-delta-introducer #'add-ctx #'base-ctx) 60 | #'(id ...) 61 | 'add) 62 | 'remove) 63 | #`(begin 64 | #,@(reverse accum) 65 | #,(syntax/loc exp-form 66 | (def (new-id ...) rhs)) 67 | (sequence #:need-end-expr orig base-ctx add-ctx remove-ctx . forms))] 68 | [_ #`(begin 69 | #,@(reverse accum) 70 | #,exp-form 71 | (sequence #:saw-non-defn #f base-ctx add-ctx remove-ctx . forms))])]))) 72 | 73 | (define-syntax (rhombus-forward stx) 74 | (raise-syntax-error #f 75 | "should not get expanded" 76 | stx)) 77 | -------------------------------------------------------------------------------- /rhombus/private/import-syntax.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse 4 | syntax/boundmap 5 | enforest/proc-name 6 | "srcloc.rkt" 7 | "tail.rkt") 8 | "name-root.rkt" 9 | (submod "import.rkt" for-meta) 10 | "syntax.rkt" 11 | "parse.rkt") 12 | 13 | (provide imp) 14 | 15 | (define-simple-name-root imp 16 | modifier) 17 | 18 | (define-syntax modifier 19 | (make-identifier-syntax-definition-transformer (lambda (x) x) 20 | #'make-import-modifier)) 21 | 22 | (define-for-syntax (make-import-modifier proc) 23 | (import-modifier 24 | (lambda (req stx) 25 | (error "TBD")))) 26 | -------------------------------------------------------------------------------- /rhombus/private/infer-name.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require syntax/parse) 3 | 4 | (provide infer-name) 5 | 6 | (define (infer-name var-ids) 7 | (syntax-parse var-ids 8 | [(id) #'id] 9 | [_ (car (generate-temporaries (list #'rhs)))])) 10 | -------------------------------------------------------------------------------- /rhombus/private/introducer.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide make-interned-syntax-introducer/add) 4 | 5 | (define (make-interned-syntax-introducer/add sym) 6 | (define proc (make-interned-syntax-introducer sym)) 7 | (lambda (stx [mode 'add]) (proc stx mode))) 8 | -------------------------------------------------------------------------------- /rhombus/private/keyword.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse) 4 | "expression.rkt" 5 | "binding.rkt" 6 | "expression+binding.rkt" 7 | "literal.rkt") 8 | 9 | (provide keyword) 10 | 11 | (define-syntax keyword 12 | (make-expression+binding-prefix-operator 13 | #'keyword 14 | '((default . stronger)) 15 | 'macro 16 | (lambda (stx) 17 | (syntax-parse stx 18 | #:datum-literals (parens group) 19 | [(_ (parens (group k:keyword)) . tail) 20 | (values (syntax/loc stx (quote k)) 21 | #'tail)])) 22 | (lambda (stx) 23 | (syntax-parse stx 24 | #:datum-literals (parens group) 25 | [(_ (parens (group k:keyword)) . tail) 26 | (values (binding-form #'literal-infoer 27 | #'k) 28 | #'tail)])))) 29 | 30 | -------------------------------------------------------------------------------- /rhombus/private/literal.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse) 4 | "binding.rkt" 5 | "parse.rkt") 6 | 7 | (provide literal-infoer) 8 | 9 | (define-syntax (literal-infoer stx) 10 | (syntax-parse stx 11 | [(_ static-infos datum) 12 | (binding-info #'literal 13 | #'static-infos 14 | #'() 15 | #'literal-matcher 16 | #'literal-bind-nothing 17 | #'datum)])) 18 | 19 | (define-syntax (literal-matcher stx) 20 | (syntax-parse stx 21 | [(_ arg-id datum IF success fail) 22 | #'(IF (equal? arg-id (quote datum)) 23 | success 24 | fail)])) 25 | 26 | (define-syntax (literal-bind-nothing stx) 27 | (syntax-parse stx 28 | [(_ arg-id datum) 29 | #'(begin)])) 30 | 31 | -------------------------------------------------------------------------------- /rhombus/private/map-ref-set-key.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide #%map-ref 4 | #%map-set! 5 | #%map-append) 6 | 7 | (define #%map-ref #f) 8 | (define #%map-set! #f) 9 | (define #%map-append #f) 10 | -------------------------------------------------------------------------------- /rhombus/private/misuse.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide make-raise-misuse) 4 | 5 | (define (make-raise-misuse what) 6 | (lambda (self stx) 7 | (raise-syntax-error #f 8 | (format "misuse of ~a as an expression" 9 | what) 10 | stx))) 11 | -------------------------------------------------------------------------------- /rhombus/private/name-path-op.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide name-path-op) 4 | 5 | (define name-path-op '|.|) 6 | -------------------------------------------------------------------------------- /rhombus/private/name-root-ref.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse 4 | (prefix-in enforest: enforest/name-root) 5 | "srcloc.rkt")) 6 | 7 | ;; convert a hierachical layer implemented as portal syntax to a name-root 8 | 9 | (provide (for-syntax name-root-ref 10 | portal-syntax->lookup)) 11 | 12 | (define-for-syntax (name-root-ref v) 13 | (define (make get) 14 | (enforest:name-root 15 | (lambda (stxes) 16 | (syntax-parse stxes 17 | #:datum-literals (op parens |.|) 18 | [(form-id (op |.|) field:identifier . tail) 19 | (values (relocate #'field (get #'form-id "identifier" #'field)) #'tail)] 20 | [(form-id (op |.|) (parens (group (~and target (op field)))) . tail) 21 | (values (relocate #'target #`(op #,(get #'form-id "operator" #'field))) #'tail)] 22 | [(form-id (op (~and dot |.|)) . tail) 23 | (raise-syntax-error #f 24 | "expected an identifier or parentheses after dot" 25 | #'dot)] 26 | [(form-id . tail) 27 | (raise-syntax-error #f 28 | "expected a dot after name" 29 | #'form-id)])))) 30 | (or 31 | (enforest:name-root-ref v) 32 | (and 33 | (portal-syntax? v) 34 | (portal-syntax->lookup (portal-syntax-content v) make)))) 35 | 36 | (define-for-syntax (portal-syntax->lookup portal-stx make) 37 | (syntax-parse portal-stx 38 | [([(~datum import) _] pre-ctx-s ctx-s) 39 | (define pre-ctx #'pre-ctx-s) 40 | (define ctx #'ctx-s) 41 | (make (lambda (who-stx what name) 42 | (define id (datum->syntax ctx 43 | (syntax-e name) 44 | name 45 | name)) 46 | (define pre-id (datum->syntax pre-ctx (syntax-e name))) 47 | (cond 48 | [(identifier-distinct-binding id pre-id) 49 | id] 50 | [who-stx 51 | (raise-syntax-error #f 52 | (format "no such imported ~a" what) 53 | name)] 54 | [else #f])))] 55 | [((~datum map) [key val] ...) 56 | (define keys (syntax->list #'(key ...))) 57 | (define vals (syntax->list #'(val ...))) 58 | (make (lambda (who-stx what name) 59 | (or (for/or ([key (in-list keys)] 60 | [val (in-list vals)]) 61 | (and (eq? (syntax-e key) (syntax-e name)) 62 | val)) 63 | (and who-stx 64 | (raise-syntax-error #f 65 | (format "~a not provided by ~a" 66 | what 67 | (syntax-e who-stx)) 68 | name)))))] 69 | [_ #f])) 70 | -------------------------------------------------------------------------------- /rhombus/private/name-root.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse 4 | enforest/name-root 5 | "srcloc.rkt") 6 | "dot.rkt") 7 | 8 | (provide define-simple-name-root) 9 | 10 | (define-syntax-rule (define-simple-name-root id content ...) 11 | ;; portal syntax with this shape is recognized by "name-root-ref.rkt" 12 | (#%require (portal id (map [content content] ...)))) 13 | -------------------------------------------------------------------------------- /rhombus/private/nested-bindings.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse 4 | shrubbery/print) 5 | racket/unsafe/undefined 6 | "binding.rkt" 7 | "parse.rkt" 8 | "forwarding-sequence.rkt" 9 | "composite.rkt" 10 | "static-info.rkt") 11 | 12 | (provide nested-bindings) 13 | 14 | (define-syntax (nested-bindings stx) 15 | (syntax-parse stx 16 | [(_ who try-next failure rest body) 17 | (syntax-parse #'rest 18 | [#f #'(let () body)] 19 | [(rest-getter-id rest-pat rest-id rest-info) 20 | #`(let ([rest-getter-id 21 | #,(make-rest-match #'rest-id #'values #'rest-info 22 | #'(lambda (arg) 23 | (static-if try-next 24 | #f 25 | (failure 'who arg 'rest-pat))))]) 26 | (if (static-if try-next 27 | rest-getter-id 28 | #t) 29 | (let () 30 | body) 31 | (static-if try-next 32 | (try-next) 33 | (failure 'who rest-id 'list))))])] 34 | [(_ who try-next failure (arg-id arg::binding-info arg-pat arg-default) rest . tail) 35 | #:with arg-rhs (if (syntax-e #'arg-default) 36 | #'(if (eq? arg-id unsafe-undefined) 37 | (let ([arg-info.name-id (rhombus-expression arg-default)]) 38 | arg-info.name-id) 39 | arg-id) 40 | #'arg-id) 41 | #`(let ([arg-id arg-rhs]) 42 | (arg.matcher-id arg-id 43 | arg.data 44 | if-block 45 | #,(if (syntax-e #'try-next) 46 | #`(nested-bindings who try-next failure rest . tail) 47 | #`(begin 48 | (arg.binder-id arg-id arg.data) 49 | (begin 50 | (define-static-info-syntax/maybe arg.bind-id arg.bind-static-info ...) 51 | ...) 52 | (nested-bindings who try-next failure rest . tail))) 53 | (static-if try-next 54 | (try-next) 55 | (failure 'who arg-id '#,(shrubbery-syntax->string #'arg-pat)))))])) 56 | 57 | (define-syntax (if-block stx) 58 | (syntax-parse stx 59 | [(_ tst thn els) 60 | #`(if tst (racket-block thn) (racket-block els))])) 61 | 62 | (define-syntax (racket-block stx) 63 | (syntax-parse stx 64 | [(_ e) 65 | #`(let () 66 | (rhombus-forwarding-sequence 67 | #:need-end-expr #,stx 68 | e))])) 69 | 70 | (define-syntax (static-if stx) 71 | (syntax-parse stx 72 | [(_ #f thn els) #'els] 73 | [(_ _ thn els) #'thn])) 74 | -------------------------------------------------------------------------------- /rhombus/private/operator-parse.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require syntax/parse) 3 | 4 | (provide :operator 5 | :operator-or-identifier) 6 | 7 | (define-syntax-class :operator 8 | (pattern ((~datum op) name))) 9 | 10 | (define-syntax-class :operator-or-identifier 11 | (pattern ((~datum op) name)) 12 | (pattern name:identifier)) 13 | -------------------------------------------------------------------------------- /rhombus/private/parens.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse)) 4 | 5 | (provide (for-syntax 6 | :parens 7 | :block 8 | :alts)) 9 | 10 | (begin-for-syntax 11 | (define-syntax-class :parens 12 | #:description "parentheses" 13 | #:opaque 14 | (pattern (~datum parens))) 15 | (define-syntax-class :block 16 | #:description "a `:` block" 17 | #:opaque 18 | (pattern (~datum block))) 19 | (define-syntax-class :alts 20 | #:description "a block of `|` alternatives" 21 | #:opaque 22 | (pattern (~datum alts)))) 23 | -------------------------------------------------------------------------------- /rhombus/private/parsed.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base) 3 | "parse.rkt") 4 | 5 | (provide (for-syntax parsed 6 | unparsed)) 7 | 8 | (begin-for-syntax 9 | (define (parsed v) 10 | #`(parsed #,v)) 11 | 12 | (define (unparsed d) 13 | #`(rhombus-expression (group #,d)))) 14 | -------------------------------------------------------------------------------- /rhombus/private/ref-result-key.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide #%ref-result) 4 | 5 | (define #%ref-result #f) 6 | -------------------------------------------------------------------------------- /rhombus/private/set.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse 4 | "srcloc.rkt") 5 | "expression.rkt" 6 | "binding.rkt" 7 | (submod "annotation.rkt" for-class) 8 | "static-info.rkt" 9 | "map-ref-set-key.rkt" 10 | "call-result-key.rkt" 11 | "parse.rkt") 12 | 13 | (provide Set 14 | (for-space rhombus/annotation Set) 15 | (for-space rhombus/static-info Set) 16 | 17 | make_set 18 | (for-space rhombus/static-info make_set)) 19 | 20 | (module+ for-ref 21 | (provide set? 22 | set-ht 23 | set)) 24 | 25 | (module+ for-info 26 | (provide (for-syntax set-static-info))) 27 | 28 | (struct set (ht)) 29 | 30 | (define (set-member? s v) 31 | (hash-ref (set-ht s) v #f)) 32 | 33 | (define (set-member! s v in?) 34 | (if in? 35 | (hash-set! (set-ht s) v #t) 36 | (hash-remove! (set-ht s) v))) 37 | 38 | (define (Set . vals) 39 | (define base-ht (hash)) 40 | (set (for/fold ([ht base-ht]) ([val (in-list vals)]) 41 | (hash-set ht val #t)))) 42 | 43 | (define-for-syntax set-static-info 44 | #'((#%map-ref set-member?) 45 | (#%map-set! set-member!) 46 | (#%map-append set-append))) 47 | 48 | (define-annotation-syntax Set 49 | (annotation-constructor #'Set #'set? set-static-info 50 | 1 51 | (lambda (arg-id predicate-stxs) 52 | #`(for/and ([v (in-hash-keys (set-ht #,arg-id))]) 53 | (#,(car predicate-stxs) v))) 54 | (lambda (static-infoss) 55 | #`()))) 56 | 57 | (define-static-info-syntax Set 58 | (#%call-result ((#%map-ref set-ref)))) 59 | 60 | (define (make_set . vals) 61 | (define ht (make-hash)) 62 | (for ([v (in-list vals)]) 63 | (hash-set! ht v #t)) 64 | (set ht)) 65 | 66 | (define-static-info-syntax make_set 67 | (#%call-result ((#%map-ref set-member?) 68 | (#%map-set! set-member!)))) 69 | 70 | (define (set-ref s v) 71 | (hash-ref (set-ht s) v #f)) 72 | 73 | ;; macro to optimize to an inline functional update 74 | (define-syntax (set-append stx) 75 | (syntax-parse stx 76 | #:literals (Set) 77 | [(_ set1 set2) 78 | (syntax-parse (unwrap-static-infos #'set2) 79 | #:literals (Set) 80 | [(Set v) 81 | #'(set (hash-set (set-ht set1) v #t))] 82 | [_ 83 | #'(set-append/proc set1 set2)])])) 84 | 85 | (define (set-append/proc set1 set2) 86 | (set (for/fold ([ht (set-ht set1)]) ([k (in-hash-keys (set-ht set2))]) 87 | (hash-set ht k #t)))) 88 | -------------------------------------------------------------------------------- /rhombus/private/setmap.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse) 4 | "map.rkt" 5 | (submod "map.rkt" for-info) 6 | "set.rkt" 7 | (submod "set.rkt" for-info) 8 | "static-info.rkt" 9 | "parse.rkt") 10 | 11 | (provide (for-syntax parse-setmap-expression)) 12 | 13 | (define-for-syntax (parse-setmap-expression stx) 14 | (syntax-parse stx 15 | #:datum-literals (block braces parens group) 16 | [(braces elem ...) 17 | (define-values (shape rev-args) 18 | (for/fold ([shape #f] [args '()]) ([elem (in-list (syntax->list #'(elem ...)))]) 19 | (define-values (new-shape new-args) 20 | (syntax-parse elem 21 | #:datum-literals (block braces parens group) 22 | [(group key-e ... (block val)) (values 'map 23 | (list* #'(rhombus-expression val) 24 | #'(rhombus-expression (group key-e ...)) 25 | args))] 26 | [_ (values 'set (cons #`(rhombus-expression #,elem) args))])) 27 | (when (and shape (not (eq? shape new-shape))) 28 | (raise-syntax-error #f 29 | (if (eq? shape 'set) 30 | "set element after map element" 31 | "map element after set element") 32 | stx 33 | elem)) 34 | (values new-shape new-args))) 35 | (wrap-static-info* 36 | (quasisyntax/loc stx 37 | (#,(if (eq? shape 'set) #'Set #'Map) 38 | #,@(reverse rev-args))) 39 | (if (eq? shape 'set) 40 | set-static-info 41 | map-static-info))])) 42 | -------------------------------------------------------------------------------- /rhombus/private/static-info-pack.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require syntax/parse 3 | "tail.rkt") 4 | 5 | (provide unpack-static-infos 6 | pack-static-infos) 7 | 8 | (define (unpack-static-infos v) 9 | (pack-tail 10 | (syntax-parse v 11 | [((key val) ...) 12 | #'((parens (group key) (group val)) ...)]))) 13 | 14 | (define (pack-static-infos v who) 15 | (datum->syntax 16 | #f 17 | (map (lambda (p) 18 | (syntax-parse p 19 | #:datum-literals (group parens) 20 | [(parens (group key) (group val)) 21 | #'(key val)])) 22 | (syntax->list (unpack-tail v who))))) 23 | -------------------------------------------------------------------------------- /rhombus/private/static-info-syntax.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse 4 | enforest/name-parse 5 | "tail.rkt" 6 | "static-info-pack.rkt") 7 | "definition.rkt" 8 | "name-root.rkt" 9 | "quasiquote.rkt" 10 | "static-info.rkt" 11 | "parse.rkt" 12 | "wrap-expression.rkt" 13 | (for-syntax "name-root.rkt") 14 | (for-syntax "parse.rkt")) 15 | 16 | (provide static_info 17 | (for-syntax static_info_ct)) 18 | 19 | (define-simple-name-root static_info 20 | macro) 21 | 22 | (begin-for-syntax 23 | (define-simple-name-root static_info_ct 24 | pack 25 | unpack 26 | wrap)) 27 | 28 | (define-syntax macro 29 | (definition-transformer 30 | (lambda (stx) 31 | (syntax-parse stx 32 | #:datum-literals (op block) 33 | #:literals (|'|) 34 | [(_ (op |'|) name::name ((~and body-tag block) body ...)) 35 | #`((define-syntax #,(in-static-info-space #'name.name) 36 | (convert-static-info 'name.name (rhombus-body-at body-tag body ...))))])))) 37 | 38 | (define-for-syntax (convert-static-info who stx) 39 | (unless (syntax? stx) 40 | (raise-result-error who "syntax?" stx)) 41 | (static-info (syntax->list (pack stx)))) 42 | 43 | (define-for-syntax (pack v) 44 | (pack-static-infos v 'static_info_ct.pack)) 45 | 46 | (define-for-syntax (unpack v) 47 | (unpack-static-infos v)) 48 | 49 | (define-for-syntax (wrap form info) 50 | #`(parsed #,(wrap-static-info* (wrap-expression form) 51 | (pack info)))) 52 | -------------------------------------------------------------------------------- /rhombus/private/string.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require racket/symbol 3 | racket/keyword 4 | "define-operator.rkt" 5 | (prefix-in rhombus: "print.rkt")) 6 | 7 | (provide &) 8 | 9 | (define-infix & append-as-strings 10 | #:stronger-than (===)) 11 | 12 | (define (append-as-strings a b) 13 | (string-append (to-string a) 14 | (to-string b))) 15 | 16 | (define (to-string a) 17 | (cond 18 | [(string? a) a] 19 | [(symbol? a) (symbol->immutable-string a)] 20 | [(keyword? a) (keyword->immutable-string a)] 21 | [else 22 | (define o (open-output-string)) 23 | (rhombus:display a o) 24 | (get-output-string o)])) 25 | -------------------------------------------------------------------------------- /rhombus/private/symbol.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse) 4 | "expression.rkt" 5 | "binding.rkt" 6 | "expression+binding.rkt" 7 | "literal.rkt") 8 | 9 | (provide symbol) 10 | 11 | (define-syntax symbol 12 | (make-expression+binding-prefix-operator 13 | #'symbol 14 | '((default . stronger)) 15 | 'macro 16 | (lambda (stx) 17 | (syntax-parse stx 18 | #:datum-literals (parens group) 19 | [(_ (parens (group id:identifier)) . tail) 20 | (values (syntax/loc stx (quote id)) 21 | #'tail)])) 22 | (lambda (stx) 23 | (syntax-parse stx 24 | #:datum-literals (parens group) 25 | [(_ (parens (group id:identifier)) . tail) 26 | (values (binding-form #'literal-infoer 27 | #'id) 28 | #'tail)])))) 29 | -------------------------------------------------------------------------------- /rhombus/private/syntax-class-mixin.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base) 3 | syntax/parse) 4 | 5 | (provide define-syntax-class-mixin 6 | define-composed-splicing-syntax-class) 7 | 8 | (define-syntax (define-syntax-class-mixin stx) 9 | (syntax-case stx (~alt) 10 | [(_ mixin-id #:datum-literals (lit-id ...) 11 | (~alt alt ...) 12 | . attrs) 13 | #'(define-syntax mixin-id 14 | (quote-syntax ((lit-id ...) 15 | (alt ...) 16 | attrs)))] 17 | [(_ mixin-id . more) 18 | #'(define-syntax-class-mixin #:datum-literals () . more)])) 19 | 20 | (define-syntax (define-composed-splicing-syntax-class stx) 21 | (syntax-case stx () 22 | [(_ id mixin-id ...) 23 | (with-syntax ([(((literal-id ...) (alt ...) (attr ...)) ...) 24 | (map syntax-local-value (syntax->list #'(mixin-id ...)))]) 25 | (with-syntax ([(unique-literal-id ...) 26 | (hash-values (for/hash ([literal-id (in-list (syntax->list #'(literal-id ... ...)))]) 27 | (values (syntax-e literal-id) literal-id)))]) 28 | #`(define-splicing-syntax-class id 29 | #:datum-literals (unique-literal-id ...) 30 | (pattern (~seq (~alt alt ... ...) (... ...)) 31 | attr ... ...))))])) 32 | -------------------------------------------------------------------------------- /rhombus/private/syntax-class.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse 4 | "introducer.rkt") 5 | syntax/parse 6 | "operator-parse.rkt" 7 | "name-root.rkt" 8 | "definition.rkt") 9 | 10 | (provide $: 11 | Term 12 | Id 13 | Op 14 | Id_Op 15 | Keyw 16 | Group 17 | Block) 18 | 19 | (module+ for-macro 20 | (provide syntax)) 21 | 22 | (module+ for-quasiquote 23 | (provide (for-syntax in-syntax-class-space 24 | rhombus-syntax-class? 25 | rhombus-syntax-class-kind 26 | rhombus-syntax-class-class))) 27 | 28 | (begin-for-syntax 29 | (define in-syntax-class-space (make-interned-syntax-introducer/add 'rhombus/syntax-class)) 30 | 31 | (struct rhombus-syntax-class (kind class))) 32 | 33 | (define-syntax $: "only in patterns") 34 | (define-syntax Term (rhombus-syntax-class 'term #f)) 35 | (define-syntax Id (rhombus-syntax-class 'term #'identifier)) 36 | (define-syntax Op (rhombus-syntax-class 'term #':operator)) 37 | (define-syntax Id_Op (rhombus-syntax-class 'term #':operator-or-identifier)) 38 | (define-syntax Keyw (rhombus-syntax-class 'term #'keyword)) 39 | (define-syntax Group (rhombus-syntax-class 'group #f)) 40 | (define-syntax Block (rhombus-syntax-class 'block #f)) 41 | 42 | (define-simple-name-root syntax 43 | class) 44 | 45 | (define-syntax class 46 | (definition-transformer 47 | (lambda (stx) 48 | (raise-syntax-error 'syntax.class "not supported, yet" stx)))) 49 | -------------------------------------------------------------------------------- /rhombus/private/syntax-error.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse)) 4 | 5 | (provide (for-syntax raise_syntax_error)) 6 | 7 | (begin-for-syntax 8 | (define raise_syntax_error 9 | (case-lambda 10 | [(form) (raise-syntax-error (name-of form) "bad syntax" (unwrap form))] 11 | [(msg form) (raise-syntax-error (name-of form) msg (unwrap form))] 12 | [(msg form detail) (raise-syntax-error (name-of form) msg (unwrap form) (unwrap detail))])) 13 | 14 | (define (name-of stx) 15 | (syntax-parse stx 16 | #:datum-literals (parens) 17 | [(parens (group who:identifier . _) . _) (syntax-e #'who)] 18 | [else #f])) 19 | 20 | (define (unwrap stx) 21 | (syntax-parse stx 22 | #:datum-literals (parens) 23 | [(parens d ...) #'(d ...)] 24 | [else stx]))) 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /rhombus/private/syntax-list.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide pack-list* 4 | unpack-list* 5 | pack-group* 6 | unpack-group* 7 | unpack-single-term-group 8 | pack-block* 9 | unpack-block*) 10 | 11 | (define (pack-list* stx depth) 12 | (cond 13 | [(eqv? depth 0) stx] 14 | [else (for/list ([t (in-list (syntax->list stx))]) 15 | (pack-list* t (sub1 depth)))])) 16 | 17 | (define (unpack-list* qs r depth) 18 | (datum->syntax 19 | qs 20 | (let unpack-list* ([r r] [depth depth]) 21 | (cond 22 | [(eqv? depth 0) 23 | (when (or (null? r) (pair? r)) 24 | (raise-arguments-error '|$| "cannot coerce list to syntax" 25 | "list" r)) 26 | (cond 27 | [(and qs (group-syntax? r)) 28 | (define l (syntax->list r)) 29 | (if (= 2 (length l)) 30 | (cadr l) 31 | (raise-arguments-error '|$| "multi-term group syntax not allowed in term context" 32 | "group syntax" r))] 33 | [else r])] 34 | [else 35 | (if (list? r) 36 | (datum->syntax 37 | qs 38 | (for/list ([r (in-list r)]) 39 | (unpack-list* r (sub1 depth)))) 40 | (raise-argument-error '... "list?" r))])))) 41 | 42 | (define (unpack-single-term-group r) 43 | (cond 44 | [(group-syntax? r) 45 | (define l (syntax->list r)) 46 | (if (= 2 (length l)) 47 | (cadr l) 48 | r)] 49 | [else r])) 50 | 51 | (define (pack-group* stx depth) 52 | (pack-list* stx depth)) 53 | 54 | (define (unpack-group* qs r depth) 55 | (datum->syntax 56 | qs 57 | (let unpack-group* ([r r] [depth depth]) 58 | (cond 59 | [(eqv? depth 0) 60 | (if (group-syntax? r) 61 | r 62 | (list 'group r))] 63 | [else 64 | (if (list? r) 65 | (datum->syntax 66 | qs 67 | (for/list ([r (in-list r)]) 68 | (unpack-group* r (sub1 depth)))) 69 | (raise-argument-error '... "list?" r))])))) 70 | 71 | (define (group-syntax? r) 72 | (and (syntax? r) 73 | (pair? (syntax-e r)) 74 | (eq? 'group (syntax-e (car (syntax-e r)))))) 75 | 76 | (define (pack-block* stx depth) 77 | (pack-list* stx depth)) 78 | 79 | (define (unpack-block* qs r depth) 80 | (datum->syntax 81 | qs 82 | (let unpack-block* ([r r] [depth depth]) 83 | (cond 84 | [(eqv? depth 0) 85 | (cond 86 | [(block-syntax? r) r] 87 | [(group-syntax? r) (list 'block r)] 88 | [else (list 'block (list 'group r))])] 89 | [else 90 | (if (list? r) 91 | (datum->syntax 92 | qs 93 | (for/list ([r (in-list r)]) 94 | (unpack-block* r (sub1 depth)))) 95 | (raise-argument-error '... "list?" r))])))) 96 | 97 | (define (block-syntax? r) 98 | (and (syntax? r) 99 | (pair? (syntax-e r)) 100 | (eq? 'block (syntax-e (car (syntax-e r)))))) 101 | -------------------------------------------------------------------------------- /rhombus/private/syntax-meta-value.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse)) 4 | 5 | (provide (for-syntax syntax_meta_value)) 6 | 7 | (begin-for-syntax 8 | (define syntax_meta_value 9 | (case-lambda 10 | [(id/op) 11 | (define id (extract-operator id/op)) 12 | (unless id 13 | (raise-argument-error 'syntax_meta_value "identifier-or-operator?" id/op)) 14 | (syntax-local-value id)] 15 | [(id/op fail) 16 | (define id (extract-operator id/op)) 17 | (unless id 18 | (raise-argument-error 'syntax_meta_value "identifier-or-operator??" id/op)) 19 | (syntax-local-value id (if (and (procedure? fail) 20 | (procedure-arity-includes? fail 0)) 21 | fail 22 | (lambda () fail)))])) 23 | 24 | (define (extract-operator v) 25 | (cond 26 | [(identifier? v) v] 27 | [(syntax? v) 28 | (syntax-parse v 29 | #:datum-literals (op) 30 | [(op id) #'id] 31 | [_ #f])] 32 | [else #f]))) 33 | -------------------------------------------------------------------------------- /rhombus/private/tail.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require syntax/parse 3 | syntax/stx 4 | enforest/proc-name) 5 | 6 | (provide pack-tail 7 | unpack-tail) 8 | 9 | (define parens-blank (syntax-property (datum->syntax #f 'parens) 'raw "")) 10 | (define group-blank (syntax-property (datum->syntax #f 'group) 'raw "")) 11 | 12 | ;; assumes that `tail` is a syntax list, and wraps 13 | ;; it as a parenthesized group; an empty list turns into 14 | ;; parentheses with no groups 15 | (define (pack-tail tail #:after [after #f]) 16 | (if (stx-null? tail) 17 | (cond 18 | [(and after 19 | (syntax-position after) 20 | (syntax-span after)) 21 | (define loc (srcloc (syntax-source after) 22 | (syntax-line after) 23 | (let ([col (syntax-column after)]) 24 | (and col (+ col (syntax-span after)))) 25 | (+ (syntax-position after) (syntax-span after)) 26 | 0)) 27 | #`(#,(syntax-property (syntax/loc loc parens) 'raw ""))] 28 | [else #`(#,parens-blank)]) 29 | #`(#,parens-blank (#,group-blank . #,tail)))) 30 | 31 | ;; assumes that `packed-tail` represents a shrubbery, 32 | ;; and unpacks be removing outer parentheses 33 | (define (unpack-tail packed-tail proc) 34 | (syntax-parse packed-tail 35 | [((~datum parens) ((~datum group) . tail)) #'tail] 36 | [((~datum parens)) #'()] 37 | [else 38 | (raise-result-error (if (symbol? proc) proc (proc-name proc)) 39 | "rhombus-syntax-list?" 40 | packed-tail)])) 41 | -------------------------------------------------------------------------------- /rhombus/private/underscore.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse) 4 | "expression.rkt" 5 | "binding.rkt" 6 | "expression+binding.rkt") 7 | 8 | (provide (rename-out [rhombus-_ _])) 9 | 10 | (define-syntax rhombus-_ 11 | (make-expression+binding-prefix-operator 12 | #'_ 13 | '((default . stronger)) 14 | 'macro 15 | ;; expression 16 | (lambda (stx) 17 | (syntax-parse stx 18 | [(form-id . tail) 19 | (raise-syntax-error #f 20 | (string-append "not allowed as an expression;\n" 21 | " only allowed as binding pattern or 'else' substitute") 22 | #'form-id)])) 23 | ;; binding 24 | (lambda (stx) 25 | (syntax-parse stx 26 | [(form-id . tail) 27 | (values (binding-form #'ignored-info 28 | #'#f) 29 | #'tail)])))) 30 | 31 | (define-syntax (ignored-info stx) 32 | (syntax-parse stx 33 | [(_ static-infos _) 34 | (binding-info #'ignored 35 | #'static-infos 36 | #'() 37 | #'always-succeed 38 | #'nothing-bind 39 | #'())])) 40 | 41 | (define-syntax (always-succeed stx) 42 | (syntax-parse stx 43 | [(_ _ _ IF success fail) 44 | #'(IF #t success fail)])) 45 | 46 | (define-syntax (nothing-bind stx) 47 | (syntax-parse stx 48 | [(_ _ _) #'(begin)])) 49 | -------------------------------------------------------------------------------- /rhombus/private/values.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse) 4 | "binding.rkt") 5 | 6 | (provide values 7 | (for-space rhombus/binding values)) 8 | 9 | (define-binding-syntax values 10 | (binding-prefix-operator 11 | #'values 12 | '((default . stronger)) 13 | 'macro 14 | (lambda (stx) 15 | (syntax-parse stx 16 | [(head . _) 17 | (raise-syntax-error #f 18 | (string-append "not allowed as a pattern (except as a non-nested" 19 | " pattern by forms that specifically recognize it") 20 | #'head)])))) 21 | -------------------------------------------------------------------------------- /rhombus/private/with-syntax.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require syntax/parse) 3 | 4 | (provide with-syntax-parse) 5 | 6 | (define-syntax with-syntax-parse 7 | (syntax-rules () 8 | [(_ () body ...) 9 | (let () body ...)] 10 | [(_ ([pat rhs] . more) . bodys) 11 | (syntax-parse rhs 12 | [pat (with-syntax-parse more . bodys)])])) 13 | -------------------------------------------------------------------------------- /rhombus/private/wrap-expression.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse) 4 | "parse.rkt") 5 | 6 | (provide (for-syntax wrap-expression)) 7 | 8 | (define-for-syntax (wrap-expression form) 9 | (syntax-parse form 10 | #:datum-literals (parsed) 11 | [(parsed e) #'e] 12 | [_ #`(rhombus-expression (group #,form))])) 13 | -------------------------------------------------------------------------------- /rhombus/runtime-config.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require racket/runtime-config 4 | racket/port 5 | racket/interaction-info 6 | shrubbery/parse 7 | shrubbery/print 8 | "private/set.rkt" 9 | (prefix-in rhombus: "private/print.rkt") 10 | (submod "private/print.rkt" redirect)) 11 | 12 | (current-interaction-info '#((submod rhombus reader) 13 | get-interaction-info 14 | #f)) 15 | 16 | (current-read-interaction 17 | (lambda (src in) 18 | (when (terminal-port? in) 19 | (flush-output (current-output-port))) 20 | (define (ensure-count in) 21 | (if (port-counts-lines? in) 22 | in 23 | (let ([in (dup-input-port in)]) 24 | (port-count-lines! in) 25 | in))) 26 | (parse-all (ensure-count in) #:source src #:interactive? #t))) 27 | 28 | (print-boolean-long-form #t) 29 | 30 | (define orig-print (global-port-print-handler)) 31 | 32 | (global-port-print-handler 33 | (lambda (v op [mode 0]) 34 | (if (racket-print-redirect? v) 35 | (orig-print (racket-print-redirect-val v) op 1) 36 | (rhombus:print v op)))) 37 | 38 | (error-syntax->string-handler 39 | (lambda (s len) 40 | (shrubbery-syntax->string s #:max-length len))) 41 | -------------------------------------------------------------------------------- /rhombus/scribblings/annotation-vs-bind.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: 3 | "util.rhm" open 4 | "common.rhm" open) 5 | 6 | @title[~tag: "annotation-vs-bind"]{Annotations versus Binding Patterns} 7 | 8 | Annotations and binding patterns serve similar and interacting purposes. 9 | The @rhombus[-:] and @rhombus[::] binding operators put annotations to 10 | work in a binding. For the other direction, the @rhombus[matching] 11 | annotation operator puts a binding form to work in a annotation. 12 | 13 | For example, suppose you want a annotation @rhombus[PersonList], which 14 | is a list of maps, and each map must at least relate @rhombus["name"] to 15 | a @rhombus[String] and @rhombus["location"] to a @rhombus[Posn]. The 16 | @rhombus[Map.of] annotation combination cannot express a per-key 17 | specialization, but the @rhombus[Map] binding pattern can. 18 | 19 | @(rhombusblock: 20 | annotation.macro 'PersonList: 21 | '(List.of(matching(Map(~name: (_ :: String), 22 | ~location: (_ :: Posn))))) 23 | 24 | val players :: PersonList: 25 | [Map(~name: "alice", ~location: Posn(1, 2)), 26 | Map(~name: "bob", ~location: Posn(3, 4))] 27 | ) 28 | 29 | As another example, here’s how a @rhombus[ListOf] annotation constructor 30 | could be implemented if @rhombus[List.of] did not exists already: 31 | 32 | @(rhombusblock: 33 | annotation.macro '(ListOf ($annotation ...) $tail ......): 34 | values('(matching([_ :: ($annotation ...), $(' ...)])), 35 | tail) 36 | ) 37 | 38 | At a lower level, the bridge between binding patterns and annotations is 39 | based on their shared use of @seclink["static-info"]{static information} 40 | as described in the @seclink["bind-macro-protocol"]{binding API} and the 41 | @seclink["annotation-macro"]{annotation API}. 42 | -------------------------------------------------------------------------------- /rhombus/scribblings/annotation.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: 3 | "util.rhm" open 4 | "common.rhm" open) 5 | 6 | @title[~tag: "annotation"]{Annotations and the Dot Operator} 7 | 8 | Besides classes defined with @rhombus[class], a few predefined 9 | annotations work with the @rhombus[-:, ~bind] and @rhombus[::, ~bind] 10 | annotation operators, including @rhombus[Integer, ~ann] (meaning exact 11 | integer), @rhombus[Number, ~ann], @rhombus[String, ~ann], 12 | @rhombus[Keyword, ~ann], and @rhombus[Any, ~ann] (meaning any value). 13 | 14 | The @rhombus[-:] and @rhombus[::] operators also work in expression 15 | positions. In that case, the assertion or check is about the expression 16 | on the left-hand side of @rhombus[-:] or @rhombus[::]. For @rhombus[::], 17 | the left-hand expression must produce a value that satisfies the 18 | right-hand annotation, otherwise a run-time exception is raised. The 19 | @rhombus[is_a] operator takes an annotation like @rhombus[::], but it 20 | produces a boolean result indicating whether the result of the left-hand 21 | expression matches the annotation. 22 | 23 | @(rhombusblock: 24 | (flip(origin) -: Posn).x // prints 0 25 | // (1 :: Posn) // would be a run-time error 26 | 27 | origin is_a Posn // prints #true 28 | 1 is_a Posn // prints #false 29 | ) 30 | 31 | When @rhombus[class] defines a new class, an annotation can be 32 | associated with each field. When the annotation is written with 33 | @rhombus[::], then the annotation is checked when an instance is 34 | created. 35 | 36 | @(rhombusblock: 37 | class Posn(x :: Integer, y :: Integer) 38 | 39 | Posn(1, 2) // prints Posn(1, 2) 40 | // Posn(1, "2") // would be a run-time error 41 | ) 42 | 43 | Naturally, class annotations can be used as field annotations, and then 44 | the @rhombus[.] operator can be chained for efficient access: 45 | 46 | @(rhombusblock: 47 | class Line(p1 -: Posn, p2 -: Posn) 48 | 49 | def l1 :: Line: 50 | Line(Posn(1, 2), Posn(3, 4)) 51 | 52 | l1.p2.x // prints 3 53 | ) 54 | 55 | More generally, @rhombus[.] access is efficient when the left-hand side 56 | of @rhombus[.] is an expression that can act as a @deftech{dot 57 | provider}. A class name is a dot provider, and it provides access to 58 | field-accessor functions, as in @rhombus[Posn.x] (which doesn’t get a 59 | specific @rhombus[x], but produces a function that can be called on a 60 | @rhombus[Posn] instance to extract its @rhombus[x] field). An identifier 61 | that is bound using @rhombus[-:] or @rhombus[::] and a class name is 62 | also a dot provider, and it provides access to fields of a class 63 | instance. More generally, an annotation that is associated to a binding 64 | or expression with @rhombus[-:] or @rhombus[::] might make the binding 65 | or expression a dot provider. See @secref["static-info"] for more 66 | information on dot providers and other static information. 67 | 68 | The @rhombus[use_static_dot] definition form binds the @rhombus[.] 69 | operator so that it works only in efficient mode with a dot provider. If 70 | the left-hand side of the @rhombus[.] is not a dot provider, then the 71 | @rhombus[.] defined by @rhombus[use_static_dot] reports a compile-time 72 | error. The @rhombus[use_dynamic_dot] form binds @rhombus[.] to the 73 | default @rhombus[.], which allows dynamic field lookup if the left-hand 74 | side is not a dot provider. 75 | 76 | @(rhombusblock: 77 | use_static_dot 78 | 79 | l1.p2.x // prints 3 80 | // 1.x // disallowed statically 81 | ) 82 | 83 | @aside{Using @rhombus[.] to reach an imported binding, as in 84 | @rhombus[f2c.fahrenheit_to_celsius], is a different kind of @rhombus[.] 85 | than the infix expression operator.} 86 | -------------------------------------------------------------------------------- /rhombus/scribblings/bind-and-static.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | 3 | @title[~style: symbol(toc)]{Binding Macros and Static Information} 4 | 5 | @local_table_of_contents[] 6 | 7 | @include_section["static-info.scrbl"] 8 | @include_section["bind-macro-protocol.scrbl"] 9 | @include_section["annotation-macro.scrbl"] 10 | -------------------------------------------------------------------------------- /rhombus/scribblings/bind-macro.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: 3 | "util.rhm" open 4 | "common.rhm" open) 5 | 6 | @title[~tag: "bind-macro"]{Binding and Annotation Macros} 7 | 8 | Macros can extend binding-position syntax, too, via @rhombus[bind.rule] and 9 | @rhombus[bind.macro]. In the simplest case, a binding operator is implemented 10 | by expanding to other binding operators, like this definition of @rhombus[$] 11 | as a prefix operator to constrain a pattern to number inputs: 12 | 13 | @(rhombusblock: 14 | import: 15 | rhombus/macro: no_prefix 16 | 17 | bind.rule '($ $n): 18 | ~parsed_right 19 | '($n :: Number) 20 | 21 | val $salary: 100.0 22 | 23 | salary // prints 100.0 24 | ) 25 | 26 | More expressive binding operators can use a lower-level protocol where a 27 | binding is represented by transformers that generate checking and 28 | binding code. It gets complicated, and it’s tied up with the propagation 29 | of static information, so the details are in @secref["bind-macro-protocol"]. 30 | After an expressive set of binding forms are implemented with the 31 | low-level interface, however, many others can be implemented though 32 | simple expansion. 33 | 34 | The @rhombus[annotation.macro] form is similar to @rhombus[bind.macro], but for 35 | annotations. 36 | 37 | @(rhombusblock: 38 | use_static_dot 39 | 40 | annotation.macro 'PosnList: 'List.of(Posn) 41 | 42 | fun nth_x(ps -: PosnList, n): 43 | ps[n].x 44 | ) 45 | 46 | For details on the low-level annotation protocol, see @secref["annotation-macro"]. 47 | 48 | -------------------------------------------------------------------------------- /rhombus/scribblings/common.rhm: -------------------------------------------------------------------------------- 1 | #lang rhombus 2 | 3 | import: 4 | rhombus/macro open 5 | for_label: 6 | rhombus open 7 | rhombus/macro open 8 | 9 | export: 10 | all_from(rhombus/macro) 11 | all_from(rhombus): 12 | for_label 13 | all_from(rhombus/macro): 14 | for_label 15 | -------------------------------------------------------------------------------- /rhombus/scribblings/defn-macro.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: 3 | "util.rhm" open 4 | "common.rhm" open) 5 | 6 | @title[~tag: "defn-macro"]{Definition and Declaration Macros} 7 | 8 | The @rhombus[defn.macro] form defines a definition macro. It is similar 9 | to @rhombus[expr.macro] in prefix form, except that the name must be an 10 | identifier (never an operator), and the result syntax object should 11 | represent a block, which is spliced into the definition context where 12 | the macro is used. 13 | 14 | Here’s the classic @rhombus[def_five] macro: 15 | 16 | 17 | @(rhombusblock: 18 | import: 19 | rhombus/macro: no_prefix 20 | 21 | defn.macro '(def_five $id): 22 | '(: 23 | def $id: 5 24 | ) 25 | 26 | def_five v 27 | v // prints 5 28 | ) 29 | 30 | Declarations macros are written with @rhombus[decl.macro], and the 31 | block produced by expansion can use forms like @rhombus[import] and 32 | @rhombus[export]. 33 | 34 | By distinguishing between expression macros, definition macros, and 35 | declaration macros, Rhombus can report errors for out-of-place uses 36 | earlier and more clearly than Racket. 37 | -------------------------------------------------------------------------------- /rhombus/scribblings/function.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: 3 | "util.rhm" open 4 | "common.rhm" open) 5 | 6 | @title{Function Expressions} 7 | 8 | The @rhombus[fun] form works in an expression position as λ. Just like 9 | @tt{function} in JavaScript, the expression variant omits a function 10 | name. 11 | 12 | @(rhombusblock: 13 | val curried_add: fun (x): 14 | fun (y): 15 | x+y 16 | 17 | curried_add(10)(20) // prints 30 18 | ) 19 | 20 | Naturally, keyword and optional arguments (as described in the 21 | @seclink["keyword-arg"]{next section}) work with @rhombus[fun] 22 | expressions, too. 23 | -------------------------------------------------------------------------------- /rhombus/scribblings/keyword-argument.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: 3 | "util.rhm" open 4 | "common.rhm" open) 5 | 6 | @title[~tag: "keyword-arg"]{Keyword and Optional Arguments} 7 | 8 | A function argument can be made optional by using @rhombus[=] after the 9 | argument’s pattern and providing a default-value expression after 10 | @rhombus[=]: 11 | 12 | @(rhombusblock: 13 | fun scale(Posn(x, y), factor = 1): 14 | Posn(factor * x, factor * y) 15 | 16 | scale(Posn(1, 2)) // prints Posn(1, 2) 17 | scale(Posn(1, 2), 3) // prints Posn(3, 6) 18 | ) 19 | 20 | By-keyword arguments are often useful for functions that have multiple 21 | optional arguments. A keyword argument is indicated by prefixing a 22 | formal or actual argument with a shrubbery keyword, which is written 23 | with a leading @litchar{~}, and then starting a block with @rhombus[:]. 24 | 25 | @(rhombusblock: 26 | fun transform(Posn(x, y), 27 | ~scale: factor = 1, 28 | ~dx: dx = 0, 29 | ~dy: dy = 0): 30 | Posn(factor*x + dx, factor*y + dy) 31 | 32 | transform(Posn(1, 2)) // prints Posn(1, 2) 33 | transform(Posn(1, 2), ~dx: 7) // prints Posn(8, 2) 34 | transform(Posn(1, 2), ~dx: 7, ~scale: 2) // prints Posn(9, 4) 35 | ) 36 | 37 | Since a keyword by itself is not allowed as an expression or pattern, 38 | there is no possibility that a keyword will be inadvertently treated as 39 | an actual argument or binding pattern by itself. The @rhombus[keyword] 40 | form turns a keyword into an expression that produces the keyword, as in 41 | @rhombus[keyword(~scale)]. The @rhombus[symbol] form similarly turns an 42 | identifier into a symbol, as in @rhombus[symbol(x)]. 43 | 44 | @aside{The keyword prefix and @rhombus[=] for default values are not 45 | binding operators. They are specific to the syntax of @rhombus[fun].} 46 | 47 | If an argument name is the same as its keyword (just without the 48 | @litchar{~}), then the @rhombus[:] argument name can be omitted. That 49 | only works for an argument that would otherwise be just an identifier 50 | and maybe a default value, because keywords don’t work as variable names 51 | in binding patterns. 52 | 53 | @(rhombusblock: 54 | fun transform(Posn(x, y), 55 | ~scale: factor = 1, 56 | ~dx = 0, 57 | ~dy = 0): 58 | Posn(factor*x + dx, factor*y + dy) 59 | ) 60 | 61 | -------------------------------------------------------------------------------- /rhombus/scribblings/macro.rhm: -------------------------------------------------------------------------------- 1 | #lang rhombus 2 | 3 | import: 4 | scribble/rhombus/manual: 5 | expose: examples 6 | close_eval 7 | 8 | export: 9 | make_macro_eval 10 | close_eval 11 | 12 | fun make_macro_eval(): 13 | val macro_eval: manual.make_rhombus_eval() 14 | @examples[ 15 | ~eval: macro_eval, 16 | ~hidden: #true, 17 | import: rhombus/macro open 18 | ] 19 | macro_eval 20 | -------------------------------------------------------------------------------- /rhombus/scribblings/multiple-value.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: 3 | "util.rhm" open 4 | "common.rhm" open) 5 | 6 | @title[~tag: "multiple-values"]{Multiple Values} 7 | 8 | The @rhombus[values] form returns multiple values: 9 | 10 | @(rhombusblock: 11 | values(1, "apple") // prints 1 and "apple" 12 | ) 13 | 14 | When an expression in a module body returns multiple values, each one is 15 | printed, the same as in @litchar{#lang racket}. 16 | 17 | When the @rhombus[val] binding form is followed by parentheses with @math{N} 18 | groups, then the right-hand side should produce @math{N} values, and each 19 | value is matched against the corresponding group. 20 | 21 | @(rhombusblock: 22 | val (n, s): values(1, "apple") 23 | 24 | n // prints 1 25 | s // prints "apple" 26 | ) 27 | 28 | A definition binding with with @rhombus[val] or @rhombus[def] can also 29 | use @rhombus[values] in the outermost pattern, and that’s the same as 30 | not writing @rhombus[values], but makes the receiver and sender side 31 | look more the same: 32 | 33 | @(rhombusblock: 34 | def values(n, s): values(1, "apple") 35 | 36 | n // prints 1 37 | s // prints "apple" 38 | ) 39 | 40 | As in Racket, multiple values are not a tuple value. They must be 41 | specifically received as values. The @rhombus[values] binding pattern 42 | works only with definition forms that recognize it, and not, for 43 | example, as a function argument. 44 | -------------------------------------------------------------------------------- /rhombus/scribblings/mutable-var.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: 3 | "util.rhm" open 4 | "common.rhm" open) 5 | 6 | @title[~tag: "mutable-vars"]{Mutable Variables} 7 | 8 | Variables are immutable unless they are declared with the 9 | @rhombus[mutable] binding operator. The @rhombus[:=] infix operator 10 | assigns to a mutable variable while also returning the variable’s new 11 | value. 12 | 13 | @(rhombusblock: 14 | def mutable todays_weather: "sunny" 15 | 16 | todays_weather // prints "sunny" 17 | todays_weather := "rainy" // prints "rainy" 18 | todays_weather // prints "rainy" 19 | 20 | def f(mutable x): 21 | x := x + 8 22 | x 23 | 24 | f(10) // prints 18 25 | 26 | // f = 5 // would be an error: f is not mutable 27 | ) 28 | 29 | @aside{The @rhombus[:=] operator should also cooperate with @rhombus[.] 30 | when a class field is declared @rhombus[mutable], but that’s not yet 31 | implemented.} 32 | -------------------------------------------------------------------------------- /rhombus/scribblings/overview.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | 3 | @title[~style: symbol(toc)]{Rhombus Overview} 4 | 5 | @local_table_of_contents[] 6 | 7 | @include_section["notation.scrbl"] 8 | @include_section["module.scrbl"] 9 | @include_section["definition.scrbl"] 10 | @include_section["annotation.scrbl"] 11 | @include_section["function.scrbl"] 12 | @include_section["keyword-argument.scrbl"] 13 | @include_section["conditional.scrbl"] 14 | @include_section["multiple-value.scrbl"] 15 | @include_section["mutable-var.scrbl"] 16 | @include_section["operator.scrbl"] 17 | @include_section["list.scrbl"] 18 | @include_section["map.scrbl"] 19 | @include_section["set.scrbl"] 20 | @include_section["syntax.scrbl"] 21 | @include_section["expr-macro.scrbl"] 22 | @include_section["defn-macro.scrbl"] 23 | @include_section["bind-macro.scrbl"] 24 | @include_section["annotation-vs-bind.scrbl"] 25 | -------------------------------------------------------------------------------- /rhombus/scribblings/ref-annotation.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm" open) 3 | 4 | @title{Annotations} 5 | 6 | @doc[ 7 | operator (expr :: annotation) :: annotation 8 | ]{ 9 | 10 | Checks that the value of @rhombus[expr] satisifies 11 | @rhombus[annotation], and returns the value if so. 12 | 13 | @examples[ 14 | [1, 2, 3] :: List 15 | ] 16 | 17 | } 18 | 19 | @doc[ 20 | bind.rule '($binding :: $annotation) 21 | ]{ 22 | 23 | Binds the same as @rhombus[binding], but first checks that the value to 24 | be bound satisfies @rhombus[annotation]. 25 | 26 | @examples[ 27 | val x :: List: [1, 2, 3] 28 | ] 29 | 30 | } 31 | 32 | @doc[ 33 | annotation.macro 'Any 34 | ]{ 35 | 36 | Matches any value. 37 | 38 | } 39 | 40 | @doc[ 41 | operator (arg -: annotation) :: annotation 42 | ]{ 43 | 44 | Associates static information to the overall expression the same as 45 | @rhombus[::], but performs no run-time check on the value of 46 | @rhombus[expr]. 47 | 48 | @examples[ 49 | [1, 2, 3] -: List, 50 | "oops" -: List 51 | ] 52 | 53 | } 54 | 55 | @doc[ 56 | bind.rule '($binding -: $annotation) 57 | ]{ 58 | 59 | Associates static information to @rhombus[binding] the same as 60 | @rhombus[::, ~bind], but performs no run-time check. 61 | 62 | @examples[ 63 | val x -: List: [1, 2, 3], 64 | val x -: List: "oops" 65 | ] 66 | 67 | } 68 | 69 | 70 | @doc[ 71 | expr.rule '($expr is_a $annotation) 72 | ]{ 73 | 74 | Produces @rhombus[#true] if the value of @rhombus[expr] 75 | satisfies @rhombus[annotation], @rhombus[#false] otherwise. 76 | 77 | @examples[ 78 | [1, 2, 3] is_a List, 79 | "oops" is_a List 80 | ] 81 | 82 | } 83 | -------------------------------------------------------------------------------- /rhombus/scribblings/ref-array.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm" open) 3 | 4 | @title{Arrays} 5 | 6 | An array works with map-referencing square brackets to access a list 7 | element by position (in constant time), and it works with square 8 | brackets in combination with the assignment operator @rhombus[:=] to 9 | update the array. 10 | 11 | @doc[ 12 | fun Array(v :: Any, ...) :: Array 13 | ]{ 14 | 15 | Constructs a mutable array containing given arguments. 16 | 17 | @examples[ 18 | val a: Array(1, 2, 3), 19 | a, 20 | a[0], 21 | a[0] := 0, 22 | a 23 | ] 24 | 25 | } 26 | 27 | @doc[ 28 | bind.macro '(Array($binding, ...)) 29 | ]{ 30 | 31 | Matches an array with as many elements as @rhombus[binding]s, where 32 | each element matches its corresponding @rhombus[binding]. 33 | 34 | @examples[ 35 | val Array(1, x, y): Array(1, 2, 3), 36 | y 37 | ] 38 | 39 | } 40 | 41 | @doc[ 42 | annotation.macro 'Array, 43 | annotation.macro '(Array.of($annotation)), 44 | ]{ 45 | 46 | Matches any array in the form without @rhombus[of]. The @rhombus[of] 47 | variant matches an array whose elements satisfy @rhombus[annotation]. 48 | 49 | } 50 | -------------------------------------------------------------------------------- /rhombus/scribblings/ref-begin.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm" open) 3 | 4 | @title{Begin} 5 | 6 | @doc[ 7 | decl.macro '(begin: 8 | $body 9 | ...) 10 | ]{ 11 | 12 | Returns the result of the @rhombus[body] block, which may include local 13 | definitions. 14 | 15 | @examples[ 16 | begin: 17 | 1 18 | 2, 19 | begin: 20 | val one: 1 21 | one + one 22 | ] 23 | 24 | } 25 | -------------------------------------------------------------------------------- /rhombus/scribblings/ref-boolean.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm" open) 3 | 4 | @title{Booleans} 5 | 6 | @doc[ 7 | annotation.macro 'Boolean 8 | ]{ 9 | 10 | Matches @rhombus[#true] or @rhombus[#false] 11 | 12 | } 13 | 14 | @doc[ 15 | expr.macro '($expr || $expr), 16 | ]{ 17 | 18 | Produces the value of the first @rhombus[expr] if it is 19 | non-@rhombus[#false], otherwise produces the value(s) of the second 20 | @rhombus[expr]. 21 | 22 | The second @rhombus[expr] is evaluated in tail position with respect to 23 | the @rhombus[||] form. 24 | 25 | } 26 | 27 | @doc[ 28 | expr.macro '($expr && $expr), 29 | ]{ 30 | 31 | Produces @rhombus[#false] if the the value of the first @rhombus[expr] 32 | is @rhombus[#false], otherwise produces the value(s) of the second 33 | @rhombus[expr]. 34 | 35 | The second @rhombus[expr] is evaluated in tail position with respect to 36 | the @rhombus[&&] form. 37 | 38 | } 39 | 40 | @doc[ 41 | operator (! v):: Boolean 42 | ]{ 43 | 44 | Returns @rhombus[#true] if @rhombus[v] is @rhombus[#false], 45 | @rhombus[#false] otherwise. 46 | 47 | 48 | @examples[ 49 | !#false, 50 | !#true, 51 | !"false" 52 | ] 53 | 54 | } 55 | -------------------------------------------------------------------------------- /rhombus/scribblings/ref-class.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm" open) 3 | 4 | @title{Classes} 5 | 6 | @doc[ 7 | ~literal: ::, 8 | defn.macro '(class $identifier($field, ...)), 9 | 10 | grammar field: 11 | $identifier 12 | $identifier :: $annotation 13 | ]{ 14 | 15 | Binds @rhombus[identifier] as a class name, which serves several roles: 16 | 17 | @itemlist[ 18 | 19 | @item{a constructor function, which takes as many arguments as the 20 | supplied @rhombus[field]s and returns an instance of the class;}, 21 | 22 | @item{an annotation, which is satisfied by any instance of the class;}, 23 | 24 | @item{a pattern constructor, which takes as many patterns as the 25 | supplied @rhombus[field]s and matches an instance of the class where the 26 | fields match the corresponding patterns;}, 27 | 28 | @item{a dot povider to access accessor functions @rhombus[identifier.field]}, 29 | 30 | @item{an annotation constructor @rhombus[identifier.of], which takes as 31 | many annotation arguments as supplied @rhombus[field]s.} 32 | 33 | ] 34 | 35 | } 36 | -------------------------------------------------------------------------------- /rhombus/scribblings/ref-cond.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm" open) 3 | 4 | @title{Conditionals} 5 | 6 | @doc[ 7 | decl.macro '(if $test_expr 8 | | $then_body 9 | ... 10 | | $else_body 11 | ...) 12 | ]{ 13 | 14 | If @rhombus[test_expr] produces a true value (which is value other than 15 | @rhombus[#false]), returns the result of the @rhombus[then_body] clause, 16 | otherwise returns the result of the @rhombus[else_body] clause. 17 | 18 | @examples[ 19 | if #true 20 | | "yes" 21 | | "no", 22 | if 1+2 == 3 23 | | val yep: "yes" 24 | yep 25 | | "no" 26 | ] 27 | 28 | } 29 | 30 | @doc[ 31 | decl.macro '(cond 32 | | $clause_test_expr: 33 | $clause_result_body 34 | ... 35 | | ...), 36 | decl.macro '(cond 37 | | $clause_test_expr: 38 | $clause_result_body 39 | ... 40 | | ... 41 | | ~else: 42 | $clause_result_body 43 | ...) 44 | ]{ 45 | 46 | Tries the @rhombus[clause_test_expr]s in sequence, and as soon as one 47 | produces a non-@rhombus[#false] value, returns the result of the 48 | corresponding @rhombus[clause_result_body] block. The keyword 49 | @rhombus[~else] can be used as a synonym for @rhombus[#true] in the last 50 | clause. 51 | 52 | If no @rhombus[clause_test_expr] produces a true value and there is no 53 | @rhombus[~else] clause, a run-time exception is raised. 54 | 55 | } 56 | -------------------------------------------------------------------------------- /rhombus/scribblings/ref-def.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm" open) 3 | 4 | @title{Definitions} 5 | 6 | @doc[ 7 | defn.macro '(val $binding: 8 | $body 9 | ...) 10 | ]{ 11 | 12 | Binds the identifiers of @rhombus[binding] to the value of the 13 | @rhombus[body] sequence. The @rhombus[body] itself can include 14 | definitions, and its normally it ends with an expression to provide the 15 | result value. 16 | 17 | A @rhombus[binding] can be just an identifier, or it can be constructed 18 | with a binding operator, such as a pattern form or @rhombus[::] for 19 | annotations. 20 | 21 | @examples[ 22 | val pi: 3.14, 23 | pi 24 | ] 25 | 26 | @examples[ 27 | ~label: #false, 28 | val pi: 29 | val tau: 6.28 30 | tau/2, 31 | pi 32 | ] 33 | 34 | @examples[ 35 | ~label: #false, 36 | val [x, y, z]: [1+2, 3+4, 5+6], 37 | y 38 | ] 39 | 40 | @examples[ 41 | ~label: #false, 42 | val ns :: List: [1+2, 3+4, 5+6], 43 | ns 44 | ] 45 | 46 | } 47 | 48 | 49 | @doc[ 50 | defn.macro '(let $binding: 51 | $body 52 | ...) 53 | ]{ 54 | 55 | Like @rhombus[val], but for bindings that become visible only after the 56 | @rhombus[let] form within its definition context. The @rhombus[let] form 57 | cannot be used in a top-level context outside of a module or local block. 58 | 59 | @examples[ 60 | begin: 61 | let v: 1 62 | fun get_v(): v 63 | let v: v+1 64 | [get_v(), v] 65 | ] 66 | 67 | } 68 | 69 | 70 | 71 | @doc[ 72 | defn.macro '(def $binding: 73 | $body 74 | ...), 75 | defn.macro '(def $identifier ($kwopt_binding, ...) $maybe_result_annotation: 76 | $body 77 | ...), 78 | defn.macro '(def 79 | | $identifier ($binding, ...) $maybe_result_annotation: 80 | $body 81 | ... 82 | | ...), 83 | defn.macro '(def ' $expr_pattern: 84 | $body 85 | ...), 86 | grammar expr_pattern: 87 | $identifier 88 | ($identifier_or_operator $pattern ...) 89 | ($ $identifier $identifier_or_operator $pattern ...), 90 | grammar identifier_or_operator: 91 | $identifier 92 | $operator 93 | ]{ 94 | 95 | Like @rhombus[val], @rhombus[def], or @rhombus[expr.rule], depending on 96 | the form. The @rhombus[fun]-like form matches only if 97 | @rhombus[identifier] is not bound as a pattern operator. 98 | 99 | } 100 | 101 | 102 | -------------------------------------------------------------------------------- /rhombus/scribblings/ref-defn-macro.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: 3 | "common.rhm" open 4 | "macro.rhm" 5 | for_label: 6 | rhombus/macro: 7 | only: expr_ct 8 | for_meta -1) 9 | 10 | @(val macro_eval: macro.make_macro_eval()) 11 | 12 | @title{Definition Macros} 13 | 14 | @doc[ 15 | defn.macro '(defn.macro '($identifier_or_operator $pattern ...): 16 | body 17 | ...), 18 | grammar identifier_or_operator: 19 | $identifier 20 | $operator, 21 | ]{ 22 | 23 | Defines @rhombus[identifier] or @rhombus[operator] as a macro that can 24 | be used in a definition context, where the compile-time @rhombus[body] 25 | block returns the expansion result. The macro pattern is matched to an 26 | entire group in a definition context. 27 | 28 | The expansion result must be a parenthesized block, and the groups of 29 | the block are inlined in place of the definition-macro use. 30 | 31 | @examples[ 32 | ~eval: macro_eval, 33 | defn.macro '(enum: 34 | $ids 35 | ...): 36 | // temporary list library: 37 | fun | length([]): 0 38 | | length([a, as, ...]): 1+length(as) 39 | fun | iota_accum(0, acc): acc 40 | | iota_accum(n, acc): iota_accum(n-1, cons(n, acc)) 41 | fun iota(n): iota_accum(n, []) 42 | // useful example part is here: 43 | val ns: iota(length(ids)) 44 | '(: 45 | val $ids: $ns 46 | ...), 47 | enum: 48 | a 49 | b 50 | c, 51 | b 52 | ] 53 | 54 | } 55 | 56 | @doc[ 57 | defn.macro '(defn.sequence_macro 58 | '(: $identifier_or_operator $pattern ... 59 | $pattern 60 | ...): 61 | body 62 | ...), 63 | grammar identifier_or_operator: 64 | $identifier 65 | $operator, 66 | ]{ 67 | 68 | Similar to @rhombus[defn.macro], but defines a macro for a definition 69 | context that is matched against all of the remiaining groups in the 70 | context, so the pattern is a block pattern. 71 | 72 | The macro result is two values: a parenthesized block of groups to 73 | splice in place of the sequence-macro use, and a parenthesized block of 74 | groups that represent the tail of the definition context that was not consumed. 75 | 76 | @examples[ 77 | ~eval: macro_eval, 78 | defn.sequence_macro '(: reverse_defns 79 | $defn1 80 | $defn2 81 | $tail 82 | ......): 83 | values('(: $defn2; $defn1), '(: $tail; ......)), 84 | : 85 | reverse_defns 86 | def seq_x: seq_y+1 87 | def seq_y: 10 88 | seq_x 89 | ] 90 | } 91 | 92 | @«macro.close_eval»[macro_eval] 93 | -------------------------------------------------------------------------------- /rhombus/scribblings/ref-dot.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm" open) 3 | 4 | @title{Dot} 5 | 6 | @doc[ 7 | expr.macro '($target . $identifier) 8 | ]{ 9 | 10 | Accesses a component of @rhombus[target], either statically or 11 | dyanamically. The access is static when @rhombus[target] is a @tech{dot 12 | provider}. 13 | 14 | } 15 | 16 | 17 | @doc[ 18 | defn.macro 'use_static_dot 19 | ]{ 20 | 21 | (Re-)defines @rhombus[.] so that it accesses a component of a target 22 | only when the access can be resolved statically. 23 | 24 | } 25 | -------------------------------------------------------------------------------- /rhombus/scribblings/ref-equal.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm" open) 3 | 4 | @title{Equality} 5 | 6 | @doc[ 7 | operator ((v1 :: Any) == (v1 :: Any)) :: Boolean 8 | ]{ 9 | 10 | Reports whether @rhombus[v1] and @rhombus[v2] are equal, which includes 11 | recursively comparing elements of compound data structures. Two numbers 12 | are @rhombus[==] only if they are both exact or both inexact. 13 | 14 | @examples[ 15 | "apple" == "apple", 16 | [1, 2, 3] == 1, 17 | [1, "apple", {"alice": 97}] == [1, "apple", {"alice": 97}], 18 | 1 == 1.0 19 | ] 20 | 21 | } 22 | 23 | @doc[ 24 | operator ((v1 :: Any) === (v1 :: Any)) :: Boolean 25 | ]{ 26 | 27 | Reports whether @rhombus[v1] and @rhombus[v2] are the same object. 28 | Being the @emph{same} is weakly defined, but only @rhombus[==] values 29 | can possibly be the same object, and mutable values are the same only if 30 | modifying one has the same effect as modifying the other. Interned 31 | values like symbols are @rhombus[===] when they are @rhombus[==]. 32 | 33 | @examples[ 34 | symbol(apple) === symbol(apple), 35 | symbol(apple) === symbol(banana), 36 | ] 37 | 38 | } 39 | 40 | @doc[ 41 | operator ((x :: Number) .= (y :: Number)) :: Boolean 42 | ]{ 43 | 44 | Reports whether @rhombus[x] and @rhombus[y] are numerically equal, 45 | where inexact numbers are effectively coerced to exact for comparisions 46 | to exact numbers. 47 | 48 | @examples[ 49 | 1 .= 1, 50 | 1 .= 2, 51 | 1.0 .= 1 52 | ] 53 | 54 | } 55 | 56 | 57 | @doc[ 58 | expr.macro '(=) 59 | ]{ 60 | 61 | The @rhombus[=] operator is not bound as an expression or binding 62 | operator. It is used as a syntactic delimiter by various forms, such as 63 | in @rhombus[fun] when specifying the default value for an optional 64 | argument. 65 | 66 | } 67 | -------------------------------------------------------------------------------- /rhombus/scribblings/ref-export.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm" open) 3 | 4 | @title{Export} 5 | 6 | @doc[ 7 | decl.macro '(export: 8 | $export_item ... : 9 | $export_modifier 10 | ... 11 | ...), 12 | 13 | grammar export_item: 14 | $identifier_or_operator 15 | all_from($module_path) 16 | all_in($identifier) 17 | names: $identifier_or_operator ..., 18 | 19 | grammar identifier_or_operator: 20 | $identifier 21 | $operator, 22 | ]{ 23 | 24 | Exports from the enclosing module. 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /rhombus/scribblings/ref-expr-macro.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: 3 | "common.rhm" open 4 | "macro.rhm" 5 | for_label: 6 | rhombus/macro: 7 | only: expr_ct 8 | for_meta -1) 9 | 10 | @(val macro_eval: macro.make_macro_eval()) 11 | 12 | @title{Expression Macros} 13 | 14 | @doc[ 15 | defn.macro '(expr.rule $rule_pattern: 16 | ' $template), 17 | defn.macro '(expr.rule 18 | | $rule_pattern: 19 | ' $template 20 | | ...), 21 | grammar rule_pattern: 22 | ' $identifier 23 | '($identifier_or_operator $pattern ...) 24 | '($ $term_pattern $identifier_or_operator $pattern ...), 25 | grammar identifier_or_operator: 26 | $identifier 27 | $operator, 28 | grammar term_pattern: 29 | $term_identifier 30 | ($term_identifier $: $syntax_class) 31 | ]{ 32 | 33 | Defines @rhombus[identifier] or @rhombus[operator] as a pattern-based 34 | macro whose expansion is described by a @rhombus[template] that can 35 | refer to pattern variables bound in the @rhombus[rule_pattern]. A 36 | @rhombus[rule_pattern] is matched to a portion of its enclosing group, 37 | and need not extend to the end of the group to match. 38 | 39 | Each @rhombus[rule_pattern] starts with @rhombus['] followed by either 40 | an identifier or by parentheses. An identifier by itself is defined as a 41 | ``nofix'' macro, which is the same as a prefix operator that consumes no 42 | arguments. With parentheses after @rhombus['], either the first term is 43 | an identifier or operator to be defined as a prefix macro, or a single 44 | @rhombus[$, ~bind] escape is followed by the identifier or operator to 45 | be defined as an infix macro. 46 | 47 | Using @litchar{|} alternatives, a single definition can have any number 48 | of prefix and infix variants (presumably) distinguished by patterns that 49 | are tried in order. 50 | 51 | The body after each @rhombus[rule_pattern] must be an immediate 52 | @rhombus['] template, and any @rhombus[$] escape within the template can 53 | only refer to an input pattern variable. More general compile-time 54 | expressions are not allowed, and @rhombus[expr.macro] must be used 55 | instead. 56 | 57 | @examples[ 58 | ~eval: macro_eval, 59 | expr.rule '(thunk: $body): 60 | '(fun (): $body), 61 | thunk: "ok", 62 | (thunk: "ok")() 63 | ] 64 | 65 | } 66 | 67 | 68 | @doc[ 69 | defn.macro '(expr.macro $rule_pattern: 70 | $body 71 | ...), 72 | defn.macro '(expr.macro 73 | | $rule_pattern: 74 | $body 75 | ... 76 | | ...), 77 | ]{ 78 | 79 | Like @rhombus[expr.rule], but 80 | 81 | @itemlist[ 82 | 83 | @item{with arbitrary compile-time code in the body after each 84 | @rhombus[rule_pattern],}, 85 | 86 | @item{each @rhombus[rule_pattern] is matched to the entire remainder 87 | of a group where the macro is used; and}, 88 | 89 | @item{a body returns value values: an expansion for the consumed part 90 | of the input group, and a tail for the unconsumed part.} 91 | 92 | ] 93 | 94 | } 95 | 96 | @doc[ 97 | val expr_ct.call_result_key 98 | ]{ 99 | 100 | Provided @rhombus[for_meta, ~impmod], a value that can be used to 101 | associate static information with an expression that describes the 102 | result value if the expression is used as a function to call. 103 | 104 | } 105 | 106 | 107 | @«macro.close_eval»[macro_eval] 108 | -------------------------------------------------------------------------------- /rhombus/scribblings/ref-io.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm" open) 3 | 4 | @title{Input and Output} 5 | 6 | @doc[ 7 | fun print(v, out = current_output_port()) :: Void 8 | ]{ 9 | 10 | Prints @rhombus[v] to @rhombus[out]. 11 | 12 | } 13 | 14 | @doc[ 15 | fun println(v, out = current_output_port()) :: Void 16 | ]{ 17 | 18 | Prints @rhombus[v] to @rhombus[out], then prints a newline. 19 | 20 | } 21 | 22 | @doc[ 23 | fun display(v, out = current_output_port()) :: Void 24 | ]{ 25 | 26 | Displays @rhombus[v] to @rhombus[out]. 27 | 28 | Strings, symbols, and keywords display as their character content. A 29 | byte string displays as its raw byte content. Any other kind of value 30 | displays the same way that it prints. 31 | 32 | } 33 | 34 | @doc[ 35 | fun displayln(v, out = current_output_port()) :: Void 36 | ]{ 37 | 38 | Displays @rhombus[v] to @rhombus[out] like @rhombus[display], then 39 | prints a newline. 40 | 41 | } 42 | 43 | @doc[ 44 | fun current_output_port(), 45 | fun current_output_port(out), 46 | ]{ 47 | 48 | A parameter for the current output port. 49 | 50 | } -------------------------------------------------------------------------------- /rhombus/scribblings/ref-list.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm" open) 3 | 4 | @(val dots: @rhombus[..., ~bind]) 5 | 6 | @title{Lists} 7 | 8 | Lists can be constructed using the syntax 9 | @rhombus[[$$(@rhombus[expr, ~var]), ...]], which creates list containing 10 | the values of the @rhombus[expr, ~var]s as elements. 11 | 12 | A list works with map-referencing square brackets to access a list 13 | element by position (in time proportional to the position), and it works 14 | with the @rhombus[++] operator to append lists. 15 | 16 | @doc[ 17 | fun List(v :: Any, ...) :: List 18 | ]{ 19 | 20 | Constructs a list of the given arguments, equivalent to using 21 | @rhombus[[v, ...]]. 22 | 23 | @examples[ 24 | val lst: List(1, 2, 3), 25 | lst, 26 | lst[0], 27 | lst ++ [4, 5] 28 | ] 29 | 30 | } 31 | 32 | @doc[ 33 | bind.macro '(List($binding, ...)), 34 | bind.macro '(List($binding, ..., $dots)), 35 | grammar dots: 36 | $$(dots) 37 | ]{ 38 | 39 | Matches a list with as many elements as @rhombus[binding]s, or if 40 | @rhombus[dots] is included, at least as many elements as 41 | @rhombus[binding]s before the last one, and then them last one is 42 | matched against the rest of the list. 43 | 44 | @examples[ 45 | val List(1, x, y): [1, 2, 3], 46 | y, 47 | val List(1, xs, ...): [1, 2, 3], 48 | xs 49 | ] 50 | 51 | } 52 | 53 | @doc[ 54 | annotation.macro 'List, 55 | annotation.macro '(List.of($annotation)), 56 | ]{ 57 | 58 | Matches any list in the form without @rhombus[of]. The @rhombus[of] 59 | variant matches a list whose elements satisfy @rhombus[annotation]. 60 | 61 | } 62 | 63 | @doc[ 64 | bind.macro '(...) 65 | ]{ 66 | 67 | Used within binding patterns such as @rhombus[List, ~bind] or 68 | @rhombus[', ~bind] to indicate repetation. 69 | } 70 | 71 | -------------------------------------------------------------------------------- /rhombus/scribblings/ref-macro.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm" open) 3 | 4 | @title[~style: symbol(toc)]{Macros} 5 | 6 | @docmodule[rhombus/macro] 7 | 8 | Simple pattern-based expression macros can be written using 9 | @rhombus[def] without imorting any additional libraries besides 10 | @rhombusmodname[rhombus], but implementing others forms of macros requires 11 | using the @rhombusmodname[rhombus/macro] module (usually with no prefix). 12 | 13 | The @rhombusmodname[rhombus/macro] module provides bindings like 14 | @rhombus[expr.macro], and it also re-exports all of 15 | @rhombusmodname[rhombus] @rhombus[for_meta] for use in compile-time 16 | expressions. 17 | 18 | @local_table_of_contents[] 19 | 20 | @include_section["ref-expr-macro.scrbl"] 21 | @include_section["ref-defn-macro.scrbl"] 22 | -------------------------------------------------------------------------------- /rhombus/scribblings/ref-map.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm" open) 3 | 4 | @title{Maps} 5 | 6 | Immutable maps can be constructed using the syntax 7 | @rhombus[{$$(@rhombus[key_expr, ~var]): $$(@rhombus[value_expr, ~var]), ...}], 8 | which creates a map from the values of the @rhombus[key_expr, ~var]s to 9 | the corresponding values of the @rhombus[value_expr, ~var]s. Note 10 | that using @litchar{,} in place of @litchar{:} creates a set with 11 | separate values, instead of a key--value mapping. 12 | 13 | To access a mapping, use square brackets after a map expression with an 14 | expression for the key within square brackets. Mutable maps can be 15 | updated with a combination of square brackets and the @rhombus[:=] 16 | operator. 17 | 18 | @doc[ 19 | fun Map(key :: Any, value:: Any, ...) :: Map 20 | ]{ 21 | 22 | Constructs an immutable map containing given keys mapped to the given 23 | values, equivalent to using @rhombus[{key: value, ...}]. The number of 24 | arguments to @rhombus[Map] must be even with keys and values 25 | interleaved. 26 | 27 | @examples[ 28 | val m: Map("x", 1, "y", 2), 29 | m, 30 | m["x"] 31 | ] 32 | 33 | } 34 | 35 | @doc[ 36 | bind.macro '(Map($key_expr, $binding, ...)) 37 | ]{ 38 | 39 | Matches a map the keys computed by @rhombus[key_expr] to values that 40 | match the corresponding @rhombus[binding]s. 41 | 42 | @examples[ 43 | val Map("x", x, "y", y): {"x": 1, "y": 2}, 44 | y 45 | ] 46 | 47 | } 48 | 49 | @doc[ 50 | annotation.macro 'Map, 51 | annotation.macro '(Map.of($key_annotation, $value_annotation)), 52 | ]{ 53 | 54 | Matches any map in the form without @rhombus[of]. The @rhombus[of] 55 | variant matches a map whose keys satisfy @rhombus[key_annotation] 56 | and whose values satisfy @rhombus[value_annotation]. 57 | 58 | } 59 | 60 | 61 | @doc[ 62 | fun make_map(key :: Any, value:: Any, ...) :: Map 63 | ]{ 64 | 65 | Similar to @rhombus[Map] as a constructor, but creates a mutable map 66 | that can be updated using @rhombus[=]. 67 | 68 | @examples[ 69 | val m: make_map("x", 1, "y", 2), 70 | m, 71 | m["x"], 72 | m["x"] := 0, 73 | m 74 | ] 75 | 76 | } 77 | 78 | 79 | @doc[ 80 | operator ((v1 :: Map) ++ (v2 :: Map)) :: Map, 81 | operator ((v1 :: Set) ++ (v2 :: Set)) :: Set, 82 | operator ((v1 :: List) ++ (v2 :: List)) :: List, 83 | ]{ 84 | 85 | Appends @rhombus[v1] and @rhombus[v2] to create a new map, set, or 86 | list. In the case of maps, mappings for keys in @rhombus[v2] replace 87 | ones that exist already in @rhombus[v1]. In the case of sets, the new 88 | set has all of the elements of @rhombus[v1] and @rhombus[v2]. In the case 89 | of lists, the elements of @rhombus[v1] appear first in the result list 90 | followed by the elements of @rhombus[v2]. 91 | 92 | The combination 93 | @rhombus[$$(@rhombus(map_expr, ~var)) ++ {$$(@rhombus(key_expr, ~var)): $$(@rhombus(value_expr, ~var))}] 94 | is recognized by the compiler and turned into an efficient functional update of the 95 | map produced by @rhombus[map_expr], as opposed to creating an intermediate map. 96 | Set update is handled similarly. 97 | 98 | @examples[ 99 | val m: {"x": 1, "y": 2}, 100 | m ++ {"x": 0}, 101 | m, 102 | {1, 2, 3} ++ {"four", "five"}, 103 | [1, 2, 3] ++ [4, 5] 104 | ] 105 | 106 | } 107 | -------------------------------------------------------------------------------- /rhombus/scribblings/ref-match.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm" open) 3 | 4 | @title{Matching} 5 | 6 | @doc[ 7 | decl.macro '(match $target_expr 8 | | $binding: 9 | $result_body 10 | ... 11 | | ...), 12 | decl.macro '(match $target_expr 13 | | $binding: 14 | $result_body 15 | ... 16 | | ... 17 | | ~else: 18 | $result_body 19 | ...) 20 | ]{ 21 | 22 | Tries matching theresult of @rhombus[target_expr] against each 23 | @rhombus[binding] in sequence, and as soon as one matches, returns the 24 | result of the corresponding @rhombus[result_body] block. The keyword 25 | @rhombus[~else] can be used as a synonym for @rhombus[_, ~bind] (which matches 26 | any value without binding any identifiers) in the last clause. 27 | 28 | Typically, a @rhombus[binding] imposes requires on a value and binds 29 | some number of identifiers as a result of a successful match. For 30 | example, a literal number works as a @rhombus[binding] pattern, but it 31 | binds zero identifiers. An identifier as a @rhombus[binding] pattern 32 | matches any value and binds the identifier the the matching value. A 33 | list form is a @rhombus[binding] pattern with subpatterns as its 34 | elements, and it matches a list with the right number of elements that 35 | match match the corresponding pattern. The set of @rhombus[binding] 36 | forms is extensible, so it cannot be completely enumerated here. 37 | 38 | If no @rhombus[target_expr] produces a true value and there is no 39 | @rhombus[~else] clause, a run-time exception is raised. 40 | 41 | @examples[ 42 | match 1+2 43 | | 3: "three" 44 | | ~else: "not three", 45 | match [1+2, 3+4] 46 | | [x, y]: x+y 47 | ] 48 | 49 | } 50 | 51 | @doc[ 52 | bind.macro '_ 53 | ]{ 54 | 55 | Matches any value without binding any identifiers. 56 | 57 | @examples[ 58 | match 1+2 59 | | 0: "zero" 60 | | _: "nonzero" 61 | ] 62 | } 63 | -------------------------------------------------------------------------------- /rhombus/scribblings/ref-mutable.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm" open) 3 | 4 | @title{Mutable Variables and Assignment} 5 | 6 | @doc[ 7 | bind.macro '(mutable $identifier) 8 | ]{ 9 | 10 | Binds @rhombus[identifier] so that its vaue can be changed using 11 | @rhombus[:=]. 12 | 13 | No static information is associated with @rhombus[identifier], even if 14 | a surrounding binding pattern would otherwise associate static 15 | information with it. 16 | 17 | } 18 | 19 | @doc[ 20 | expr.macro '($identifier := $expr) 21 | ]{ 22 | 23 | Changes the value of @rhombus[identifier] to the result of 24 | @rhombus[expr]. The @rhombus[identifier] must be bound with 25 | @rhombus[mutable, ~bind]. 26 | 27 | @examples[ 28 | val mutable count: 0, 29 | count := count + 1, 30 | count 31 | ] 32 | 33 | } 34 | -------------------------------------------------------------------------------- /rhombus/scribblings/ref-number.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm" open) 3 | 4 | @title{Numbers} 5 | 6 | @doc[ 7 | annotation.macro 'Number 8 | ]{ 9 | 10 | Matches any number. 11 | 12 | } 13 | 14 | @doc[ 15 | annotation.macro 'Integer 16 | ]{ 17 | 18 | Matches exact integers. 19 | 20 | } 21 | 22 | @doc[ 23 | operator ((x :: Number) + (y :: Number)) :: Number, 24 | operator ((x :: Number) - (y :: Number)) :: Number, 25 | operator ((x :: Number) * (y :: Number)) :: Number, 26 | operator ((x :: Number) / (y :: Number)) :: Number 27 | ]{ 28 | 29 | The usual arithmetic operators with the usual precedence, except that 30 | @rhombus[/] does not have the same precedence as @rhombus[*] when it 31 | appears to the right of @rhombus[*]. 32 | 33 | @examples[ 34 | 1+2, 35 | 3-4, 36 | 5*6, 37 | 8/2, 38 | 1+2*3 39 | ] 40 | 41 | } 42 | 43 | @doc[ 44 | operator ((x :: Number) > (y :: Number)) :: Boolean, 45 | operator ((x :: Number) >= (y :: Number)) :: Boolean, 46 | operator ((x :: Number) < (y :: Number)) :: Boolean, 47 | operator ((x :: Number) <= (y :: Number)) :: Boolean 48 | ]{ 49 | 50 | The usual comparsion operators. 51 | 52 | @examples[ 53 | 1 < 2, 54 | 3 >= 3.0 55 | ] 56 | 57 | } 58 | -------------------------------------------------------------------------------- /rhombus/scribblings/ref-set.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm" open) 3 | 4 | @title{Sets} 5 | 6 | Immutable sets can be constructed using the syntax 7 | @rhombus[{$$(@rhombus[val_expr, ~var]), ...}], 8 | which creates a set containing the values of the @rhombus[value_expr, ~var]s. 9 | 10 | To check for membership in a set, use square brackets after a map 11 | expression with an expression for a value, and the result is a boolean 12 | indicating whether the value is in the set. Mutable sets can be updated 13 | with a combination of square brackets and the @rhombus[:=] operator, where 14 | a @rhombus[#false] result on the right-hand side of @rhombus[:=] removes an 15 | element from a set, and any other right-hand side result causes the value 16 | to be inluded in the set. 17 | 18 | @doc[ 19 | fun Set(value:: Any, ...) :: Map 20 | ]{ 21 | 22 | Constructs an immutable set containing given values, equivalent to 23 | using @rhombus[{value, ...}]. 24 | 25 | @examples[ 26 | val s: Set("x", 1, "y", 2), 27 | s, 28 | s["x"], 29 | s[1], 30 | s[42] 31 | ] 32 | 33 | } 34 | 35 | @doc[ 36 | annotation.macro 'Set, 37 | annotation.macro '(Set.of($annotation)), 38 | ]{ 39 | 40 | Matches any set in the form without @rhombus[of]. The @rhombus[of] 41 | variant matches a set whose values satisfy @rhombus[annotation]. 42 | 43 | } 44 | 45 | 46 | @doc[ 47 | fun make_set(value:: Any, ...) :: Map 48 | ]{ 49 | 50 | Similar to @rhombus[Set] as a constructor, but creates a mutable set 51 | that can be updated using @rhombus[:=]. 52 | 53 | @examples[ 54 | val m: make_set("x", 1, "y", 2), 55 | m, 56 | m["x"], 57 | m["x"] := #false, 58 | m, 59 | m["x"] := #true, 60 | m 61 | ] 62 | 63 | } 64 | -------------------------------------------------------------------------------- /rhombus/scribblings/ref-string.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm" open) 3 | 4 | @title{Strings} 5 | 6 | @doc[ 7 | annotation.macro 'String 8 | ]{ 9 | 10 | Matches strings. 11 | 12 | } 13 | 14 | @doc[ 15 | operator (v1 & v2) :: String 16 | ]{ 17 | 18 | Coerces @rhombus[v1] and @rhombus[v2] to a string, then appends the strings. 19 | 20 | The string for of a value is the same way that @rhombus[display] would 21 | print it, which means that strings, symbols, and keywords print as their 22 | character content. 23 | 24 | @examples[ 25 | "hello" & "world", 26 | "it goes to " & 11, 27 | "the list " & [1, 2, 3] & " has " & 3 & " elements" 28 | ] 29 | 30 | } 31 | -------------------------------------------------------------------------------- /rhombus/scribblings/ref-stxobj.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm" open) 3 | 4 | @title{Syntax Objects} 5 | 6 | @doc[ 7 | expr.macro '(' $form) 8 | ]{ 9 | 10 | Creates a syntax object quoting @rhombus[form]. 11 | 12 | A @rhombus[$] within @rhombus[form] escapes to an expression whose 13 | value replaces the @rhombus[$] form. A @rhombus['] within @rhombus[form] 14 | increases the quoting depth so that a matching @rhombus[$] within the 15 | nested @rhombus['] form doesn't escape, but merely decreases the quoting 16 | depth. 17 | 18 | @examples[ 19 | '1, 20 | 'pi, 21 | '(1 + 2), 22 | '($(1 + 2)), 23 | '(' $(1 + $(4-1))), 24 | ] 25 | 26 | } 27 | 28 | 29 | @doc[ 30 | expr.macro '($ $expr) 31 | ]{ 32 | 33 | Only allowed within a @rhombus['] form, escapes so that the value of 34 | @rhombus[expr] is used in place of the @rhombus[$] form. 35 | 36 | } 37 | 38 | 39 | @doc[ 40 | bind.macro '(' $form) 41 | ]{ 42 | 43 | Matches a syntax object consistent with @rhombus[form]. 44 | 45 | A @rhombus[$, ~bind] within @rhombus[form] escapes to an binding that is 46 | matched against the corresponding portion of a candidate syntax object. 47 | Ellipses, etc. 48 | 49 | @examples[ 50 | match '(1 + 2) 51 | | '($n + $m): [n, m] 52 | ] 53 | 54 | } 55 | 56 | 57 | @doc[ 58 | bind.macro '($ $identifier), 59 | bind.macro '($ ($identifier $: $syntax_class)), 60 | ]{ 61 | 62 | Only allowed within a @rhombus[', ~bind] binding pattern, escapes so that 63 | @rhombus[identifier] is bound to the corresponding portion of the syntax 64 | object that matches the @rhombus[', ~bind] form. 65 | 66 | The @rhombus[syntax_class] can be @rhombus[Term, ~stxclass], @rhombus[Id, ~stxclass], 67 | or @rhombus[Group, ~stxclass], among others. 68 | 69 | } 70 | 71 | @doc[ 72 | expr.macro '($identifier $: $syntax_class) 73 | ]{ 74 | 75 | Only allowed with @rhombus[$] within a template, matches and binds 76 | @rhombus[identifier] only if the canditate syntax-object portion matches 77 | @rhombus[syntax_class]. 78 | 79 | } 80 | 81 | @doc[ 82 | syntax.class Term, 83 | syntax.class Id, 84 | syntax.class Op, 85 | syntax.class Id_Op, 86 | syntax.class Keyw, 87 | syntax.class Group, 88 | syntax.class Block, 89 | ]{ 90 | 91 | Syntax classes, all of which imply a term match except for 92 | @rhombus[Group, ~stxclass] and @rhombus[Block, ~stxclass]. 93 | 94 | The @rhombus[Group, ~stxclass] syntax class can be used only for a 95 | pattern identifier that is the sole term of its group in a pattern. The 96 | identifier is bound to a match for the entire group. 97 | 98 | The @rhombus[Block, ~stxclass] syntax class can be used only for a 99 | pattern identifier that is the sole term of a block. The identifier is 100 | bound to a match for the entire block. 101 | 102 | } 103 | 104 | 105 | @doc[ 106 | bind.macro '(......) 107 | ]{ 108 | 109 | Used within @rhombus[', ~bind] binding patterns to indicate tail 110 | repetition. 111 | 112 | } 113 | 114 | 115 | -------------------------------------------------------------------------------- /rhombus/scribblings/ref-values.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm"open) 3 | 4 | @title{Multiple Values} 5 | 6 | @doc[ 7 | fun values(v, ...) 8 | ]{ 9 | 10 | Returns the @rhombus[v]s as multiple result values. 11 | 12 | If only one @rhombus[v] is provided, the result is the same as just 13 | @rhombus[v]. Any other number of values must be received by a context 14 | that is expecting multiple values, such as with a 15 | @rhombus[values, ~bind] binding pattern. 16 | 17 | } 18 | 19 | @doc[ 20 | bind.macro '(values($binding, ...)) 21 | ]{ 22 | 23 | Matches multiple result values corresponding to the number of 24 | @rhombus[binding]s, where each result matches the corresponing 25 | @rhombus[binding]. 26 | 27 | } 28 | -------------------------------------------------------------------------------- /rhombus/scribblings/ref-void.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: "common.rhm" open) 3 | 4 | @title{Void} 5 | 6 | @doc[ 7 | annotation.macro 'Void 8 | ]{ 9 | 10 | Matches @rhombus[#void]. 11 | 12 | } 13 | -------------------------------------------------------------------------------- /rhombus/scribblings/reference.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | 3 | @title[~style: symbol(toc)]{Core Rhombus Reference} 4 | 5 | @docmodule[rhombus] 6 | 7 | @local_table_of_contents[] 8 | 9 | @include_section["ref-def.scrbl"] 10 | @include_section["ref-import.scrbl"] 11 | @include_section["ref-export.scrbl"] 12 | @include_section["ref-function.scrbl"] 13 | @include_section["ref-class.scrbl"] 14 | @include_section["ref-mutable.scrbl"] 15 | 16 | @include_section["ref-cond.scrbl"] 17 | @include_section["ref-match.scrbl"] 18 | @include_section["ref-begin.scrbl"] 19 | 20 | @include_section["ref-boolean.scrbl"] 21 | @include_section["ref-number.scrbl"] 22 | @include_section["ref-string.scrbl"] 23 | @include_section["ref-list.scrbl"] 24 | @include_section["ref-array.scrbl"] 25 | @include_section["ref-map.scrbl"] 26 | @include_section["ref-set.scrbl"] 27 | @include_section["ref-void.scrbl"] 28 | 29 | @include_section["ref-equal.scrbl"] 30 | @include_section["ref-dot.scrbl"] 31 | @include_section["ref-values.scrbl"] 32 | @include_section["ref-annotation.scrbl"] 33 | @include_section["ref-stxobj.scrbl"] 34 | @include_section["ref-macro.scrbl"] 35 | 36 | @include_section["ref-io.scrbl"] 37 | 38 | -------------------------------------------------------------------------------- /rhombus/scribblings/rhombus.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | 3 | @title{Rhombus Prototype} 4 | 5 | This is the experimental Rhombus prototype using 6 | @seclink[~doc: [symbol(lib), "shrubbery/scribblings/shrubbery.scrbl"], "top"]{Shrubbery notation}. 7 | 8 | @table_of_contents[] 9 | 10 | @include_section["overview.scrbl"] 11 | @include_section["bind-and-static.scrbl"] 12 | @include_section["reference.scrbl"] 13 | -------------------------------------------------------------------------------- /rhombus/scribblings/set.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: 3 | "util.rhm" open 4 | "common.rhm" open) 5 | 6 | @title[~tag: "set"]{Sets} 7 | 8 | When @litchar["{"]...@litchar["}"] is used with elements that do not 9 | have @rhombus[:], then @litchar["{"]...@rhombus["}"] creates a set. (If 10 | a set-element expression uses @rhombus[:], then it will need to be in 11 | parentheses to avoid being parsed as a key–value pair.) A set can serve 12 | as a map, where the set's elements act as keys and each key's value is 13 | @rhombus[#true]. There's a @rhombus[Set] constructor that's analogous to 14 | @rhombus[Map], but @rhombus[Set] accepts just values to include in the 15 | set. The @rhombus[++] operator effectively unions sets. 16 | 17 | @(rhombusblock: 18 | val friends: {"alice", "bob", "carol"} 19 | 20 | if friends["alice"] && friends["carol"] 21 | | "I know both" 22 | | "Who are they?" 23 | // prints "I know both" 24 | 25 | val new_friends: friends ++ {"david"} 26 | new_friends["david"] // prints #true 27 | friends["david"] // prints #false 28 | ) 29 | 30 | @rhombus[Set.of] and @rhombus[make_set] work as you'd expect. When 31 | @litchar{[}...@litchar{]} with @rhombus[:=] is used to modify a mutable 32 | set, the ``key'' is removed from the set if the assigned value is 33 | @rhombus[#false], otherwise the ``key'' is added to the set. 34 | -------------------------------------------------------------------------------- /rhombus/scribblings/util.rhm: -------------------------------------------------------------------------------- 1 | #lang rhombus 2 | 3 | import: 4 | scribble/rhombus as scribble 5 | 6 | export: 7 | aside 8 | 9 | def aside(content): 10 | scribble.nested(~style: symbol(inset), scribble.italic(content)) 11 | -------------------------------------------------------------------------------- /scribble/private/doc.rhm: -------------------------------------------------------------------------------- 1 | #lang rhombus 2 | 3 | import: 4 | rhombus/macro open 5 | "typeset-doc.rkt" open 6 | 7 | export: 8 | doc 9 | 10 | expr.macro '(doc $parens $tail ......): 11 | ~op_stx: me 12 | match parens 13 | | '($_ ..., ..., [$_ ..., ...]): 14 | values(parsed(['#{typeset-doc}, me, parens]), 15 | tail) 16 | | ~else: 17 | raise_syntax_error("expected forms and documentation content", '($me $parens)) 18 | -------------------------------------------------------------------------------- /scribble/private/docmodule.rhm: -------------------------------------------------------------------------------- 1 | #lang rhombus 2 | 3 | import: 4 | rhombus/macro open 5 | scribble/manual expose: 6 | defmodule racketmodname 7 | "rhombus.rhm" open 8 | for_meta: 9 | racket/base 10 | for_label: 11 | rhombus expose: 12 | import 13 | 14 | export: 15 | docmodule 16 | rhombusmodname 17 | 18 | for_meta: 19 | fun translate_mod(mod): 20 | match mod 21 | | '($(a $: Id)): a 22 | | '($a / $b ...): 23 | base.#{string->symbol}(unwrap_syntax(a) 24 | & "/" 25 | & unwrap_syntax(translate_mod('($b ...)))) 26 | | '(lib($str)): 27 | [symbol(lib), str] 28 | 29 | decl.macro '(docmodule ($mod ...)): 30 | '(: $(parsed(['defmodule, 31 | keyword(#{#:require-form}), 32 | unparsed('(fun (mod): @rhombus[import: $$(mod)])), 33 | translate_mod('($mod ...))]))) 34 | 35 | expr.macro '(rhombusmodname ($mod ...) $tail ......): 36 | values(parsed(['racketmodname, translate_mod('($mod ...))]), 37 | tail) 38 | -------------------------------------------------------------------------------- /scribble/private/example.rhm: -------------------------------------------------------------------------------- 1 | #lang rhombus 2 | 3 | import: 4 | rhombus/macro open 5 | "typeset-example.rkt" open 6 | 7 | export: 8 | examples 9 | make_rhombus_eval 10 | close_eval 11 | 12 | expr.macro '(examples $parens $tail ......): 13 | ~op_stx: me 14 | match parens 15 | | '($_, ...): 16 | values(parsed(['#{typeset-examples}, parens]), 17 | tail) 18 | | ~else: 19 | raise_syntax_error("expected forms", '($me $parens)) 20 | 21 | fun make_rhombus_eval(): 22 | #{make-rhombus-eval}() 23 | 24 | fun close_eval(e): 25 | #{close-eval}(e) 26 | -------------------------------------------------------------------------------- /scribble/private/include.rkt: -------------------------------------------------------------------------------- 1 | #lang rhombus 2 | import: 3 | rhombus/macro open 4 | scribble/base expose: #{include-section} 5 | 6 | export: 7 | include_section 8 | 9 | defn.macro '(include_section($(mod $: Term))): 10 | '(: 11 | $(parsed(['#{include-section}, mod]))) 12 | -------------------------------------------------------------------------------- /scribble/private/property.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require syntax/parse) 3 | 4 | (provide term-identifiers-syntax-property 5 | group-identifiers-syntax-property) 6 | 7 | (define (term-identifiers-syntax-property stx property val [preserved? #t]) 8 | (define (add-to-atom a) 9 | (if (procedure? property) 10 | (property a) 11 | (syntax-property a property val preserved?))) 12 | (define (add-to-groups gs) 13 | (for/list ([g (in-list (syntax->list gs))]) 14 | (add-to-group g))) 15 | (define (add-to-group g) 16 | (syntax-parse g 17 | #:datum-literals (group) 18 | [((~and tag group) t ...) 19 | (datum->syntax g (cons #'tag (add-to-terms #'(t ...))) g g)])) 20 | (define (add-to-terms ts) 21 | (for/list ([t (in-list (syntax->list ts))]) 22 | (add-to-term t))) 23 | (define (add-to-term stx) 24 | (syntax-parse stx 25 | #:datum-literals (parens brackets braces block alts op) 26 | [((~and tag (~or parens brackets braces block)) g ...) 27 | (datum->syntax stx (cons #'tag (add-to-groups #'(g ...))) stx stx)] 28 | [((~and tag alts) b ...) 29 | (datum->syntax stx (cons #'tag (add-to-terms #'(b ...))) stx stx)] 30 | [((~and tag parens) g ...) 31 | (datum->syntax stx (cons #'tag (add-to-groups #'(g ...))) stx stx)] 32 | [((~and tag parens) g ...) 33 | (datum->syntax stx (cons #'tag (add-to-groups #'(g ...))) stx stx)] 34 | [((~and tag op) id) 35 | (datum->syntax stx (list #'tag (add-to-atom #'id)) stx stx)] 36 | [_ (add-to-atom stx)])) 37 | (add-to-term stx)) 38 | 39 | (define (group-identifiers-syntax-property g-stx property val [preserved? #t]) 40 | (syntax-parse g-stx 41 | #:datum-literals (group) 42 | [((~and tag group) t ...) 43 | (datum->syntax g-stx 44 | (cons #'tag 45 | (for/list ([t (in-list (syntax->list #'(t ...)))]) 46 | (term-identifiers-syntax-property t property val preserved?))) 47 | g-stx 48 | g-stx)])) 49 | -------------------------------------------------------------------------------- /scribble/private/typeset-help.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | shrubbery/property 4 | racket/syntax-srcloc) 5 | (only-in rhombus/private/name-root-ref 6 | portal-syntax->lookup)) 7 | 8 | (provide (for-syntax 9 | resolve-name-ref 10 | append-consecutive-syntax-objects)) 11 | 12 | (define-for-syntax (append-consecutive-syntax-objects datum pre t) 13 | (define pre-loc (syntax-srcloc pre)) 14 | (define t-loc (syntax-srcloc pre)) 15 | (define t/s (if (and pre-loc 16 | t-loc 17 | (equal? (srcloc-source pre-loc) 18 | (srcloc-source t-loc)) 19 | (srcloc-position t-loc) 20 | (srcloc-span t-loc) 21 | (srcloc-position pre-loc) 22 | ((srcloc-position pre-loc) . < . (srcloc-position t-loc))) 23 | (datum->syntax t 24 | datum 25 | (struct-copy srcloc pre-loc 26 | [span (- (+ (srcloc-position t-loc) 27 | (srcloc-span t-loc)) 28 | (srcloc-position pre-loc))]) 29 | t 30 | t) 31 | t)) 32 | (syntax-raw-prefix-property t/s (syntax-raw-prefix-property pre))) 33 | 34 | (define-for-syntax (resolve-name-ref root field) 35 | (define p (identifier-binding-portal-syntax root #f)) 36 | (define lookup (and p (portal-syntax->lookup p values))) 37 | (define dest (and lookup (lookup #f "identifier" field))) 38 | (and dest 39 | (let ([raw (format "~a.~a" 40 | (syntax-e root) 41 | (syntax-e field))]) 42 | (define loc-stx 43 | (append-consecutive-syntax-objects 44 | 'loc-stx 45 | (append-consecutive-syntax-objects 'loc-stx root #'dot) 46 | field)) 47 | (syntax-raw-property (datum->syntax dest (syntax-e dest) loc-stx loc-stx) 48 | raw)))) 49 | -------------------------------------------------------------------------------- /scribble/private/typeset_meta.rhm: -------------------------------------------------------------------------------- 1 | #lang rhombus 2 | 3 | import: 4 | racket/base 5 | 6 | export: 7 | Transformer 8 | Spacer 9 | in_space 10 | 11 | class Transformer(proc) 12 | class Spacer(proc) 13 | 14 | fun in_space(stx): 15 | base.#{make-interned-syntax-introducer}(symbol(#{rhombus/scribble/typeset}))(stx) 16 | -------------------------------------------------------------------------------- /scribble/rhombus.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base 3 | syntax/parse) 4 | rhombus/private/parse 5 | rhombus/private/forwarding-sequence 6 | (prefix-in doc: scribble/doclang2) 7 | (rename-in scribble/base 8 | [verbatim base:verbatim] 9 | [table-of-contents table_of_contents] 10 | [local-table-of-contents local_table_of_contents]) 11 | scribble/private/manual-defaults 12 | "private/rhombus.rhm" 13 | "private/include.rkt") 14 | 15 | (provide (rename-out [module-begin #%module-begin]) 16 | (except-out (all-from-out scribble/base) 17 | base:verbatim 18 | include-section) 19 | verbatim 20 | pkg 21 | include_section 22 | (all-from-out "private/rhombus.rhm" 23 | "private/include.rkt")) 24 | (define-syntax-rule (rhombus-out) 25 | (begin 26 | (require (except-in rhombus #%module-begin)) 27 | (provide (all-from-out rhombus)))) 28 | (rhombus-out) 29 | 30 | (module reader syntax/module-reader 31 | #:language 'scribble/rhombus 32 | #:read read-proc 33 | #:read-syntax read-syntax-proc 34 | #:info get-info-proc 35 | #:whole-body-readers? #t 36 | (require shrubbery/parse 37 | (only-in (submod shrubbery reader) 38 | [get-info-proc shrubbery:get-info-proc])) 39 | (provide read-proc 40 | read-syntax-proc 41 | get-info-proc) 42 | (define (read-proc in) 43 | (list (syntax->datum (parse-all in #:test-mode? #t)))) 44 | (define (read-syntax-proc src in) 45 | (list (parse-all in #:text-mode? #t #:source src))) 46 | (define (get-info-proc key default make-default) 47 | (case key 48 | [(color-lexer) 49 | (dynamic-require 'shrubbery/syntax-color 50 | 'shrubbery-text-mode-lexer)] 51 | [(drracket:keystrokes) 52 | (append (shrubbery:get-info-proc key default make-default) 53 | (dynamic-require 'scribble/private/indentation 'keystrokes))] 54 | [(drracket:toolbar-buttons) 55 | (dynamic-require 'scribble/tools/drracket-buttons 'drracket-buttons)] 56 | [else (shrubbery:get-info-proc key default make-default)]))) 57 | 58 | (define-syntax (module-begin stx) 59 | (syntax-parse stx 60 | #:datum-literals (brackets) 61 | [(_ (brackets g ...)) 62 | #:with (g-unwrapped ...) (for/list ([g (in-list (syntax->list #'(g ...)))]) 63 | (syntax-parse g 64 | #:datum-literals (group parens) 65 | [(group (parens sub-g)) #'sub-g] 66 | [else g])) 67 | #`(doc:#%module-begin 68 | #:id #,(datum->syntax stx 'doc) 69 | #:begin [(module configure-runtime racket/base (require rhombus/runtime-config))] 70 | #:post-process post-process 71 | (rhombus-forwarding-sequence 72 | (scribble-rhombus-top g-unwrapped ...)))])) 73 | 74 | ;; Includes shortcut for string literals: 75 | (define-syntax (scribble-rhombus-top stx) 76 | (let loop ([stx stx] [accum null]) 77 | (syntax-parse stx 78 | [(_ str:string . rest) 79 | (loop #'(t . rest) 80 | (cons #'str accum))] 81 | [(_ . rest) 82 | (define top #`(rhombus-top-step scribble-rhombus-top . rest)) 83 | (if (null? accum) 84 | top 85 | #`(begin 86 | #,@(reverse accum) 87 | top))]))) 88 | 89 | ;; ---------------------------------------- 90 | 91 | (define (verbatim #:indent [indent 0] l) 92 | (apply base:verbatim #:indent indent l)) 93 | 94 | (define (pkg l) 95 | (tt l)) 96 | -------------------------------------------------------------------------------- /scribble/rhombus/manual.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (except-in scribble/rhombus 3 | if) 4 | (prefix-in manual: scribble/manual) 5 | "../private/doc.rhm" 6 | "../private/docmodule.rhm" 7 | "../private/example.rhm" 8 | (only-in "../private/typeset-doc.rkt" 9 | grammar) 10 | "../private/rhombus-spacer.rkt") 11 | 12 | (provide (all-from-out scribble/rhombus) 13 | litchar 14 | (rename-out [manual:deftech deftech] 15 | [manual:tech tech] 16 | [manual:math math] 17 | [manual:filepath filepath]) 18 | doc 19 | docmodule 20 | rhombusmodname 21 | grammar 22 | examples 23 | make_rhombus_eval 24 | close_eval 25 | (all-from-out "../private/rhombus-spacer.rkt")) 26 | 27 | (module reader syntax/module-reader 28 | #:language 'scribble/rhombus/manual 29 | #:read read-proc 30 | #:read-syntax read-syntax-proc 31 | #:info get-info-proc 32 | #:whole-body-readers? #t 33 | (require (submod scribble/rhombus reader))) 34 | 35 | (define (litchar ls) 36 | (manual:litchar (if (string? ls) ls (car ls)))) 37 | -------------------------------------------------------------------------------- /shrubbery/info.rkt: -------------------------------------------------------------------------------- 1 | #lang info 2 | 3 | (define scribblings '(("scribblings/shrubbery.scrbl" (multi-page)))) 4 | 5 | -------------------------------------------------------------------------------- /shrubbery/interaction.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require "lex.rkt" 3 | "private/paren.rkt" 4 | syntax-color/racket-lexer) 5 | 6 | (provide shrubbery-submit-predicate) 7 | 8 | (define (shrubbery-submit-predicate in whitespace-after?) 9 | (and whitespace-after? 10 | ;; siilar to `lex-all` with `#:interactive? #t`, but staying in lexer 11 | ;; land by using `racket-lexer/status`: 12 | (let loop ([prev-status 'initial] [something? #f] [depth 0] [blanks 0] [multi? #f]) 13 | (let-values ([(tok type paren start end backup status) 14 | (lex/status in (file-position in) prev-status racket-lexer*/status)]) 15 | (case (cond 16 | [(eof-object? tok) 'EOF] 17 | [(token? tok) (token-name tok)] 18 | [else 's-exp]) 19 | [(EOF) (and (not (lex-nested-status? prev-status)) 20 | something? 21 | (zero? depth) 22 | (or (not multi?) 23 | (blanks . > . 0)))] 24 | [(fail) #f] 25 | [(opener) (loop status #t (+ depth 1) 0 multi?)] 26 | [(closer) (loop status #t (max 0 (- depth 1)) 0 multi?)] 27 | [(comment) (loop status something? depth 0 multi?)] 28 | [(whitespace) 29 | (define lines (count-newlines (syntax-e (token-value tok)))) 30 | (loop status something? depth (+ lines blanks) multi?)] 31 | [(block-operator semicolon-operator) 32 | (loop status #t depth 0 33 | (or multi? (and (not (lex-nested-status? prev-status)) 34 | (zero? depth))))] 35 | [else (loop status #t depth 0 multi?)]))))) 36 | 37 | (define (count-newlines s) 38 | (for/sum ([c (in-string s)]) 39 | (if (char=? c #\newline) 40 | 1 41 | 0))) 42 | -------------------------------------------------------------------------------- /shrubbery/keystroke.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require "armor.rkt") 3 | 4 | (provide shrubbery-keystrokes) 5 | 6 | (define (map-meta key proc) 7 | (append 8 | (if #t ; mapping option as meta? 9 | (list (list (format "?:a:~a" key) proc)) 10 | null) 11 | (list (list (format "?:m:~a" key) proc) 12 | (list (format "esc;~a" key) proc)))) 13 | 14 | (define shrubbery-keystrokes 15 | (append 16 | (map-meta "a" toggle-armor))) 17 | -------------------------------------------------------------------------------- /shrubbery/main.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (module reader racket/base 4 | (require syntax/strip-context 5 | "parse.rkt") 6 | 7 | (provide (rename-out [shrubbery-read read] 8 | [shrubbery-read-syntax read-syntax]) 9 | get-info 10 | get-info-proc) 11 | 12 | (define (shrubbery-read in) 13 | (syntax->datum 14 | (shrubbery-read-syntax #f in))) 15 | 16 | (define (shrubbery-read-syntax src in) 17 | (strip-context 18 | #`(module anything racket/base 19 | '#,(parse-all in)))) 20 | 21 | (define (get-info in mod line col pos) 22 | (lambda (key default) 23 | (get-info-proc key default (lambda (key default) default)))) 24 | 25 | (define (get-info-proc key default make-default) 26 | (case key 27 | [(color-lexer) 28 | (dynamic-require 'shrubbery/syntax-color 29 | 'shrubbery-lexer)] 30 | [(drracket:indentation) 31 | (dynamic-require 'shrubbery/indentation 32 | 'shrubbery-indentation)] 33 | [(drracket:range-indentation) 34 | (dynamic-require 'shrubbery/indentation 35 | 'shrubbery-range-indentation)] 36 | [(drracket:paren-matches) 37 | (dynamic-require 'shrubbery/indentation 38 | 'shrubbery-paren-matches)] 39 | [(drracket:quote-matches) 40 | (dynamic-require 'shrubbery/indentation 41 | 'shrubbery-quote-matches)] 42 | [(drracket:grouping-position) 43 | (dynamic-require 'shrubbery/navigation 44 | 'shrubbery-grouping-position)] 45 | [(drracket:submit-predicate) 46 | (dynamic-require 'shrubbery/interaction 47 | 'shrubbery-submit-predicate)] 48 | [(drracket:keystrokes) 49 | (dynamic-require 'shrubbery/keystroke 50 | 'shrubbery-keystrokes)] 51 | [else (make-default key default)]))) 52 | -------------------------------------------------------------------------------- /shrubbery/private/delta-text.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require racket/class) 3 | 4 | (provide make-delta-text) 5 | 6 | (define delta-text% 7 | (class object% 8 | (init-field next ; the text that this is a delta from 9 | at-pos ; the start of a line where whitespace is inserted or deleted 10 | delta) ; amount to insert or (when negative) delete 11 | 12 | (super-new) 13 | 14 | ;; No effect before this position: 15 | (define pre at-pos) 16 | ;; Simple token shifting after this position (new coordinates): 17 | (define post (let-values ([(s e) (send next get-token-range pre)]) 18 | (unless (= pre s) (error "bad delta construction")) 19 | (+ e delta))) 20 | ;; The range from `pre` to `post` is a whitespace token, 21 | ;; either newly extended to newly truncated 22 | 23 | (define/public (get-text s e) 24 | (cond 25 | [(e . <= . pre) (send next get-text s e)] 26 | [(s . >= . post) 27 | (send next get-text (- s delta) (- e delta))] 28 | [(s . < . pre) 29 | (string-append 30 | (get-text s pre) 31 | (get-text pre e))] 32 | [(e . > . post) 33 | (string-append 34 | (get-text s post) 35 | (get-text post e))] 36 | [else 37 | ;; bounds are completely in whitespace region: 38 | (make-string #\space (- e s))])) 39 | 40 | (define/public (classify-position* pos) 41 | (cond 42 | [(pos . < . pre) 43 | (send next classify-position* pos)] 44 | [(pos . >= . post) 45 | (send next classify-position* (- pos delta))] 46 | [else 'white-space])) 47 | 48 | (define/public (classify-position pos) 49 | (define type (classify-position* pos)) 50 | (if (hash? type) (hash-ref type 'type 'unknown) type)) 51 | 52 | (define/public (get-token-range pos) 53 | (cond 54 | [(pos . < . pre) 55 | (send next get-token-range pos)] 56 | [(pos . >= . post) 57 | (define-values (s e) (send next get-token-range (- pos delta))) 58 | (values (+ s delta) (+ e delta))] 59 | [else (values pre post)])) 60 | 61 | (define/private (shift-in r) 62 | (if (r . < . pre) 63 | r 64 | (max pre (- r delta)))) 65 | 66 | ;; biased to the end of inserted whitespace 67 | (define/private (shift-out r) 68 | (and r 69 | (cond 70 | [(r . <= . pre) r] 71 | [else (max pre (+ r delta))]))) 72 | 73 | (define/public (last-position) 74 | (shift-out (send next last-position))) 75 | 76 | (define/public (position-paragraph pos [eol? #f]) 77 | (send next position-paragraph (shift-in pos) eol?)) 78 | 79 | (define/public (paragraph-start-position para) 80 | (shift-out (send next paragraph-start-position para))) 81 | 82 | (define/public (paragraph-end-position para) 83 | (shift-out (send next paragraph-end-position para))) 84 | 85 | (define/public (backward-match pos cutoff) 86 | (shift-out (send next backward-match (shift-in pos) (shift-in cutoff)))) 87 | 88 | (define/public (forward-match pos cutoff) 89 | (shift-out (send next forward-match (shift-in pos) (shift-in cutoff)))))) 90 | 91 | (define (make-delta-text t at-pos delta) 92 | (new delta-text% 93 | [next t] 94 | [at-pos at-pos] 95 | [delta delta])) 96 | -------------------------------------------------------------------------------- /shrubbery/private/paren.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide shrubbery-paren-matches 4 | shrubbery-quote-matches) 5 | 6 | (define shrubbery-paren-matches 7 | '((|(| |)|) 8 | (|[| |]|) 9 | (|{| |}|) 10 | (« »))) 11 | 12 | (define shrubbery-quote-matches 13 | '(#\")) 14 | -------------------------------------------------------------------------------- /shrubbery/private/peek-port.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide call-with-peeking-port) 4 | 5 | (struct peek-port (in [pos #:mutable])) 6 | 7 | (define current-peek-port-key (gensym)) 8 | (define (current-peek-port) (continuation-mark-set-first #f current-peek-port-key)) 9 | 10 | ;; make the port once, to minimize overhead: 11 | (define peek-input-port 12 | (make-input-port 13 | 'peeker 14 | (lambda (bstr) 15 | (define pp (current-peek-port)) 16 | (define pos (peek-port-pos pp)) 17 | (define n (peek-bytes! bstr pos (peek-port-in pp))) 18 | (cond 19 | [(eof-object? n) eof] 20 | [else 21 | (set-peek-port-pos! pp (+ pos n)) 22 | n])) 23 | (lambda (bstr offset evt) 24 | (define pp (current-peek-port)) 25 | (peek-bytes! bstr (+ offset (peek-port-pos pp)) (peek-port-in pp))) 26 | void)) 27 | 28 | (define (call-with-peeking-port in proc) 29 | (with-continuation-mark 30 | current-peek-port-key 31 | (peek-port in 0) 32 | (proc peek-input-port))) 33 | -------------------------------------------------------------------------------- /shrubbery/private/property.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide syntax-raw-property 4 | syntax-raw-prefix-property 5 | syntax-raw-suffix-property 6 | syntax-raw-tail-property 7 | syntax-raw-tail-suffix-property) 8 | 9 | (define syntax-raw-property 10 | (case-lambda 11 | [(stx) (syntax-property stx 'raw)] 12 | [(stx val) (syntax-property stx 'raw val #t)])) 13 | 14 | (define syntax-raw-prefix-property 15 | (case-lambda 16 | [(stx) (syntax-property stx 'raw-prefix)] 17 | [(stx val) (syntax-property stx 'raw-prefix val #t)])) 18 | 19 | (define syntax-raw-suffix-property 20 | (case-lambda 21 | [(stx) (syntax-property stx 'raw-suffix)] 22 | [(stx val) (syntax-property stx 'raw-suffix val #t)])) 23 | 24 | ;; "tail" is attached to the head term of a list, and it 25 | ;; applies after the last item in the list, but counts as 26 | ;; the representation of the list itself 27 | (define syntax-raw-tail-property 28 | (case-lambda 29 | [(stx) (syntax-property stx 'raw-tail)] 30 | [(stx val) (syntax-property stx 'raw-tail val #t)])) 31 | 32 | ;; like tail, but for a further suffix that is outside the list 33 | (define syntax-raw-tail-suffix-property 34 | (case-lambda 35 | [(stx) (syntax-property stx 'raw-tail-suffix)] 36 | [(stx val) (syntax-property stx 'raw-tail-suffix val #t)])) 37 | -------------------------------------------------------------------------------- /shrubbery/property.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require "private/property.rkt") 3 | 4 | (provide (all-from-out "private/property.rkt")) 5 | -------------------------------------------------------------------------------- /shrubbery/scribblings/example.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | 3 | @title[~tag: "example"]{Examples} 4 | 5 | Here are some example shrubberies. Each line either uses old indentation 6 | to continue a nesting level that was started on a previous line, starts 7 | with new indentation and follows a line that ends with @litchar{:}, or 8 | starts with new indentation and a @litchar{|} on the same line. A 9 | @litchar{:} or @litchar{|} can also appear in the middle of a line, but 10 | that's roughly a shorthand for starting a new indented line after the 11 | @litchar{:} or before the @litchar{|}. The complete rules involve more 12 | terminology, but that's enough to get a sense of the examples. 13 | 14 | @(rhombusblock: 15 | def identity(x): x 16 | 17 | def fib(n): 18 | cond 19 | | n == 0: 0 20 | | n == 1: 1 21 | | else: fib(n-1) + fib(n-2) 22 | 23 | def print_sexp(v): 24 | match v 25 | | empty: display("()") 26 | | cons(a, d): 27 | if is_list(d) 28 | | display("(") 29 | print_sexp(a) 30 | for (v = in_list(d)): 31 | display(" ") 32 | print_sexp(v) 33 | display(")") 34 | | display("(") 35 | print_sexp(a) 36 | display(". ") 37 | print_sexp(d) 38 | display(")") 39 | | v: print_atom(v) 40 | ) 41 | 42 | Forms like @litchar{def}, @litchar{cond}, and @litchar{match} are not 43 | specified by shrubbery notation, since specifying those forms is up to a 44 | language that is built on top of shrubbery notation. Still, shrubbery 45 | notation is meant to accommodate a particular kind of syntax for nested 46 | blocks (via @litchar{:} and indentation) and conditional blocks (via 47 | @litchar{|}). 48 | 49 | Identifiers are C-style with alphanumerics and underscores. Operators 50 | are sequences of symbolic characters in the sense of 51 | @litchar{char-symbolic?}, roughly. No spaces are needed between 52 | operators and non-operators, so @litchar{1+2} and @litchar{1 + 2} mean 53 | the same thing. Comments are C-style. See @secref["lexeme-parsing"] 54 | for more information. 55 | 56 | The following tokens are used for grouping, in addition to line breaks 57 | and indentation: 58 | 59 | @verbatim[~indent: 2]{ 60 | ( ) [ ] { } ; , : | « » \ 61 | } 62 | 63 | Parentheses, square brackets, and curly braces are used to delimit 64 | groups in the obvious way. A @litchar{;} or @litchar{,} acts as a group 65 | separator, even within a single line. A @litchar{:} or @litchar{|} 66 | treats remaining item on the same line like a new indented line, which 67 | forms a block of nested groups. A guillemot pair @litchar{«} and @litchar{»} can be 68 | used in rare cases to explicitly bracket subgroups formed by 69 | @litchar{:} and @litchar{|} without line breaks. A @litchar{\} continues 70 | a line, effectively shifting all columns on the next line as if they 71 | appeared immediately after the @litchar{\}. 72 | -------------------------------------------------------------------------------- /shrubbery/scribblings/grammar-s-exp.rkt: -------------------------------------------------------------------------------- 1 | #lang at-exp racket/base 2 | (require scribble/bnf 3 | scribble/manual) 4 | 5 | (provide shrubbery_s_expression_grammar) 6 | 7 | (define shrubbery_s_expression_grammar 8 | (BNF (list @nonterm{parsed} 9 | @racket[(top @#,nonterm{group} ...)]) 10 | (list @nonterm{group} 11 | @racket[(group @#,nonterm{term} ... @#,nonterm{tail-term})]) 12 | (list @nonterm{term} 13 | @nonterm{atom} 14 | @racket[(op @#,nonterm{symbol})] 15 | @racket[(parens @#,nonterm{group} ...)] 16 | @racket[(brackets @#,nonterm{group} ...)] 17 | @racket[(braces @#,nonterm{group} ...)]) 18 | (list @nonterm{tail-term} 19 | @nonterm{term} 20 | @nonterm{block} 21 | @racket[(alts @#,nonterm{block} ...)]) 22 | (list @nonterm{block} 23 | @racket[(block @#,nonterm{group} ...)]))) 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /shrubbery/scribblings/grammar.rhm: -------------------------------------------------------------------------------- 1 | #lang rhombus 2 | 3 | import: 4 | scribble/rhombus open 5 | scribble/bnf: 6 | rename: #{BNF-seq} as bseq 7 | #{BNF-alt} as balt 8 | optional as boptional 9 | expose: nonterm bseq balt kleenestar kleeneplus boptional 10 | 11 | export: 12 | bnf 13 | bseq balt boptional nonterm bseq kleenestar kleeneplus boptional 14 | bis bor 15 | 16 | def bis: @elem{@hspace[1]::=@hspace[1]} 17 | def bor: @elem{@hspace[1] | @hspace[1]} 18 | -------------------------------------------------------------------------------- /shrubbery/scribblings/meta.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | 3 | @title{Design Considerations} 4 | 5 | Shrubbery notation serves the same role as S-expression notation as a 6 | vehicle for a programming language, but with different trade-offs. 7 | 8 | S-expression notation imposes a grouping at the lexeme level that is all 9 | but guaranteed to be respected by further parsing via macro expansion. 10 | One consequence of this lexeme-based grouping is that programs can be 11 | pretty-printed and textually traversed in standard ways. 12 | 13 | A traditional use of S-expression notation, however, insists that 14 | @emph{all} grouping is reflected in the S-expression. Reifying all 15 | grouping at the lexeme level is so onerous that many practical 16 | deployments of S-expressions include deviations from the rule, such as 17 | keyword-based arguments or implicit grouping by position (as in various 18 | Clojure forms). 19 | 20 | Another disadvantage of S-expressions is that many of the parentheses 21 | are redundant after the expression is pretty-printed, because 22 | indentation provides the same grouping information in a more 23 | human-readable way. That observation suggests instead relying on line 24 | breaks and indentation to impart grouping information, as in Python. 25 | 26 | Shrubbery notation explores a point in the design space where the 27 | notation is 28 | 29 | @itemlist[ 30 | 31 | @item{line- and indentation-sensitive, and}, 32 | 33 | @item{intended to constrain grouping but not reflect every detail of 34 | grouping.} 35 | 36 | ] 37 | 38 | Deferring complete grouping to another parser relieves a burden on 39 | reader-level notation. At the same time, line- and indentation-sensitive 40 | rules constrain parsing to ensure that line breaks and indentation in 41 | the source are not misleading. 42 | 43 | @include_section["rationale.scrbl"] 44 | @include_section["prior-art.scrbl"] -------------------------------------------------------------------------------- /shrubbery/scribblings/prior-art.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | 3 | @title{Prior Art} 4 | 5 | Indentation-sensitive parsing and the use of @litchar{:} is obviously 6 | informed by Python. 7 | 8 | Sampling notation's rules relating indentation, lines, @litchar{;}, and 9 | @litchar{:} are originally based on the 10 | @hyperlink["https://github.com/tonyg/racket-something"]{#lang something} 11 | reader, which also targets an underlying expander that 12 | further groups tokens. Shrubbery notation evolved away from using 13 | @litchar{{}} for blocks, however, because @litchar{:} was nearly always 14 | preferred in experiments with the notation. For the very rare case that 15 | explicit grouping is needed for a block, @litchar{«} and @litchar{»} can 16 | be used. Freeing @litchar{{}} from use for blocks, meanwhile, allows its 17 | use for set and map notations. 18 | 19 | Shrubbery notation is also based on 20 | @hyperlink["https://github.com/jeapostrophe/racket2-rfcs/blob/lexpr/lexpr/0004-lexpr.md"]{Lexprs}, 21 | particularly its use of @litchar{|}. Lexprs uses mandatory @litchar{:} and @litchar{|} tokens 22 | as a prefix for indentation, and it absorbs an additional line after 23 | an indented section to allow further chaining of the group. Although 24 | @litchar{«»} can be used to form multiple subgroups within a shrubbery group, 25 | the notation discourages that style in favor of further nesting (or, 26 | in the case of @litchar{if}, in favor of @litchar{|} notation like other 27 | conditionals). 28 | 29 | Shrubbery notation is in some sense a follow-up to 30 | @hyperlink["https://github.com/mflatt/racket2-rfcs/blob/sapling/sapling/0005-sapling.md"]{sapling notation}. 31 | The primary difference is that shrubbery notation is 32 | indentation-sensitive, while sapling notation is 33 | indentation-insensitive. Indentation sensitivity and block conventions 34 | in shrubbery notation avoid some delimiters and blank lines that are 35 | needed in sapling notation. 36 | 37 | More generally, shrubbery notation takes inspiration from 38 | S-expressions and alternative S-expression notations. The idea that, 39 | even in an S-expression-like setting, some parsing can be deferred a 40 | later parser has many precedents, including Clojure's choice of where 41 | to put parentheses and notations that use something like @litchar{$} to escape 42 | to infix mode. 43 | -------------------------------------------------------------------------------- /shrubbery/scribblings/shrubbery.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/rhombus/manual 2 | @(import: 3 | "grammar.rhm" open 4 | "grammar-s-exp.rkt": 5 | expose: shrubbery_s_expression_grammar) 6 | 7 | @title{Shrubbery Notation} 8 | 9 | Shrubbery notation is a set of text-level convention that build toward a 10 | full programming language, such as 11 | @seclink[~doc: [symbol(lib), "rhombus/scribblings/rhombus.scrbl"], "top"]{Rhombus}. 12 | The notation is line- and indentation-sensitive, and it is intended to partially group 13 | input, but leave further parsing to another layer, especially 14 | @seclink[~doc: [symbol(lib), "enforest/scribblings/enforest.scrbl"], "top"]{enforestation}. 15 | The parsed form of a shrubbery imposes grouping to ensure that further 16 | parsing is consistent with the shrubbery's lines and indentation. 17 | 18 | @table_of_contents[] 19 | 20 | @include_section["example.scrbl"] 21 | @include_section["group-and-block.scrbl"] 22 | @include_section["lexeme-parsing.scrbl"] 23 | @include_section["parsed-representation.scrbl"] 24 | @include_section["meta.scrbl"] 25 | -------------------------------------------------------------------------------- /shrubbery/srcloc.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base)) 3 | 4 | (provide syntax-srcloc) 5 | 6 | (define-syntax (get stx) 7 | (syntax-case stx () 8 | [(_ syntax-srcloc) 9 | (cond 10 | [(identifier-binding #'syntax-srcloc) 11 | #'(begin)] 12 | [(file-exists? (collection-file-path "syntax-srcloc.rkt" "racket")) 13 | #`(require #,(datum->syntax #'syntax-srcloc 'racket/syntax-srcloc))] 14 | [else 15 | #'(define (syntax-srcloc stx) 16 | (srcloc (syntax-source stx) 17 | (syntax-line stx) 18 | (syntax-column stx) 19 | (syntax-position stx) 20 | (syntax-span stx)))])])) 21 | 22 | (get syntax-srcloc) 23 | -------------------------------------------------------------------------------- /shrubbery/syntax-color.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require "lex.rkt" 3 | "lex-comment.rkt" 4 | syntax-color/racket-lexer 5 | syntax-color/lexer-contract 6 | racket/symbol) 7 | 8 | (provide shrubbery-lexer 9 | shrubbery-text-mode-lexer) 10 | 11 | (define (shrubbery-lexer in pos status) 12 | (let-values ([(tok type paren start end backup status) 13 | (lex/comment/status in pos status racket-lexer*/status)]) 14 | (define (to-string-or-eof tok) 15 | (cond 16 | [(eof-object? tok) tok] 17 | [(string? tok) tok] 18 | [(token? tok) (to-string-or-eof (token-e tok))] 19 | [(syntax? tok) (to-string-or-eof (syntax-e tok))] 20 | [(symbol? tok) (symbol->immutable-string tok)] 21 | [else "other"])) 22 | (values (to-string-or-eof tok) type paren start end backup 23 | (if (lex/comment-dont-stop-status? status) 24 | (dont-stop status) 25 | status)))) 26 | 27 | (define (shrubbery-text-mode-lexer in pos status) 28 | (shrubbery-lexer in pos (or status 29 | (make-in-text-status)))) 30 | -------------------------------------------------------------------------------- /shrubbery/tests/armor.rkt: -------------------------------------------------------------------------------- 1 | #lang at-exp racket/base 2 | (require racket/class 3 | racket/pretty 4 | "../lex.rkt" 5 | "../parse.rkt" 6 | "../armor.rkt" 7 | "like-text.rkt" 8 | "input.rkt") 9 | 10 | (define (out name parsed write) 11 | (define path (build-path (find-system-path 'temp-dir) name)) 12 | (printf "~a\n" path) 13 | (call-with-output-file* 14 | path 15 | #:exists 'truncate 16 | (lambda (o) (write parsed o)))) 17 | 18 | (define (check what input expected #:unarmor? [unarmor? #t]) 19 | (printf "checking ~s\n" what) 20 | (define t (new like-text% [content input])) 21 | (define length (string-length input)) 22 | 23 | (armor-region t 0 length) 24 | (define in (open-input-string input)) 25 | (port-count-lines! in) 26 | (define orig-parsed (syntax->datum (parse-all in))) 27 | (define armored (send t get-text 0 (send t last-position))) 28 | (define armor-parsed 29 | (with-handlers ([exn:fail? (lambda (exn) 30 | (printf "~a\n" (exn-message exn)) 31 | (out "armored" armored write-string) 32 | (error "parse of armored failed"))]) 33 | (define in (open-input-string armored)) 34 | (port-count-lines! in) ; helps with source locations on error 35 | (syntax->datum (parse-all in)))) 36 | (unless (equal? orig-parsed armor-parsed) 37 | (out "original" orig-parsed pretty-write) 38 | (out "armored" armor-parsed pretty-write) 39 | (error "parse of armored is different")) 40 | 41 | (when unarmor? 42 | (define t2 (new like-text% [content armored])) 43 | (unarmor-region t2 0 (send t2 last-position)) 44 | (define unarmored (send t2 get-text 0 (send t2 last-position))) 45 | (unless (equal? input unarmored) 46 | (out "input" input display) 47 | (out "unarmored" unarmored display) 48 | (out "armored" armored display) 49 | (error "unarmored is different from input")))) 50 | 51 | (check "simple" "a: 1 2" '(top (group a (block (group 1) (group 2))))) 52 | (check 1 input1 expected1) 53 | (check '1a input1a expected1a #:unarmor? #f) 54 | (check 2 input2 expected2 #:unarmor? #f) ; unarmor not right yet for continuing 55 | (check 3 input3 expected3) 56 | (check 4 input4 expected4) 57 | -------------------------------------------------------------------------------- /shrubbery/tests/delta-text.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require racket/class 3 | "like-text.rkt" 4 | "../private/delta-text.rkt") 5 | 6 | (define input 7 | (string-append "#lang shrubbery\n" 8 | "a\n" 9 | " b\n" 10 | " c\n" 11 | " (d)\n" 12 | " @f{e}\n")) 13 | 14 | (define (check rx delta) 15 | (define m (regexp-match-positions rx input)) 16 | (define at-pos (caar m)) 17 | (define input2 18 | (string-append (substring input 0 at-pos) 19 | (if (positive? delta) 20 | (make-string delta #\space) 21 | "") 22 | (substring input (if (positive? delta) 23 | at-pos 24 | (- at-pos delta))))) 25 | (define dt (make-delta-text (new like-text% 26 | [content input]) 27 | at-pos 28 | delta)) 29 | (define t (new like-text% 30 | [content input2])) 31 | (for ([i (in-range (string-length input2))]) 32 | (define-values (ds de) (send dt get-token-range i)) 33 | (define-values (s e) (send t get-token-range i)) 34 | (define dc (send dt classify-position i)) 35 | (define c (send t classify-position i)) 36 | (define dp (send dt position-paragraph i)) 37 | (define p (send t position-paragraph i)) 38 | (define dsp (send dt paragraph-start-position dp)) 39 | (define sp (send t paragraph-start-position p)) 40 | (define dep (send dt paragraph-end-position dp)) 41 | (define ep (send t paragraph-end-position p)) 42 | (define dbm (send dt backward-match i 0)) 43 | (define bm (send t backward-match i 0)) 44 | (unless (and (eqv? ds s) 45 | (eqv? de e) 46 | (equal? dc c) 47 | (equal? dp p) 48 | (equal? dsp sp) 49 | (equal? dep ep) 50 | (eqv? dbm bm)) 51 | (error 'fail 52 | (string-append "range\n" 53 | " rx: ~s = ~s\n" 54 | " delta: ~s\n" 55 | " offset: ~a\n" 56 | " text: ~s\n" 57 | " delta t: ~s\n" 58 | " plain t: ~s") 59 | rx at-pos 60 | delta 61 | i 62 | (substring input2 i) 63 | (list ds de dc dp dsp dep dbm) 64 | (list s e c p sp ep bm))))) 65 | 66 | (check #rx" b" 1) 67 | (check #rx" b" -1) 68 | (check #rx" c" 1) 69 | (check #rx" c" -1) 70 | (check #rx" c" 2) 71 | (check #rx" c" -2) 72 | (check #rx" c" 5) 73 | (check #rx" .d" 1) 74 | (check #rx" .d" -1) 75 | (check #rx" .d" 2) 76 | (check #rx" .d" -2) 77 | (check #rx" .d" 3) 78 | (check #rx" .d" -3) 79 | (check #rx" .d" 5) 80 | -------------------------------------------------------------------------------- /shrubbery/write.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require racket/keyword 3 | racket/symbol) 4 | 5 | ;; Writing a shubbery represented as an S-expression 6 | 7 | (provide write-shrubbery) 8 | 9 | (define rx:identifier #px"^(?:\\p{L}|_)(?:\\p{L}|\\p{N}|_)*$") 10 | 11 | (define (write-shrubbery v [op (current-output-port)]) 12 | (cond 13 | [(and (pair? v) (eq? 'group (car v))) 14 | ;; printing a raw group 15 | (display "«" op) 16 | (write-shrubbery-term v op) 17 | (display "»" op)] 18 | [else 19 | (write-shrubbery-term v op)])) 20 | 21 | (define (write-shrubbery-term v op) 22 | (let loop ([v v] [sole? #t]) 23 | (cond 24 | [(list? v) 25 | (cond 26 | [(null? v) 27 | (error 'write-shubbery "unexpected ~s" v)] 28 | [(eq? 'op (car v)) 29 | (cond 30 | [(and (not sole?) 31 | (memq (cadr v) '(... ¿)) ) 32 | (display "¿(? " op) 33 | (display (cadr v) op) 34 | (display ")" op)] 35 | [else 36 | (display (cadr v) op)])] 37 | [(eq? 'alts (car v)) 38 | (display "" op) 39 | (for/fold ([first? #t]) ([v (in-list (cdr v))]) 40 | (unless first? (display " " op)) 41 | (display "|« " op) 42 | (unless (and (pair? v) (eq? (car v) 'block)) 43 | (error 'write-shubbery "unexpected ~s" v)) 44 | (for/fold ([first? #t]) ([v (in-list (cdr v))]) 45 | (unless first? (display "; " op)) 46 | (loop v #f) 47 | #f) 48 | (display " »" op) 49 | #f)] 50 | [(eq? 'top (car v)) 51 | (for/fold ([first? #t]) ([v (in-list (cdr v))]) 52 | (unless first? (display "; " op)) 53 | (loop v #t) 54 | #f) 55 | (void)] 56 | [else 57 | (define-values (open sep close) 58 | (case (car v) 59 | [(group) (values "" " " "")] 60 | [(block) (values ":« " "; " " »")] 61 | [(parens) (values "(" ", " ")")] 62 | [(brackets) (values "[" ", " "]")] 63 | [(braces) (values "{" ", " "}")] 64 | [else (error 'write-shubbery "unexpected ~s" (car v))])) 65 | (display open op) 66 | (for/fold ([first? #t]) ([v (in-list (cdr v))]) 67 | (unless (or first? 68 | (and (pair? v) (eq? (car v) 'block))) 69 | (display sep op)) 70 | (loop v #f) 71 | #f) 72 | (display close op)])] 73 | [(symbol? v) 74 | (define s (symbol->immutable-string v)) 75 | (cond 76 | [(regexp-match? rx:identifier s) 77 | (display s op)] 78 | [else 79 | (display "#{" op) 80 | (write v op) 81 | (display "}" op)])] 82 | [(keyword? v) 83 | (define s (keyword->immutable-string v)) 84 | (cond 85 | [(regexp-match? rx:identifier s) 86 | (display "~" op) 87 | (display s op)] 88 | [else 89 | (display "#{") 90 | (write v op) 91 | (display "}")])] 92 | [(or (string? v) 93 | (bytes? v) 94 | (exact-integer? v)) 95 | (write v op)] 96 | [(flonum? v) 97 | (cond 98 | [(eqv? v +inf.0) (display "#inf" op)] 99 | [(eqv? v -inf.0) (display "#neginf" op)] 100 | [(eqv? v +nan.0) (display "#nan" op)] 101 | [else (write v op)])] 102 | [(boolean? v) 103 | (display (if v "#true" "#false") op)] 104 | [(void? v) 105 | (display "#void" op)] 106 | [else 107 | (display "#{") 108 | (write v op) 109 | (display "}")]))) 110 | --------------------------------------------------------------------------------