///|
priv struct DagreTraceKernel {
nodes : Array[@model.MermaidNode]
rank_by_id : Map[String, Int32]
max_rank : Int32
edges : Array[@model.MermaidEdge]
}
///|
fn main {
run()
}
///|
fn print_usage() -> Unit {
let usage =
#|Usage:
#| moon run cmd/dagre_trace -- --case case1
#| moon run cmd/dagre_trace -- --case case2
#| moon run cmd/dagre_trace -- --case case3
#| moon run cmd/dagre_trace -- --case all
#|Flags:
#| --case case1 | case2 | case3 | all
#| --help Show this help
#|
println(usage.trim_end().to_owned())
}
///|
fn cli_args_without_program() -> Array[String] {
let all_args = @env.args()
if all_args.length() <= 1 {
[]
} else {
let args : Array[String] = []
for i in 1.. String? {
if args.length() == 0 {
return Some("all")
}
if args.length() == 1 {
let arg = args[0]
if arg == "--help" {
return Some("help")
}
if arg is [.. "--case=", .. raw_case] {
let case_name = raw_case.trim().to_lower()
if case_name == "" {
return None
}
return Some(case_name.to_owned())
}
return None
}
if args.length() == 2 && args[0] == "--case" {
let case_name = args[1].trim().to_lower()
if case_name == "" {
return None
}
return Some(case_name.to_owned())
}
None
}
///|
fn bool_literal(value : Bool) -> String {
if value {
"true"
} else {
"false"
}
}
///|
fn sorted_int_keys_for_barycenters(
entries : Map[Int32, Map[String, String]],
) -> Array[Int32] {
let keys : Array[Int32] = []
for rank, _ in entries {
keys.push(rank)
}
keys.sort()
keys
}
///|
fn sorted_string_keys_for_barycenter_rank(
entries : Map[String, String],
) -> Array[String] {
let keys : Array[String] = []
for node_id, _ in entries {
keys.push(node_id)
}
keys.sort()
keys
}
///|
fn dump_trace(
trace : @layout_engine_graph_lane_core.DagreParityOrderTrace,
) -> Unit {
for trace_index, rank_nodes in trace.rank_nodes_trace {
for rank, node_ids in rank_nodes {
let joined_node_ids = node_ids.join(",")
println("TRACE\t\{trace_index}\t\{rank}\t\{joined_node_ids}")
}
}
for step in trace.sweep_step_trace {
println(
"PASS\t\{step.pass_index}\t\{bool_literal(step.incoming_from_neighbor)}\t\{bool_literal(step.bias_right)}\t\{step.candidate_crossings}",
)
for rank, node_ids in step.rank_nodes {
let joined_node_ids = node_ids.join(",")
println("PASS_LAYER\t\{step.pass_index}\t\{rank}\t\{joined_node_ids}")
}
for rank in sorted_int_keys_for_barycenters(step.barycenters_by_rank) {
match step.barycenters_by_rank.get(rank) {
Some(entries) =>
for node_id in sorted_string_keys_for_barycenter_rank(entries) {
match entries.get(node_id) {
Some(value) =>
println(
"BARY\t\{step.pass_index}\t\{rank}\t\{node_id}\t\{value}",
)
None => ()
}
}
None => ()
}
}
}
}
///|
fn flow_node(id : String) -> @model.MermaidNode {
{ id, label: id, shape: Rectangle, }
}
///|
fn flow_edge(source : String, target : String) -> @model.MermaidEdge {
{
source,
target,
label: None,
style: Solid,
has_arrow_start: false,
has_arrow_end: true,
relation_operator: Some("-->"),
}
}
///|
fn case1_kernel() -> DagreTraceKernel {
{
nodes: [
flow_node("A"),
flow_node("B"),
flow_node("M"),
flow_node("X"),
flow_node("Y"),
flow_node("__lane_dummy_f_4_1"),
flow_node("__lane_dummy_f_5_1"),
],
rank_by_id: {
"A": 0,
"B": 0,
"M": 1,
"X": 2,
"Y": 2,
"__lane_dummy_f_4_1": 1,
"__lane_dummy_f_5_1": 1,
},
max_rank: 2,
edges: [
flow_edge("A", "M"),
flow_edge("B", "M"),
flow_edge("M", "X"),
flow_edge("M", "Y"),
flow_edge("A", "__lane_dummy_f_4_1"),
flow_edge("__lane_dummy_f_4_1", "Y"),
flow_edge("B", "__lane_dummy_f_5_1"),
flow_edge("__lane_dummy_f_5_1", "X"),
],
}
}
///|
fn case2_kernel() -> DagreTraceKernel {
{
nodes: [
flow_node("A"),
flow_node("B"),
flow_node("D"),
flow_node("__lane_dummy_f_2_1"),
flow_node("__lane_dummy_f_6_1"),
flow_node("C"),
flow_node("__lane_dummy_f_3_1"),
flow_node("__lane_dummy_f_7_1"),
flow_node("E"),
flow_node("F"),
],
rank_by_id: {
"A": 0,
"B": 0,
"D": 1,
"__lane_dummy_f_2_1": 1,
"__lane_dummy_f_6_1": 1,
"C": 1,
"__lane_dummy_f_3_1": 1,
"__lane_dummy_f_7_1": 1,
"E": 2,
"F": 2,
},
max_rank: 2,
edges: [
flow_edge("A", "D"),
flow_edge("B", "C"),
flow_edge("A", "__lane_dummy_f_2_1"),
flow_edge("__lane_dummy_f_2_1", "E"),
flow_edge("B", "__lane_dummy_f_3_1"),
flow_edge("__lane_dummy_f_3_1", "F"),
flow_edge("C", "F"),
flow_edge("D", "E"),
flow_edge("A", "__lane_dummy_f_6_1"),
flow_edge("__lane_dummy_f_6_1", "F"),
flow_edge("B", "__lane_dummy_f_7_1"),
flow_edge("__lane_dummy_f_7_1", "E"),
],
}
}
///|
fn case3_kernel() -> DagreTraceKernel {
{
nodes: [
flow_node("A"),
flow_node("B"),
flow_node("C"),
flow_node("D"),
flow_node("E"),
flow_node("F"),
flow_node("G"),
flow_node("H"),
],
rank_by_id: {
"A": 0,
"B": 0,
"C": 1,
"D": 1,
"E": 2,
"F": 2,
"G": 3,
"H": 3,
},
max_rank: 3,
edges: [
flow_edge("A", "C"),
flow_edge("A", "D"),
flow_edge("B", "C"),
flow_edge("B", "D"),
flow_edge("C", "E"),
flow_edge("D", "F"),
flow_edge("C", "F"),
flow_edge("D", "E"),
flow_edge("E", "G"),
flow_edge("F", "H"),
flow_edge("E", "H"),
flow_edge("F", "G"),
],
}
}
///|
fn dump_case_trace(case_name : String) -> Bool {
let kernel = if case_name == "case1" {
Some(case1_kernel())
} else if case_name == "case2" {
Some(case2_kernel())
} else if case_name == "case3" {
Some(case3_kernel())
} else {
None
}
match kernel {
Some(found) => {
let trace = @layout_engine_graph_lane_core.collect_dagre_parity_order_trace(
found.nodes,
found.rank_by_id,
found.max_rank,
found.edges,
)
dump_trace(trace)
true
}
None => false
}
}
///|
fn run() -> Unit {
let args = cli_args_without_program()
match parse_case_arg(args) {
Some(case_name) =>
if case_name == "help" {
print_usage()
} else if case_name == "all" {
println("CASE\tcase1")
let _ = dump_case_trace("case1")
println("CASE\tcase2")
let _ = dump_case_trace("case2")
println("CASE\tcase3")
let _ = dump_case_trace("case3")
} else if !dump_case_trace(case_name) {
println("Error: unknown case '\{case_name}'")
print_usage()
}
None => {
println("Error: invalid arguments")
print_usage()
}
}
}