Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 67 additions & 26 deletions OVERVIEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ In this way, Bauble is a format very useful for specifying information processed

Once a context has been created, that context can then be used for parsing various Bauble source files and extracting the newly parsed data back as typed values that can be used to update the state of the program.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new documentation in this file would probably benefit from being the focus of review efforts.

# BaubleContext
# `BaubleContext`

[`BaubleContext`] is used to register various Bauble source files to parse information from, as well as maintaining a type registry where every known type to Bauble is provided.
Through [`BaubleContext`], various separate Bauble source files are able to reference each other's objects.
Expand Down Expand Up @@ -52,34 +52,63 @@ Whether the conversion is successful depends on the implementation of the [`Baub
In order to support all Bauble values being parsed and represented as a single type in Rust, using type erasure is a possibility, however it has to be manually implemented.
For example, a single `ErasedBauble` type which can represent any type that implements `Bauble`, but has to be explicitly downcast to the concrete type sometime later at runtime (when the type is known).

## Bauble overview
# Bauble overview

# Includes (use)
## Includes (use)

All Bauble source files support the usage of `use`.
Similar to Rust, it may include any item defined from a separate Bauble module, as long as both modules (the module being used, and the module using) exist within the same [`BaubleContext`].
Alternatively to `use`, like in Rust, the fully qualified path may be written out instead.
`use` may be used to include both objects and Bauble registered types/traits.

# Object
## Object

All bauble source files consists of a set of objets.
A single Bauble object is a tree of Bauble values and a type.
A Bauble object can be thought of as a single asset, being tied to a unique path.
Objects are the format of its contents Bauble provides after it has parsed all of its files, and ultimately what is used to convery the parsed Bauble contents.

# Values
### Object terminology

A value in Bauble is typed, and contains an enum representing the kind of the value (whether it is a number, a string, an array or a map for example).
There are three kinds of Bauble objects. Top level, local, and inline. These can be seen
represented in the [`ObjectPath`] enum.

A top level (or Top for short) object is the main kind of object. A single source file has a single
top level object which appears at the start of the file with the `0` identifier used as its name
(NOTE: completely empty files are also allowed). These objects have the same path as their
containing file. Top objects can be freely referenced by objects in other files. They can also be
referenced in the current file both by their full path and by the special `0` identifier.

Local objects are the remaining named objects in a file that appear after the top object. They can
only be referenced by other objects in the same file and only via their name rather than a full
path.

An inline object is a reference value within a Bauble object which is not written as a reference in
the source (see [References](#references)). Instead they appear as an "inlined" object directly
defined together with the object. Functionally, inline objects work similar to a regular reference
with the difference being they are defined locally to the object where they are used and are not
named separately referencable objects themselves. They act as a convenience for the alternative of
defining and referencing a separate object. The paths of these objects are generated based on the
parent object when loading a Bauble file.

In addition to referencing other Bauble objects, external assets can be referenced. These are
assets that aren't bauble files. For example, an audio or image asset. They can be exposed to be
referenced by bauble values via `BaubleContext::register_asset`. When an object references an
external asset it uses `ObjectPath::Top` wrapping the path provided to `register_asset `. When
something can be either a Bauble object or an external asset, we use the term "asset".

## Values

A value in Bauble is typed, and contains an enum representing the kind of the value (whether it is a number, a string, an array or a map for example).
Values are not that usually interacted with by the end user, as generally Bauble is implemented through the derive macro which means the user does not need to handle parsing from a raw Bauble value themselves.

# Types
## Types

Types are registered to the [`BaubleContext`] through the builder, and every type requires the [`Bauble`] trait to be implemented on top of it.
The [`Bauble`] trait determines how the type gets registered into the [`BaubleContext`], how it is parsed from Bauble source, and what path the type has.
In Bauble every object is associated with a type, the type may be explicitly written with the value or implied by context, similar to type inference rules in Rust.

# Traits
## Traits

Traits may be registered to Bauble in the form of `dyn Trait` types.
If a trait has been registered, `dyn Trait` can then be used within registered Bauble types. In order for Bauble to parse a value of type `dyn Trait` with a value of a type that implements `Trait` called `T`, both `Trait` the `T` must be registered, and Bauble must know `T` implements `Trait`.
Expand All @@ -88,30 +117,42 @@ Bauble is unable to use reflection and figure out if a type implements a trait b
In order to register Bauble traits, you can use the method [`get_or_register_trait`](types::TypeRegistry::get_or_register_trait), then in order to mark a type as implementing that trait in Bauble,
use [`add_trait_dependency`](types::TypeRegistry::add_trait_dependency)

# Modules
## Modules

Every registered source file in Bauble is a module, similar to Rust. Every module contains various objects. The notion of sub-modules are not really present in Bauble.

## Paths

Every registered source file in Bauble is a module, similar to Rust. Every module contains various assets. The notion of sub-modules are not really present in Bauble.
Every asset, module, reference and registered type/trait in Bauble has a corresponding unique path.
In Bauble this is known as [`path::TypePath`], and is the association to that particular element in
the [`BaubleContext`].
A path consists of various elements. Similar to Rust, most elements are seperated by `::`, so
`a::b` means element `b` which is a child of element `a`.
An element here can be a module, type, object, or external asset.

# Paths
References to Objects use [`object_path::ObjectPath`] which is an enum that augments
[`path::TypePath`] with information about the kind of object. Note, local and inline objects are
not known to the `BaubleContext` so referring to top objects in the `BaubleContext` just uses
`TypePath`.

Every asset, module, reference and registered type/trait in Bauble has a corresponding unique path. In Bauble this is known as [`path::TypePath`], and is the association to that particular element in the [`BaubleContext`].
A path consists of various elements. Similar to Rust, most elements are seperated by `::`, so `a::b` means element `b` which is a child of element `a`.
An element here can be a module, type or object.
There are things known as sub-objects which may be appended to the path of a regular object, which are effectively children of the current object.
A sub-object's path is denoted by `<path to parent>&$ty@$idx` where `$ty` is the index of the type of the sub-object and `$idx` is the index of the sub-object to the parent (the first sub-oject being 0, the second 1, the third 2, etc).
There are objects known as inline objects which are effectively children of the current object.
Their paths are constructed by appending to the name of a regular object to create a new object
path with a different name that shares the same path prefix.
An inline object's path is denoted by `<path to parent>&$ty@$num` where `$ty` ID of the type of the
inline object and `$num` is an aribtrary number that distinguishes different inline objects with
the same parent (usually starting at 0).

# References
## References

A reference is a Bauble object which may not be present in the current module.
A Bauble object's value may use a reference to avoid code duplication in the local file (referencing a previous object to use its values), or to reference the value of a Bauble object from a different file (module).
A reference is a value that points to another Bauble object or a registered external asset.
A Bauble object's value may use a reference to avoid code duplication in the local file
(referencing a previous object to use its values), or to reference the value of a Bauble object
from a different file (module).
References are specified using a bauble `Path`.

Bauble does expose the builtin type for references, [`Ref`], which can be used for convenience to represent references from Bauble in Rust.
It is not required to use this type to represent references, custom types which are capable of parsing reference values are equal to the builtin [`Ref`] type, it is just a convenience.
Bauble does expose the builtin type for references, [`Ref`], which can be used for convenience to
represent references from Bauble in Rust.
It is not required to use this type to represent references, custom types which are capable of
parsing reference values are equal to the builtin [`Ref`] type, it is just a convenience.

# Sub-objects

A single Bauble object may contain sub-objects.
A sub-object is a reference value within a Bauble object which is not written as a reference, and rather as an "inlined" object directly defined together with the object.
Functionally sub-objects work similar to a regular reference with the difference being they are defined locally to the object where they are used and are not
top level objects themselves.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Bauble has three steps in parsing.

## Examples of bauble
```rust
// bauble has the capability to use different types. Which are resolved by the `AssetCtx`
// bauble has the capability to use different types. Which are resolved by the `BaubleContext`
use rpg::{Enemy, DamageType};

slime = Enemy {
Expand Down
19 changes: 10 additions & 9 deletions bauble/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,9 @@ impl BaubleContextBuilder {
}

#[allow(missing_docs)]
pub fn set_top_level_trait_requirement<T: ?Sized + BaubleTrait>(&mut self) -> &mut Self {
pub fn set_object_trait_requirement<T: ?Sized + BaubleTrait>(&mut self) -> &mut Self {
let tr = self.registry.get_or_register_trait::<T>();
self.registry.set_top_level_trait_dependency(tr);
self.registry.set_object_trait_dependency(tr);

self
}
Expand Down Expand Up @@ -390,7 +390,7 @@ impl CtxNode {
!node.is_empty()
});

// Top level assets match the path of their file (and all assets registered in
// Top level objects match the path of their file (and all objects registered in
// BaubleContext are top level).
self.reference.asset.take();
}
Expand Down Expand Up @@ -605,27 +605,27 @@ impl BaubleContext {

let mut early_ctx = crate::value::EarlyContext::new(self);

// Register assets paths from each successfully parsed file into the early context (before
// Register object paths from each successfully parsed file into the early context (before
// types are known).
for (file, values) in file_values.iter() {
// Need a partial borrow here.
let (path, _) = early_ctx.ctx.file(*file);
let path = path.to_owned();
crate::value::pre_register_assets(&mut early_ctx, path.borrow(), values);
crate::value::pre_register_objects(&mut early_ctx, path.borrow(), values);
}

let mut local_ctx = crate::local_context::LocalContext::new();

let mut delayed = Vec::new();
let mut skip = Vec::new();

// Then, register assets from each successfully parsed file into the context (while
// Then, register objects from each successfully parsed file into the context (while
// resolving types).
for (file, values) in file_values.iter() {
// Need a partial borrow here.
let (path, _) = early_ctx.ctx.file(*file);
let path = path.to_owned();
match crate::value::register_assets(
match crate::value::register_objects(
&mut early_ctx,
&mut local_ctx,
path.borrow(),
Expand Down Expand Up @@ -774,8 +774,9 @@ impl BaubleContext {

/// Get all the assets starting from `path`, with an optional maximum depth of `max_depth`.
///
/// Inline objects are not registered as assets, they are exclusively visible in the list of
/// objects returned when loading/reloading files.
/// Only top level objects are registered as assets (in addition to external assets that are
/// manually registered for referencing by Bauble objects). Local and inline objects are
/// exclusively visible in the list of objects returned when loading/reloading files.
pub fn assets(
&self,
path: TypePath<&str>,
Expand Down
8 changes: 4 additions & 4 deletions bauble/src/object_path.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Bauble objects can either be top-level, local, or inline.
//! Bauble objects can either be top level, local, or inline.
//!
//! For a reference to an object, this is known once the full path to an
//! object is resolved during value loading. That resolved path is represented using
Expand All @@ -14,8 +14,8 @@
use crate::types::path::TypePath;
use std::hash::{Hash, Hasher};

/// Special cased identifier that is required and only allowed for the first asset in a file
/// (i.e. the top level asset that is named after the file).
/// Special cased identifier that is required and only allowed for the first object in a file
/// (i.e. the top level object that is named after the file).
pub const TOP_LEVEL_IDENTIFIER: &str = "0";

/// Full path to a bauble object (or external asset). See [the module level documentation](self)
Expand All @@ -39,7 +39,7 @@ pub const TOP_LEVEL_IDENTIFIER: &str = "0";
/// - `inline_ref` will be `ObjectPath::Inline("my_file::inline_ref&6@0")
#[derive(Copy, Clone, Debug)]
pub enum ObjectPath<S = String> {
/// Top-level object or external asset. There is at most one per file.
/// Top level object or external asset. There is at most one per file.
///
/// This shares the path of the containing file and uses the special identifier
/// [`TOP_LEVEL_IDENTIFIER`].
Expand Down
4 changes: 2 additions & 2 deletions bauble/src/parse/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,14 +179,14 @@ pub struct Binding {

#[derive(Debug, PartialEq, Eq, Hash)]
pub enum BindingIdent {
/// This is the top level asset in a parsed file.
/// This is the top level object in a parsed file.
///
/// It has the special cased identifier `0` and appears as the first item in the file.
///
/// This holds no identifier string because it will have the same path as the file containing
/// it.
TopLevel(Spanned<()>),
/// This is a local asset. I.e. any additional assets in a parsed file.
/// This is a local object. I.e. any additional objects in a parsed file.
Local(Ident),
}

Expand Down
36 changes: 19 additions & 17 deletions bauble/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ pub struct TypeRegistry {
type_from_rust: HashMap<std::any::TypeId, TypeId>,
to_be_assigned: HashSet<TypeId>,

top_level_trait_dependency: TraitId,
object_trait_dependency: TraitId,

primitive_types: [TypeId; 5],
}
Expand Down Expand Up @@ -269,8 +269,8 @@ impl TypeRegistry {

asset_refs: Default::default(),

// NOTE: Top level values always have to derive from this trait.
top_level_trait_dependency: Self::any_trait(),
// NOTE: Top level value in an object must always implement this trait.
object_trait_dependency: Self::any_trait(),

to_be_assigned: Default::default(),

Expand Down Expand Up @@ -304,14 +304,14 @@ impl TypeRegistry {
this
}

/// If a type implements the required top-level trait.
pub fn impls_top_level_trait(&self, id: TypeId) -> bool {
self.key_trait(self.top_level_trait_dependency).contains(id)
/// If a type implements the required trait for all objects.
pub fn impls_object_trait(&self, id: TypeId) -> bool {
self.key_trait(self.object_trait_dependency).contains(id)
}

/// The trait that's expected for all top-level bauble assets to have.
pub fn top_level_trait(&self) -> TraitId {
self.top_level_trait_dependency
/// The trait that's expected for all bauble objects to have.
pub fn object_trait(&self) -> TraitId {
self.object_trait_dependency
}

/// This is present in all `TypeRegistry`
Expand Down Expand Up @@ -574,7 +574,7 @@ impl TypeRegistry {
// If the path is not writable then it cannot be validated
// as it cannot be written out as Bauble source.
|| !ty.meta.path.is_representable_type()
|| !ty.meta.traits.contains(&self.top_level_trait_dependency)
|| !ty.meta.traits.contains(&self.object_trait_dependency)
{
continue;
}
Expand Down Expand Up @@ -607,8 +607,8 @@ impl TypeRegistry {
// Check that instantiated objects match after being serialized to bauble text and
// parsed.
//
// Changes in sub-asset paths are specifically ignored, only the content of the
// sub-assets must match.
// Changes in inline object paths are specifically ignored, only the content of the
// inline objects must match.

// dummy top level object
let mut source = "0 = ()\n".to_string();
Expand Down Expand Up @@ -772,9 +772,10 @@ impl TypeRegistry {
}
}

/// Sets the trait all top-level assets are expected to have. By default this is the any trait.
pub fn set_top_level_trait_dependency(&mut self, tr: TraitId) {
self.top_level_trait_dependency = tr;
/// Sets the trait all types used for objects are expected to have. By default this is the any
/// trait.
pub fn set_object_trait_dependency(&mut self, tr: TraitId) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "top level" here referred to the value at the top of the value tree within a bauble object. I renamed this to avoid confusion with top level objects.

self.object_trait_dependency = tr;
}

/// Registers `ty` as implementing `tr`.
Expand Down Expand Up @@ -1039,8 +1040,9 @@ pub type ValidationFunction =

/// Function that creates a instance of the default value. Stored in [`TypeMeta`].
///
/// * `&mut AdditionalUnspannedObjects` allows creating sub-assets if the new value needs to
/// reference sub-assets.
/// * `&mut AdditionalUnspannedObjects` allows creating objects if the new value needs to
/// reference additional objects (depending on configuration of the `AdditionalUnspannedObjects`
/// these can be created as inline objects or as new local objects).
/// * `&TypeRegistry` allows calling [`TypeRegistry::instantiate`] to create new default instances
/// of contained types and is used to get type information of contained types.
/// * `TypeId` is the ID of the type. This allows the function to retrieve information about the
Expand Down
Loading
Loading