On this page:
define-element
define-elements-together
10.1.3.1 Struct Type Definition Overview
field
get-field
lift-property
lift-methods
lift-begin
10.1.3.2 Field Definition Forms
define/  field
define-fields
define-values/  fields
10.1.3.3 Supporting Standard Interfaces
declare-resp-field
prop:  element->plain-text
declare-paragraphs-status-field
10.1.3.4 Implementing Additional Forms
field/  derived
get-field/  derived
lift-property/  derived
lift-methods/  derived
lift-begin/  derived
define/  field/  derived
define-values/  fields/  derived
field-name/  maybe-opts
local-element-name
0.5.91
10.1.3 Defining TEI Elements🔗ℹ

syntax

(define-element name-id
  maybe-inset
  contract-option ...
  type+prose)
 
maybe-inset = 
  | #:inset? inset?-expr
     
contract-option = #:contains-text
  | #:extra-check extra-check-expr
  | #:attr-contracts attr-contracts-spec
  | #:required-attrs (attr-name-id ...)
  | #:children children-spec
  | #:required-order (child-name-id ...)
     
attr-contracts-spec = ([attr-name-id attr-contract-expr] ...)
     
children-spec = ([repeat-constraint child-name-id] ...)
     
repeat-constraint = 1  |  1+  |  0-1  |  0+
     
type+prose = struct-type-def #:prose [prose-body ...+]
  | #:prose [prose-body ...+] struct-type-def
     
struct-type-def = see Struct Type Definition Overview
 
  extra-check-expr : 
(or/c #f (-> raw-xexpr-element/c
             (or/c blame? #f)
             any/c
             any/c))
  attr-contract-expr : flat-contract?
Declares the specification for the TEI XML element name-id, including documentation, a contract, and a TEI element struct type implementation. See also define-elements-together. A define-element form may appear only at the module level or in an element definition prose context.

The contract-option clauses specify validity requirements for the element’s attributes and contents. At the documentation-time phase, they generate the code that produces the element’s “blue box”. (See Formal Specification for examples.) The attr-contract-expr sub-forms are typeset as with racketblock, so bindings at the label phase level relative to the documentation-time phase are used for cross-references. At runtime, contract-option clauses are used to generate the contract produced by (tei-xexpr/c name-id). The attr-contract-expr and extra-check-expr expressions are evaluated at the runtime of the module containing the define-element form, and references to their values are part of the static information encapsulated by the module’s elements specification transformer to be used when it is invoked (see define-values/elements-specifications).

The struct-type-def non-terminal produces the implementation of the TEI element struct type for the element. Its grammar and semantics are documented under Struct Type Definition Overview. These forms have meaning only at runtime.

The prose-body forms are expanded in a element definition prose context, a special type of expression context that allows nested uses of begin-for-runtime, define-element, and related forms. Their values at the documentation-time phase are used for the prose description of the element, while runtime code embedded via begin-for-runtime or similar is lifted to the module level of the runtime phase.

If an inset?-expr is given and evaluates (at the documentation-time phase) to a non-false value, the prose-body forms are indented (somewhat like defsubform).

syntax

(define-elements-together
  maybe-inset
  ([name-id
    contract-option ...
    struct-type-def]
   ...+)
  prose-body ...)
Like define-element, but define several TEI XML elements at once, somewhat like deftogether.

10.1.3.1 Struct Type Definition Overview🔗ℹ

syntax

 
struct-type-def = struct-type-def-part ...
     
struct-type-def-part = #:predicate predicate-name-id
  | #:property prop-expr prop-val-expr
  | #:methods generic-id [methods-body ...]
  | #:begin [begin-body ...]
  | #:constructor ctor-spec
     
ctor-spec = 
[ctor-arg-binding ...
 ctor-body ...]
     
ctor-arg-binding = #:name name-arg-id
  | #:attributes attributes-arg-id
  | #:body body-arg-id
  | #:body/elements-only body/elements-only-arg-id
  | #:this/thunk this/thunk-id
     
ctor-body = plain-ctor-body ; definitions and expressions
  | special-ctor-form ; after partial expansion
     
special-ctor-form = (field . _) ; and related forms
  | (lift-begin . _)
  | (lift-property . _)
  | (lift-methods . _)
 
  prop-expr : struct-type-property?
The grammar above describes the struct-type-def non-terminal from the grammars for define-element and define-elements-together, which produces the implementation of the TEI element struct type for the element. All of these sub-forms have meaning only at the runtime phase.

If a predicate-name-id is given, it is bound at the module level to a predicate function recognizing instances of the TEI element struct type being defined.

A #:property or #:methods clause works like the corresponding clause for struct, except that, in the prop-val-expr or methods-body forms, the get-field form may be used to access TEI element struct fields (discussed below) if any are defined.

A #:begin clause causes its begin-body forms to be spliced into the surrounding module-level context, but wraped to cooperate with get-field like prop-val-expr and methods-body forms.

The ctor-spec (when given) declares TEI element struct fields that are present on instances of the TEI element struct type and defines an initialization function to compute their values.

The ctor-spec may begin with ctor-arg-binding clauses, which bind names to the arguments needed to compute the values of the TEI element struct fields. The #:name, #:attributes, #:body, and #:body/elements-only keywords correspond to the functions tei-element-get-name, tei-element-get-attributes, tei-element-get-body, and (when applicable) tei-get-body/elements-only. Note that it is a syntax error for #:body/elements-only to appear if the #:contains-text contract-option was provided for the TEI element struct type. (See define-element.)

The #:this/thunk ctor-arg-binding clause is slightly different. When it is present, this/thunk-id is bound to a thunk that returns the TEI element struct instance being constructed (i.e. “this”). Internally, calling the thunk forces a thread-safe promise (see delay/sync) that returns the instance. Such a thunk cannot be used in a reentrant way, which would be the moral equivalent of use-before-definition, but it can immediately be used in background threads, such as to create TEI element struct fields that contain delay/thread promises.

After the ctor-arg-binding clauses, the ctor-body forms are a mixture of definitions, expressions, and special-ctor-form declarations, which are recognized after partial expansion. The ctor-body need not end with an expression. Definitions create local bindings as usual, but definitions of identifiers declared as TEI element struct fields with field or a related special-ctor-form declaration also specify the value for that field on the resulting TEI element struct instance. Expressions are evaluated for their side-effects: there is no “return value”. Other special-ctor-form declarations (such as lift-begin. lift-property, and lift-methods) provide hooks for implementing higher-level macros to be used in the ctor-body.

syntax

(field field-name-id field-option ...)

 
field-option = accessor-opt  |  print-opt  |  check-opt
     
accessor-opt = 
  | infer-opt
  | #:accessor accessor-id
     
infer-opt = #:infer
     
print-opt = 
  | #:print? bool-literal
  | #:hide
     
check-opt = 
  | #:check check-expr
 
  check-expr : contract?
The primitive special-ctor-form for declaring a TEI element struct field. Higher-level forms such as define/field and declare-resp-field are built on top of field.

A TEI element struct type has one immutable field for each field declaration among its expanded ctor-body forms (in addition to fields inherited from its abstract base type). The field declaration requires that field-name-id must be defined as a value (not syntax) by one of the plain-ctor-body forms: not only must it have a binding, but a binding from the surrounding context is not sufficient. After all of the plain-ctor-body forms have been evaluated, the TEI element struct instance is created using the values bound to the field-name-ids for the TEI element struct fields.

The field-name-ids are recognized with consideration to lexical context, rather than symbolically, so macros may freely introduce field declarations without fear of name collisions.

The field-option clauses, when given, provide some additional convieniences:
  • A check-expr specifies a contract on the value of the field. The expression is lifted to the module level and evaluated only once, and the contract is applied just before the TEI element struct instance is created.

  • If an accessor-id or infer-opt is given, a function that accesses the value of the field is bound in the context of the enclosing define-element form. If an accessor-id is given, it is used as the name of the defined function (including its lexical context). Otherwise, if infer-opt is given, an identifier is synthesized with the lexical context of field-name-id in the form name-id-field-name-id, where name-id is the name of the element currently being defined.

    A field with no accessor-id or infer-opt specified can still be accessed via the get-field form inside the text of a begin-body, prop-val-expr, or methods-body subform of define-element. Read on for details.

  • A print-opt clause, when given, controls whether the field is used for printing (as with print, write, and display). The field is not printed if #:hide appears or if #:print? is used with #f.

syntax

(get-field field-name-id)

(get-field field-name-id target-expr)
Inside the text of a begin-body, prop-val-expr, or methods-body subform of define-element, (get-field field-name-id) expands to a procedure that accesses the field field-name-id (which must have been declared with field) from an instance TEI element struct type being defined. The (get-field field-name-id target-expr) variant is short for ((get-field field-name-id) target-expr).

Like field, get-field is sensitive to the lexical context of field-name-id: get-field cooperates with define-element to locally bind field-name-id to a compile-time value in the contexts where get-field is permitted, but shaddowing the binding of field-name-id will render it inaccessable.

Macros can take advantage of field and get-field’s respect for scope to add fields to a TEI element struct type “hygenically”. See declare-resp-field for an example of a macro that uses these features. The lift-begin, lift-property, and lift-methods special-ctor-form declarations are designed to make it convienient for a macro to expand to both a field declaration and the use of the defined field.

syntax

(lift-property prop-expr prop-val-expr)

 
  prop-expr : struct-type-property?
A lift-property declaration is lifted out of the constructor definition to attach a structure type property to the TEI element struct type being defined, roughly as though it were #:property prop-expr prop-val-expr. Because it is lifted, bindings from the plain-ctor-body forms are not in scope in the prop-expr or prop-val-expr; however, in the prop-val-expr, get-field may be used to access TEI element struct fields.

syntax

(lift-methods generic-id
              [methods-body ...])
Like lift-property, but for a #:methods clause.

syntax

(lift-begin begin-body ...)

Like lift-property, but for a #:begin clause.

10.1.3.2 Field Definition Forms🔗ℹ

Several higher-level forms are included as alternatives to the primitive field declaration.

syntax

(define/field maybe-infer field/opts
  rhs ...)
 
maybe-infer = 
  | #:infer
     
field/opts = field-name-id
  | [field-name-id field-option ...]
Combines define with a field declaration, where field-option, if present, is the same as for field.

When the #:infer keyword is given for maybe-infer, #:infer is implicitly added before the first field-option.

Multiple rhs forms are implicitly wrapped with let if needed.

Note that define/field does not support the form of define with a function header on the left-hand side.

syntax

(define-fields maybe-infer [field/opts rhs ...] ...)

Like def, but based on define/field instead of define.

When the #:infer keyword is given for maybe-infer, it applies to all of the field/opts subforms as with define/field.

syntax

(define-values/fields maybe-infer (field/opts ...)
  rhs ...)
Like define/field, but based on define-values. The last rhs form must be an expression that produces as many values as there are field/opts forms.

When the #:infer keyword is given for maybe-infer, it applies to all of the field/opts subforms as with define/field.

10.1.3.3 Supporting Standard Interfaces🔗ℹ

syntax

(declare-resp-field option ...)

 
option = attributes-expr ; required
  | #:key key-id
 
  attributes-expr : (listof (list/c symbol? string-immutable/c))
Declares and defines a TEI element struct field (protected by lexical scope) and attaches assosciated properties to make the element being defined work with tei-element-resp. The attributes-expr should be the element’s attribute list, probably obtained via an #:attributes ctor-arg-binding clause in define-element.

If a key-id is given, it is used (in quoted form) instead of 'resp for the attribute name. If the specified attribute is present, its value must match the regular expression #rx"^#.+$".

The definition of a TEI element struct type can use prop:element->plain-text to override the default behavior of element-or-xexpr->plain-text. Note that attaching prop:element->plain-text to unrelated struct types has no effect: it is only used for TEI element structs.

The argument to the function given as the property value is always the TEI element struct instance to be converted to plain text.

syntax

(declare-paragraphs-status-field value-expr)

 
  value-expr : guess-paragraphs-status/c
Declares a field to contain the value that should ultimately be returned by tei-document-paragraphs-status and attaches an assosciated structure type property. The name of the field is protected by lexical scope. The details of this protocol are subject to change.

10.1.3.4 Implementing Additional Forms🔗ℹ

syntax

(field/derived orig-datum field-name-id field-option ...)

syntax

(get-field/derived orig-datum field-name-id)

(get-field/derived orig-datum field-name-id target-expr)

syntax

(lift-property/derived orig-datum prop-expr prop-val-expr)

 
  prop-expr : struct-type-property?

syntax

(lift-methods/derived orig-datum generic-id
                      [methods-body ...])

syntax

(lift-begin/derived orig-datum begin-body ...)

syntax

(define/field/derived orig-datum maybe-infer field/opts
  rhs ...)

syntax

(define-values/fields/derived orig-datum maybe-infer (field/opts ...)
  rhs ...)
Like the corresponding form without the /derived suffix, but with error reporting in terms of orig-datum.

syntax class

(field-name/maybe-opts orig-datum infer?)  syntax class

  orig-datum : syntax?
  infer? : #f
A syntax class recognizing the field/opts subform of define/field. The syntax class binds two attributes: name is bound to the field name identifier, and declaration is bound to a synthesized field/derived declaration for the field, using orig-datum for error reporting.

When infer? is non-false, the #:infer option is implicitly added as with define/field.

procedure

(local-element-name)  (or/c #f identifier?)

This function is provided for-syntax.

During the expansion of any sub-form of the struct-type-def non-terminal (see Struct Type Definition Overview), returns the name of the TEI element struct type being defined. In any other context, returns #false.

The #:infer option for field is implemented using local-element-name.