PlantUML for diagrams.mbt: native activity, regex, EBNF, JSON and YAML readers, writers and conversion to diagram models
| Block | Model | Notes |
|---|---|---|
| @startregex | @regex.Regex | One pattern line. Structure only; no input matching. |
| @startebnf | @ebnf.Grammar | Ordered rules and notes. References stay symbolic. |
| @startjson | @data_tree.DataTree | Exact number lexemes, ordered and repeated members. |
| @startyaml | @data_tree.DataTree | The native PlantUML YAML subset. Every scalar is text. |
///|
test {
let source = "@startregex\ntitle Year\n(?<year>\\d{4})-\\d{2}\n@endregex\n"
let read = @plantuml.read(source)
assert_eq(read.tree.text(), source)
assert_eq(read.diagnostics.length(), 0)
let document = read.document.unwrap()
assert_eq(
@plantuml.print(document).output,
Some("@startregex\ntitle Year\n(?<year>\\d{4})-\\d{2}\n@endregex\n"),
)
guard @plantuml.to_diagram(document).diagram is Some(Regex(model)) else {
fail("regex")
}
assert_eq(model.title, Some("Year"))
}///|
test {
let body = @plantuml.read_body(
Ebnf,
"expr = term, { (\"+\" | \"-\"), term };",
)
assert_eq(body.diagnostics.length(), 0)
let printed = @plantuml.print_body(body.document.unwrap())
assert_eq(printed.output, Some("expr = term, { (\"+\" | \"-\"), term };\n"))
}| Input | Code |
|---|---|
| Two blocks, or text before or after the block | plantuml/multiple-blocks, plantuml/text-outside-block |
| A wrong end marker, such as @endyaml for @startjson | plantuml/wrong-end-marker |
| !include, !define, !theme and other preprocessor directives | plantuml/unsupported-directive |
| A preprocessor call such as %date() | plantuml/unsupported-preprocessing |
| A regex with more than one pattern line | regex/multiple-patterns |
| An EBNF rule that PlantUML draws as "Syntax error!" | ebnf/syntax |
| Malformed JSON | json/... |
| A YAML form that the pinned parser refuses, such as a folded > value | yaml/... |
///|
test {
let read = @plantuml.read(
"@startregex\n!include other.puml\nabc\n@endregex\n",
)
assert_true(read.document is None)
assert_eq(read.diagnostics[0].code, "plantuml/unsupported-directive")
}///|
test {
let source = "@startjson\n{\"id\": 12345678901234567890, \"id\": -0.0}\n@endjson\n"
let document = @plantuml.read(source).document.unwrap()
guard @plantuml.to_diagram(document).diagram is Some(DataTree(tree)) else {
fail("json")
}
let numbers = tree.nodes
.iter()
.filter_map(n => {
match n.value {
Number(lexeme) => Some(lexeme)
_ => None
}
})
.collect()
assert_eq(numbers, ["12345678901234567890", "-0.0"])
}///|
test {
let rendered = @plantuml.render_svg(
"@startyaml\nname: test\nlist:\n - a\n - b\n@endyaml\n",
renderer=@svg.SvgRenderer::new(mode=Fixed(Light)),
)
assert_true(rendered.output.unwrap().contains("data-diagram=\"data-tree\""))
}///|
/// A scripted stub: it supports regex requests and reports a failure at
/// the first term. It is an example of the contract, not a matcher.
struct FirstTermFails {}
///|
impl @trace.RailroadTraceProvider for FirstTermFails with fn trace(_, request) {
guard request.subject is Regex(_) else { return Unsupported("regex only") }
let topology = request.topology().topology.unwrap()
let track = topology.tracks[0]
let first = topology.terms[0]
NotMatched(
@trace.Trace::new(request, "first-term-fails", [
@trace.Attempt::new(
"a0",
NotMatched,
[Traverse(topology.connector_of(track.id, Start).unwrap().id, false)],
failure=@trace.FailurePoint::new(Terminal(first.id), position=0),
),
]),
)
}
///|
test {
let result = @plantuml.render_traced_svg(
"@startregex\nab|cd\n@endregex\n",
FirstTermFails::{ },
"x",
)
assert_true(result.run.result is Some(NotMatched(_)))
assert_true(result.rendered.output.unwrap().contains("rr-trace-failure"))
let grammar = @plantuml.render_traced_svg(
"@startebnf\nr = \"x\";\n@endebnf\n",
FirstTermFails::{ },
"x",
start_rule="r",
)
assert_true(grammar.run.result is Some(Unsupported(_)))
assert_true(grammar.rendered.output is None)
}///|
test "activity source prints and converts to a flowchart" {
let source = "@startuml\nstart\n:Read;\n:Read;\nstop\n@enduml\n"
let result = @plantuml.read(source)
assert_eq(result.tree.text(), source)
assert_eq(result.diagnostics, [])
let document = result.document.unwrap()
guard document is Activity(activity) else { fail("Expected activity") }
assert_eq(activity.dialect, Modern)
let printed = @plantuml.print(document)
assert_eq(printed.diagnostics, [])
let model = @plantuml.to_diagram(document)
assert_eq(model.diagnostics, [])
guard model.diagram is Some(Flowchart(flowchart)) else {
fail("Expected flowchart")
}
assert_eq(flowchart.nodes.length(), 4)
assert_true(flowchart.nodes[1].id != flowchart.nodes[2].id)
}///|
test "legacy activity body requires an explicit dialect" {
let result = @plantuml.read_body(
Activity,
"(*) --> \"Read\" as R\nR --> (*)\n",
dialect=Legacy,
)
assert_eq(result.diagnostics, [])
let model = @plantuml.to_diagram(result.document.unwrap())
assert_true(model.diagram is Some(Flowchart(_)))
}pub struct PrintResult {
output : String?
diagnostics : ReadOnlyArray[Diagnostic]
} derive(Eq, Debug)let diagnostic_codes : ReadOnlyArray[String]let plantuml_version : Stringfn[P : RailroadTraceProvider] render_traced_svg(source : String, provider : P, input : String, start_rule? : String, renderer? : SvgRenderer, limits? : Limits, trace_limits? : TraceLimits, matched? : ChannelStyle, not_matched? : ChannelStyle, taken? : ChannelStyle, failure? : ChannelStyle) -> TracedRenderInstall
Download zipPlantUML for diagrams.mbt: native activity, regex, EBNF, JSON and YAML readers, writers and conversion to diagram models