On this page:
space.enforest
space.transform
space_  path
macro_  definer
bridge_  definer
meta_  namespace
parse_  syntax_  class
parse_  prefix_  more_  syntax_  class
parse_  infix_  more_  syntax_  class
identifier_  parser
parse_  checker
parsed_  packer
parsed_  unpacker
description
operator_  description
reflection
Space  Meta
8.12

7.2 Spaces🔗ℹ

An identifier can have different meanings in different contexts, such as expression versus binding, because an identifier can be bound in a specific space. Example spaces include expr and bind, which generally correspond to a namespace that provides a binding form for the space. Binding forms like def, expr.macro, and bind.macro bind an identifier in the corresponding space.

Forms that bind in different spaces can be used on the same name to give that name a meaning in multiple contexts. The class form binds the class name in many spaces at once, so that a class name works as a constructor in expressions, as an attribute, as a pattern form in bindings, and so on. New bindings in new spaces can always be added alongside existing bindings. The import form supports space-specific operations through the only_space and except_space modifiers, so existing bindings can be suppressed and then, perhaps, replaced on re-export.

Most contexts have their own spaces, even though some of them also overlap with expression positions, such as definitions, declarations, class clauses, and for clauses. The parsing process for such overlapping spaces will check non-expression spaces for bindings, first. The space for expression is special in another way: a binding in that space hides any binding for another space in an enclosing scope (but not bindings in other spaces in the same scope).

The space.enforest and space.transform forms create a new space along with its associated parser driver and macro-definitions forms.

Defines space_id as a space with syntax classes and macro-definition forms that reference and bind in the space specified by space_path. The space_id_path declared with space_path should be globally unique, typically based on the module path of the enclosing module, but the intent is that this identifying path is normally referenced as space_id or through the identifier bound by reflection.

Besides being defined as a space, space_id is defined as a namespace. Among the space_clause_or_body_or_exports, nestable_body forms can add definitions and exports to the namespace, the same as for namespace. However, the namespace is particularly intended to export the name specified by macro_definer. That name is conventionally macro. As a somewhat lower-level mechanism, bridge_definer exports a name for binding arbitrary compile-time values analogous to meta.bridge. If macro_definer and bridge_definer are not declared, then there is no way to bind in the new namespace except by using lower-level mechanisms.

Also typically among the space_clause_or_body_or_exports, a meta_namespace declares the name of a compile-time namespace, typically used in macros. The meta namespace’s name is conventionally _meta appended to the end of the main namespace’s name. The space_meta_clause_or_body forms in the body of a meta_namespace clause are implicitly shifted to compile time, as if wrapped by meta. The meta namespace is particularly intended to export syntax classes with names specified by parse_syntax_class, parse_prefix_more_syntax_class, and parse_infix_more_syntax_class clauses. If no name is declared with parse_syntax_class or similar, then there is no way to parse terms in the new space except by lower-level mechanisms.

All together, a typical use of space.enforest includes at least space_path, macro_definer, and meta_namespace containing parse_syntax_class.

space.enforest new_thing:

  space_path my_collection/new_thing

  macro_definer macro

  meta_namespace new_thing_meta:

    parse_syntax_class Parsed

These pieces might be used by an expression macro for a form that has a “new thing” position, while an operator is meanwhile defined to work in the “new thing” space.

expr.macro 'print_new_thing: $(thing :: new_thing_meta.Parsed)':

  'println($thing)'

new_thing.macro 'the':

  '"the new thing"'

> print_new_thing: the

the new thing

The identifier supplied for parse_syntax_class is defined as a ~group syntax class with a group field. The syntax class triggers parsing of a group using macros bindings, which are defined using the identifier bound by macro_definer. For a pattern variable using the syntax class bound by parse_syntax_class, its value is the result of parsing, while the group field holds the terms that were parsed. No constraints are imposed on the result of parsing, except that it must be represented as a syntax object.

When just macro_definer and parse_syntax_class are declared, then each macro effectively must produce a fully parsed term. To enable macros in the space that expand to uses of other macros in the space, a distinction is needed between fully parsed and still-to-be-expanded terms. Use parsed_packer and parsed_unpacker to introduce that distinction, use the packer name in macros that produce fully parsed terms, and use the unpacker name to access parsed content. Meanwhile, the syntax classes bound by parse_syntax_class and similar recognize terms constructed via parsed_packer as already parsed.

space.enforest newer_thing:

  space_path my_collection/new_thing

  macro_definer macro

  meta_namespace newer_thing_meta:

    parse_syntax_class Parsed

    parsed_packer pack

    parsed_unpacker unpack

expr.macro 'print_newer_thing: $(thing :: newer_thing_meta.Parsed)':

  'println($(newer_thing_meta.unpack(thing)))'

newer_thing.macro 'the':

  newer_thing_meta.pack('"the newer thing"')

newer_thing.macro 'THE':

  'the'

> print_newer_thing: the

the newer thing

> print_newer_thing: THE

the newer thing

When a parse_checker clause is supplied, then it can impose a check and/or conversion on the result for every macro in the space. That conversion should include recursively expanding when the result is not yet fully expanded, which can be detected by supplying a second argument to the function bound by parsed_unpacker. The default parse checker performs this recursive parsing step.

More details on space clauses and meta clauses:

Like space.enforest but for a simpler form of space that has only prefix-triggered forms with identifier names, like definition contexts.

A space.transform declaration does not support parse_prefix_more_syntax_class, parse_infix_more_syntax_class, identifier_parser, or operator_description clauses.

space clause

space_path space_id_path

 

space clause

macro_definer id

 

space clause

bridge_definer id

 

space clause

meta_namespace meta_namespace_id:

  space_meta_clause_or_body

  ...

Clause forms for use within a space.enforest or space.transform form. See space.enforest for more information.

space meta clause

parse_syntax_class id

 

space meta clause

parse_prefix_more_syntax_class id

 

space meta clause

parse_infix_more_syntax_class id

 

space meta clause

identifier_parser expr

 

space meta clause

parse_checker expr

 

space meta clause

parsed_packer id

 

space meta clause

parsed_unpacker id

 

space meta clause

description expr

 

space meta clause

operator_description expr

 

space meta clause

reflection id

Provided as meta.

Clause forms for use within a meta_namespace clause within a space.enforest or space.transform form. See space.enforest for more information.

annotation

SpaceMeta

Provided as meta.

A SpaceMeta compile-time value reflects a space that would be referenced in a run-time position by the space name. For example, expr_meta.space for use with a compile-time function like syntax_meta.value refers to the same space as expr as used with only_space.