Adding Formats
=begin rakudoc
Overview
Formats convert between external data representations and Qwiratry-visible data. When a format produces custom tree nodes, add a tree navigator as a separate extension point.
Use this extension point when the new thing is a representation such as JSON, CSV, XML, YAML, TOML, binary encodings, or another parse/render representation. Do not use it for databases, APIs, filesystems, or other live sources and destinations; those belong in source/destination extensions.
To add support for a new format:
1. Setup: choose the format name, module path, and
META6.jsonentry.2. Parser: implement a parser if Qwiratry should receive that representation.
3. Renderer: implement a renderer if Qwiratry should output that representation.
4. Navigator: optionally add a tree navigator if the parsed data outputs custom tree nodes.
5. Testing: test discovery, parsing, rendering, navigation, and missing-format errors.
The current base parser receives a Str because the existing formats are
text-like, but the extension model is about data representations in general.
Future base classes or overloads may support binary blobs or other non-text
payloads.
Setup
Choose The Format Name
Choose the format name that callers will use, such as JSON, CSV, or
CBOR. Format names are normalized by Qwiratry::Format, so callers may use
labels like json while the module remains named JSON.
Add The Module
Add a module under lib/Qwiratry/Format/ named for that format.
A format module is named:
Qwiratry::Format::<FORMAT>
It defines one or more operation classes:
Qwiratry::Format::<FORMAT>::Parse
Qwiratry::Format::<FORMAT>::Render
For example, the built-in demonstration formats use *demo names to make it
clear that they are not complete production parsers:
Qwiratry::Format::JSONdemo::Parse
Qwiratry::Format::JSONdemo::Render
Formats that parse into ordinary Raku data (eg. Positional/Associative) usually do not need a tree navigator.
Update META6
Add the format module to META6.json provides so discovery can load it.
"Qwiratry::Format::Example": "lib/Qwiratry/Format/Example.rakumod"
Parser
Parser Contract
Qwiratry::Format::Base::Parse is the base class for parsers. A custom parser
inherits from this and currently implements parse(Str $input-string -- Mu)>.
Minimal Parser Example
use Qwiratry::Format::Base;
class Qwiratry::Format::Example::Parse is Qwiratry::Format::Base::Parse {
method parse(Str $input-data --> Mu) {
%(raw => $input-data)
}
}
Renderer
Renderer Contract
Qwiratry::Format::Base::Render is the base class for renderers. A custom
renderer inherits from this and implements
render(Mu $data, Associative :%options -- Str)>.
Minimal Renderer Example
use Qwiratry::Format::Base;
class Qwiratry::Format::Example::Render is Qwiratry::Format::Base::Render {
method render(Mu $data, Associative :%options --> Str) {
$data.raku
}
}
Tree Navigator
A format may need a Qwiratry::Tree::Navigator::<NAVIGATOR> module when its
parser emits a custom tree-shaped object model.
Navigators are documented in Adding Tree Navigators. Use a navigator when the generic tree walker is still the right traversal engine, but Qwiratry needs format-specific child, parent, or attribute access.
A tree navigator does not need a matching Parse or Render class. For
example, RakuAST is represented only as
Qwiratry::Tree::Navigator::RakuAST because the Raku compiler owns parsing and
rendering.
Testing
Add tests that prove:
Qwiratry::Format.formats(:type<Parse>)discovers the parser.Qwiratry::Format.formats(:type<Render>)discovers the renderer.Qwiratry::Format.make(:type<Parse>, :format<FORMAT>)returns a parser.Qwiratry::Format.make(:type<Render>, :format<FORMAT>)returns a renderer.Qwiratry::Tree::Navigator.navigatorsdiscovers any navigator needed for custom tree nodes.Parse operators accept the new format.
Render operators accept the new format.
Missing formats throw
X::Qwiratry::Format::NotFound.
Reference Material
Main Types
Qwiratry::Format
Factory and discovery service for parse and render implementations.
Qwiratry::Format::Base::Parse
Base class for parsers. A custom parser inherits from this and currently implements
parse(Str $input-string -- Mu)>.
Qwiratry::Format::Base::Render
Base class for renderers. A custom renderer inherits from this and implements
render(Mu $data, Associative :%options -- Str)>.
Qwiratry::Tree::Navigator::Base
Role for tree navigators. A custom navigator does this and implements the required tree access methods.
X::Qwiratry::Format::NotFound
Thrown when parse or render operators request a format that cannot be found.
Discovery
Qwiratry::Format discovers modules under Qwiratry::Format::* and checks
whether the requested operation class inherits from the matching base class.
my @parse-formats = Qwiratry::Format.formats(:type<Parse>);
my @render-formats = Qwiratry::Format.formats(:type<Render>);
my $parser = Qwiratry::Format.make(:type<Parse>, :format<JSONdemo>);
my $renderer = Qwiratry::Format.make(:type<Render>, :format<JSONdemo>);
=end rakudoc