From c58597389dc848958980ec3954d7db8da9f02326 Mon Sep 17 00:00:00 2001 From: elvis Date: Wed, 20 Aug 2025 19:51:03 +0200 Subject: [PATCH] Better error hanlding when parsing, added medical system fixed grammar --- Cargo.toml | 2 +- src/main.rs | 2 +- src/rsprocess/assert/rsassert.rs | 8 +- src/rsprocess/grammar.lalrpop | 146 +++++++++++++++++--- src/rsprocess/presets.rs | 94 ++++++++++--- testing/medical.system | 225 +++++++++++++++++++++++++++++++ 6 files changed, 432 insertions(+), 45 deletions(-) create mode 100644 testing/medical.system diff --git a/Cargo.toml b/Cargo.toml index 9a3c4b9..f3e5412 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,10 +8,10 @@ lalrpop = "0.22" [dependencies] rand = { version = "*" } +colored = { version = "*" } regex = { version = "1", features = ["unicode-bool"] } lalrpop-util = { version = "*", features = ["lexer", "unicode"] } petgraph = { version = "*", features = ["serde-1"] } -# TODO remove git and use crates.io version when updated petgraph-graphml = { version = "5" } serde = { version = "1", features = ["derive", "rc"] } serde_cbor_2 = { version = "*" } diff --git a/src/main.rs b/src/main.rs index c2df342..45adea1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ fn main() { let now = std::time::Instant::now(); use reactionsystems::rsprocess::presets; - match presets::run("testing/first.system".into()) { + match presets::run("testing/medical.system".into()) { Ok(()) => {}, Err(e) => {println!("{e}")} } diff --git a/src/rsprocess/assert/rsassert.rs b/src/rsprocess/assert/rsassert.rs index 5c668a5..6dd0789 100644 --- a/src/rsprocess/assert/rsassert.rs +++ b/src/rsprocess/assert/rsassert.rs @@ -112,8 +112,8 @@ impl RSassert { AssertionTypes::Context => Ok(()), AssertionTypes::NoType => - Err(format!("No return type, at least one return statement \ - required.")), + Err("No return type, at least one return statement \ + required.".into()), AssertionTypes::RangeInteger | AssertionTypes::RangeSet | AssertionTypes::RangeNeighbours => @@ -228,8 +228,8 @@ impl RSassert { AssertionTypes::Context => Ok(()), AssertionTypes::NoType => - Err(format!("No return type, at least one return statement \ - required.")), + Err("No return type, at least one return statement \ + required.".into()), AssertionTypes::RangeInteger | AssertionTypes::RangeSet | AssertionTypes::RangeNeighbours => diff --git a/src/rsprocess/grammar.lalrpop b/src/rsprocess/grammar.lalrpop index b3d54fb..105b739 100644 --- a/src/rsprocess/grammar.lalrpop +++ b/src/rsprocess/grammar.lalrpop @@ -44,15 +44,16 @@ match { "Products", "MaskProducts", "UncommonProducts", "UncommonMaskProducts", "Union", "MaskUnion", "UncommonUnion", "UncommonMaskUnion", "Difference", "MaskDifference", - "UncommonDifference", "UncommonMaskDifference", + "UncommonDifference", "UncommonMaskDifference", "EntitiesDeleted", "MaskEntitiesDeleted", "UncommonEntitiesDeleted", "UncommonMaskEntitiesDeleted", "EntitiesAdded", "MaskEntitiesAdded", "UncommonEntitiesAdded", "UncommonMaskEntitiesAdded", "label", "edge", "if", "then", "else", "let", "=", "return", "for", "in", - "not", "rand", ".empty", ".length", ".tostr", ".source", ".target", + "not", "rand", "empty", "length", "tostr", "source", "target", "&&", "||", "^^", "<=", ">=", "==", "!=", "+", "*", "/", "%", - "::", "substr", "min", "max", "commonsubstr", + "::", + "substr", "min", "max", "commonsubstr", "SystemEntities", "SystemContext", "AvailableEntities", "AllReactants", "AllInhibitors", "relabel", @@ -67,16 +68,96 @@ match { _ } - // matches words (letter followed by numbers, letters or _) Literal: String = { - WORD => <>.to_string() + WORD => <>.into(), +}; + +LiteralProcess: String = { + Literal => <>, + "true" => <>.into(), + "false" => <>.into(), + "Environment" => <>.into(), + "Initial" => <>.into(), + "Context" => <>.into(), + "Reactions" => <>.into(), + "Weights" => <>.into(), + "Sets" => <>.into(), + "Print" => <>.into(), + "Save" => <>.into(), + "Dot" => <>.into(), + "GraphML" => <>.into(), + "Serialize" => <>.into(), + "Stats" => <>.into(), + "Target" => <>.into(), + "Run" => <>.into(), + "Loop" => <>.into(), + "Frequency" => <>.into(), + "LimitFrequency" => <>.into(), + "FastFrequency" => <>.into(), + "Digraph" => <>.into(), + "Bisimilarity" => <>.into(), + "Deserialize" => <>.into(), + "Hide" => <>.into(), + "Entities" => <>.into(), + "MaskEntities" => <>.into(), + "UncommonEntities" => <>.into(), + "UncommonMaskEntities" => <>.into(), + "MaskContext" => <>.into(), + "UncommonContext" => <>.into(), + "UncommonMaskContext" => <>.into(), + "Products" => <>.into(), + "MaskProducts" => <>.into(), + "UncommonProducts" => <>.into(), + "UncommonMaskProducts" => <>.into(), + "Union" => <>.into(), + "MaskUnion" => <>.into(), + "UncommonUnion" => <>.into(), + "UncommonMaskUnion" => <>.into(), + "Difference" => <>.into(), + "MaskDifference" => <>.into(), + "UncommonDifference" => <>.into(), + "UncommonMaskDifference" => <>.into(), + "EntitiesDeleted" => <>.into(), + "MaskEntitiesDeleted" => <>.into(), + "UncommonEntitiesDeleted" => <>.into(), + "UncommonMaskEntitiesDeleted" => <>.into(), + "EntitiesAdded" => <>.into(), + "MaskEntitiesAdded" => <>.into(), + "UncommonEntitiesAdded" => <>.into(), + "UncommonMaskEntitiesAdded" => <>.into(), + "label" => <>.into(), + "edge" => <>.into(), + "if" => <>.into(), + "then" => <>.into(), + "else" => <>.into(), + "let" => <>.into(), + "return" => <>.into(), + "for" => <>.into(), + "in" => <>.into(), + "not" => <>.into(), + "rand" => <>.into(), + "empty" => <>.into(), + "length" => <>.into(), + "tostr" => <>.into(), + "source" => <>.into(), + "target" => <>.into(), + "substr" => <>.into(), + "min" => <>.into(), + "max" => <>.into(), + "commonsubstr" => <>.into(), + "SystemEntities" => <>.into(), + "SystemContext" => <>.into(), + "AvailableEntities" => <>.into(), + "AllReactants" => <>.into(), + "AllInhibitors" => <>.into(), + "relabel" => <>.into(), }; // all numbers are i64 Num: i64 = { NUMBER =>? i64::from_str(<>) - .map_err(|_| ParseError::User { error: "Number is too big" }) + .map_err(|_| ParseError::User { error: "Number is too big" }) }; Path: String = { @@ -124,7 +205,10 @@ pub Reactions: Vec = { } Reaction: RSreaction = { + #[precedence(level="1")] "[" "," "," "]" => RSreaction::from(r, i, p), + + #[precedence(level="0")] "[" "r:" "," "i:" "," "p:" "]" => RSreaction::from(r, i, p), } @@ -145,18 +229,26 @@ Boxed_CTX_process: Rc = { CTX_process: RSprocess = { "nill" => RSprocess::Nill, + "." => RSprocess::EntitySet{ entities: c, next_process: Rc::new(k) }, + "(" ")" => k, + "(" > ")" => RSprocess::Summation{ children: k.into_iter().map(Rc::new).collect::>() }, - "<" ">" "." => + + "?" "?" "." => + RSprocess::Guarded{ reaction: r, next_process: Rc::new(k) }, + + "<" "," ">" "." => RSprocess::WaitEntity{ repeat: n, repeated_process: Rc::new(k1), next_process: Rc::new(k) }, - => + + => RSprocess::RecursiveIdentifier{ identifier: translator.encode(identifier) } @@ -172,7 +264,7 @@ pub Environment: Box = { }; Env_term: (IdType, RSprocess) = { - "=" => + "=" => (translator.encode(identifier), k) }; @@ -192,61 +284,78 @@ AssertTree: rsassert::Tree = { } AssertTree2: rsassert::Tree = { + #[precedence(level="1")] "if" "then" "{" "}" => rsassert::Tree::If(Box::new(e), Box::new(t)), + #[precedence(level="0")] "if" "then" "{" "}" "else" "{" "}" => rsassert::Tree::IfElse(Box::new(e), Box::new(t1), Box::new(t2)), + #[precedence(level="2")] "let" "=" => rsassert::Tree::Assignment(v, q, Box::new(e)), + #[precedence(level="3")] "return" => rsassert::Tree::Return(Box::new(e)), + #[precedence(level="4")] "for" "in" "{" "}" => rsassert::Tree::For(v, r, Box::new(t)), } AssertVariable: rsassert::Variable = { + #[precedence(level="0")] "label" => rsassert::Variable::Special(rsassert::Special::Label), + #[precedence(level="1")] "edge" => rsassert::Variable::Special(rsassert::Special::Edge), + #[precedence(level="2")] => rsassert::Variable::Id(v), } AssertExpression: rsassert::Expression = { // Unary + #[precedence(level="0")] => rsassert::Expression::Unary(unp, Box::new(e)), - "(" ")" => + #[precedence(level="2")] + => rsassert::Expression::Unary(uns, Box::new(e)), // binary - "(" ")" => + #[precedence(level="3")] #[assoc(side="left")] + => rsassert::Expression::Binary(b, Box::new(e1), Box::new(e2)), + #[precedence(level="1")] "(" "," ")" => rsassert::Expression::Binary(b, Box::new(e1), Box::new(e2)), + #[precedence(level="4")] "(" ")" => e, "true" => rsassert::Expression::True, "false" => rsassert::Expression::False, + #[precedence(level="5")] => rsassert::Expression::Var(v), // If changing IntegerType in assert.rs, also change from Num to another // similar parser with different return type + #[precedence(level="6")] => rsassert::Expression::Integer(i), + #[precedence(level="7")] => rsassert::Expression::Label(Box::new(lab)), => rsassert::Expression::Set(set), "'" "'" => rsassert::Expression::Element(translator.encode(el)), // strings + #[precedence(level="8")] PATH => rsassert::Expression::String(<>.trim_end_matches("\"") .trim_start_matches("\"") .to_string()), @@ -264,11 +373,13 @@ AssertUnaryPrefix: rsassert::Unary = { } AssertUnarySuffix: rsassert::Unary = { - ".empty" => rsassert::Unary::Empty, - ".length" => rsassert::Unary::Length, - ".tostr" => rsassert::Unary::ToStr, - ".toel" => rsassert::Unary::ToEl, + #[precedence(level="0")] + "." "empty" => rsassert::Unary::Empty, + "." "length" => rsassert::Unary::Length, + "." "tostr" => rsassert::Unary::ToStr, + "." "toel" => rsassert::Unary::ToEl, + #[precedence(level="1")] "." => rsassert::Unary::Qualifier(q), } @@ -377,7 +488,6 @@ AssertLabel: RSlabel = { // system // a system is an environment, a set of entities as initial state, a context and // a set of reaction rules. - pub System: RSsystem = { "Environment" ":" "Initial Entities" ":" @@ -392,7 +502,6 @@ pub System: RSsystem = { // experiment // an experiment is composed by a sequence of weights and a sequence of sets of // entities of equal length. - pub Experiment: (Vec, Vec) = { "Weights" ":" > "Sets" ":" > @@ -749,14 +858,17 @@ Instruction: presets::Instruction = { } pub Run: presets::Instructions = { + #[precedence(level="0")] > => Instructions { system: presets::System::RSsystem { sys }, instructions: instr }, + #[precedence(level="1")] => Instructions { system: presets::System::RSsystem { sys }, instructions: vec![] }, + #[precedence(level="2")] "Deserialize" "(" ")" > => Instructions { system: presets::System::Deserialize { path }, diff --git a/src/rsprocess/presets.rs b/src/rsprocess/presets.rs index 44284de..3a00419 100644 --- a/src/rsprocess/presets.rs +++ b/src/rsprocess/presets.rs @@ -187,48 +187,98 @@ where } fn reformat_error( - e: ParseError + e: ParseError, + input_str: &str, ) -> Result where T: Display, { match e { - ParseError::ExtraToken { token: (l, t, r) } => Err(format!( - "Unexpected token \"{t}\" \ + ParseError::ExtraToken { token: (l, t, r) } => { + Err(format!( + "Unexpected token \"{t}\" \ between positions {l} and {r}." - )), + )) + }, ParseError::UnrecognizedEof { location: _, expected: _, - } => Err("End of file encountered while parsing.".into()), + } => { + Err("End of file encountered while parsing.".into()) + }, ParseError::InvalidToken { location } => { Err(format!("Invalid token at position {location}.")) } ParseError::UnrecognizedToken { token: (l, t, r), - expected, + expected: _, } => { + use colored::Colorize; + let mut err = format!( - "Unrecognized token \"{t}\" \ - between positions {l} and {r}." + "Unrecognized token {}{}{} \ + between positions {l} and {r}. ", + "\"".red(), + t.to_string().red(), + "\"".red(), ); - // Temporary debug. - err.push_str("Expected: "); - let mut it = expected.iter().peekable(); - while let Some(s) = it.next() { - err.push('('); - err.push_str(s); - err.push(')'); - if it.peek().is_some() { - err.push(','); - err.push(' '); - } + // // Temporary debug. + // err.push_str("Expected: "); + // let mut it = expected.iter().peekable(); + // while let Some(s) = it.next() { + // err.push('('); + // err.push_str(&format!("{}", s.green())); + // err.push(')'); + // if it.peek().is_some() { + // err.push(','); + // err.push(' '); + // } + // } + let right_new_line = + input_str[l..] + .find("\n") + .map(|pos| pos + l) + .unwrap_or(input_str.len()); + let left_new_line = + input_str[..r].rfind("\n") + .map(|pos| pos + 1) + .unwrap_or_default(); + let line_number = + input_str[..l].match_indices('\n').count() + 1; + let pre_no_color = format!("{line_number} |"); + let pre = format!("{}", pre_no_color.blue()); + + let line_pos_l = l - left_new_line; + let line_pos_r = r - left_new_line; + + err.push_str( + &format!(".\nLine {} position {} to {}:\n{}{}{}{}", + line_number, + line_pos_l, + line_pos_r, + &pre, + &input_str[left_new_line..l].green(), + &input_str[l..r].red(), + &input_str[r..right_new_line], + ) + ); + err.push('\n'); + err.push_str(&" ".repeat(pre_no_color.len()-1)); + err.push_str(&format!("{}", "|".blue())); + err.push_str(&" ".repeat(l - left_new_line)); + err.push_str(&format!("{}", &"↑".red())); + if r - l > 2 { + err.push_str(&" ".repeat(r - l - 2)); + err.push_str(&format!("{}", &"↑".red())); } + Err(err) }, - ParseError::User { error } => Err(error.to_string()), + ParseError::User { error } => { + Err(error.to_string()) + }, } } @@ -238,7 +288,7 @@ fn parser_experiment( ) -> Result<(Vec, Vec), String> { match grammar::ExperimentParser::new().parse(translator, &contents) { Ok(sys) => Ok(sys), - Err(e) => reformat_error(e), + Err(e) => reformat_error(e, &contents), } } @@ -248,7 +298,7 @@ fn parser_instructions( ) -> Result { match grammar::RunParser::new().parse(translator, &contents) { Ok(sys) => Ok(sys), - Err(e) => reformat_error(e), + Err(e) => reformat_error(e, &contents), } } diff --git a/testing/medical.system b/testing/medical.system new file mode 100644 index 0000000..9061578 --- /dev/null +++ b/testing/medical.system @@ -0,0 +1,225 @@ +Environment: [ + eafib1 = (?[{},{afib},{}]?.eafib1 + ?[{afib},{},{}]?.ehr), + ehr = (?[{},{heart_rate},{}]?.ehr + ?[{heart_rate},{},{}]?.ebb), + ebb = ({}.ebb + e_cbb + e_nsbb + e_sbb), + e_cbb = (?[{},{verapamil},{get_diltiazem}]?.empty + + ?[{},{diltiazem},{get_verapamil}]?.empty), + e_nsbb = (?[{},{carvedilol},{get_propranolol}]?.empty + + ?[{},{propranolol},{get_carvedilol}]?.empty), + e_sbb = (?[{},{atenolol},{get_bisoprolol}]?.empty + + ?[{},{bisoprolol},{get_atenolol}]?.empty), + eafib2 = (?[{},{afib},{}]?.eafib2 + ?[{afib},{},{}]?.ehf), + ehf = (?[{},{has_fib},{}]?.ehf + ?[{has_fib},{},{}]?.eflec), + eflec = ({}.eflec + e_flec), + e_flec = {get_flecainide}.empty, + eafib3 = (?[{},{afib},{}]?.eafib3 + ?[{afib},{},{}]?.econs), + econs = (?[{},{heart_rate,has_fib},{}]?.econs + + ?[{},{consensus_acei},{}]?.econs + + ?[{consensus_acei,heart_rate},{},{}]?.estroke + + ?[{consensus_acei,has_fib},{},{}]?.estroke), + estroke = (?[{},{diseases,over75},{}]?.ewarf + + ?[{over75},{doac_fail,doac_int},{}]?.edoac + + ?[{diseases},{doac_fail,doac_int},{}]?.edoac + + ?[{over75,doac_fail},{},{}]?.evkant + + ?[{over75,doac_int},{},{}]?.evkant + + ?[{diseases,doac_fail},{},{}]?.evkant + + ?[{diseases,doac_int},{},{}]?.evkant), + ewarf = ({}.ewarf + e_warf), + e_warf = {get_warfarin}.empty, + edoac = ({}.edoac + e_doac), + e_doac = (?[{},{dabigatran},{get_apixaban}]?.e_doacfail + + ?[{},{apixaban},{get_dabigatran}]?.e_doacfail), + e_doacfail = (?[{doac_fail},{},{stop_doac}]?.evkant + + ?[{},{doac_fail},{}]?.e_doacfail), + evkant = ({}.evkant + e_vkant), + e_vkant = {get_vkant}.empty, + ghyper = (?[{},{hyper},{}]?.ghyper + ?[{hyper},{},{}]?.g1), + g1 = (?[{diabete},{},{}]?.g2 + + ?[{below55},{diabete,origin},{}]?.g2 + + ?[{},{below55,diabete},{}]?.g3 + + ?[{origin},{diabete},{}]?.g3), + g2 = ({}.g2 + <1,e_acei>.g4 + <1,e_arb>.g5), + g3 = ({}.g3 + <1,e_cbb>.g6), + g4 = ({}.g4 + <1,e_cbb>.g7 + <1,e_td>.g8), + g5 = ({}.g5 + <1,e_cbb>.g9 + <1,e_td>.g10), + g6 = ({}.g6 + <1,e_acei>.g7 + <1,e_arb>.g9 + <1,e_td>.g11), + g7 = ({}.g7 + <1,e_arb>.etd + <1,e_td>.earb), + g8 = ({}.g8 + <1,e_arb>.ecbb + <1,e_cbb>.earb), + g9 = ({}.g9 + <1,e_acei>.etd + <1,e_td>.eacei), + g10 = ({}.g10 + <1,e_acei>.ecbb + <1,e_cbb>.eacei), + g11 = ({}.g11 + <1,e_acei>.earb + <1,e_arb>.eacei), + ecbb = ({}.ecbb + e_cbb), + eacei = ({}.eacei + e_acei), + e_acei = (?[{},{captopril},{get_benazepril}]?.empty + + ?[{},{benazepril},{get_captopril}]?.empty), + earb = ({}.earb + e_arb), + e_arb = (?[{},{irbesartan},{get_olmesortan}]?.empty + + ?[{},{olmesortan},{get_irbesartan}]?.empty), + etd = ({}.etd + e_td), + e_td = (?[{},{chlorothiazide},{get_indapamide}]?.empty + + ?[{},{indapamide},{get_chlorothiazide}]?.empty), + k_doac = (?[{doac_test},{},{doac_ok}]?.empty + + ?[{doac_test},{},{doac_fail}]?.empty + + ?[{},{doac_test},{}]?.k_doac), + empty = {}.empty, + kafib = {afib}.empty, + khf = {has_fib}.empty, + khr = {heart_rate}.empty, + kcons = {consensus_acei}.empty, + kageA = {over75}.empty, + kageB = {below55}.empty, + kdiabete = {diabete}.empty, + kdoacint = {doac_int}.empty, + khyper = {hyper}.empty, + korigin = {origin}.empty +] +Initial Entities: {} +Context: [ + eafib1, + eafib2, + eafib3, + ghyper, + kafib, + khf, + empty, + empty, + empty, + empty, + empty, + empty, + khyper, + empty, + k_doac +] +Reactions: ( + [{hyper}, {}, {hyper}]; + [{afib}, {}, {afib}]; + [{has_fib}, {}, {has_fib}]; + [{heart_rate}, {}, {heart_rate}]; + [{consensus_acei}, {}, {consensus_acei}]; + [{over75}, {}, {over75}]; + [{below55}, {}, {below55}]; + [{diabete}, {}, {diabete}]; + [{origin}, {}, {origin}]; + [{doac_int}, {}, {doac_int}]; + [{doac}, {doac_ok,doac_fail}, {doac_test}]; + [{doac_ok}, {doac_fail}, {doac_ok}]; + [{doac_fail}, {doac_ok}, {doac_fail}]; + [{hyper}, {}, {diseases}]; + [{diabete}, {}, {diseases}]; + [{get_diltiazem}, {stop_cbb}, {diltiazem,cbb}]; + [{diltiazem}, {stop_cbb}, {diltiazem,cbb}]; + [{get_verapamil}, {stop_cbb}, {verapamil,cbb}]; + [{verapamil}, {stop_cbb}, {verapamil,cbb}]; + [{diltiazem,verapamil}, {stop_cbb}, {alert_dup}]; + [{get_propranolol}, {stop_nsbb}, {propranolol,nsbb}]; + [{propranolol}, {stop_nsbb}, {propranolol,nsbb}]; + [{get_carvedilol}, {stop_nsbb}, {carvedilol,nsbb}]; + [{carvedilol}, {stop_nsbb}, {carvedilol,nsbb}]; + [{propranolol,carvedilol}, {stop_nsbb}, {alert_dup}]; + [{get_bisoprolol}, {stop_sbb}, {bisoprolol,sbb}]; + [{bisoprolol}, {stop_sbb}, {bisoprolol,sbb}]; + [{get_atenolol}, {stop_sbb}, {atenolol,sbb}]; + [{atenolol}, {stop_sbb}, {atenolol,sbb}]; + [{bisoprolol,atenolol}, {stop_sbb}, {alert_dup}]; + [{get_flecainide}, {stop_flec}, {flecainide}]; + [{flecainide}, {stop_flec}, {flecainide}]; + [{get_warfarin}, {stop_warf}, {warfarin}]; + [{warfarin}, {stop_warf}, {warfarin}]; + [{get_apixaban}, {stop_doac}, {apixaban,doac}]; + [{apixaban}, {stop_doac}, {apixaban,doac}]; + [{get_dabigatran}, {stop_doac}, {dabigatran,doac}]; + [{dabigatran}, {stop_doac}, {dabigatran,doac}]; + [{apixaban,dabigatran}, {stop_doac}, {alert_dup}]; + [{get_vkant}, {stop_vkant}, {vkant}]; + [{vkant}, {stop_vkant}, {vkant}]; + [{get_benazepril}, {stop_acei}, {benazepril,acei}]; + [{benazepril}, {stop_acei}, {benazepril,acei}]; + [{get_captopril}, {stop_acei}, {captopril,acei}]; + [{captopril}, {stop_acei}, {captopril,acei}]; + [{benazepril,captopril}, {stop_acei}, {alert_dup}]; + [{get_olmesortan}, {stop_arb}, {olmesortan,arb}]; + [{olmesortan}, {stop_arb}, {olmesortan,arb}]; + [{get_irbesartan}, {stop_arb}, {irbesartan,arb}]; + [{irbesartan}, {stop_arb}, {irbesartan,arb}]; + [{olmesortan,irbesartan}, {stop_arb}, {alert_dup}]; + [{get_indapamide}, {stop_td}, {indapamide,td}]; + [{indapamide}, {stop_td}, {indapamide,td}]; + [{get_chlorothiazide}, {stop_td}, {chlorothiazide,td}]; + [{chlorothiazide}, {stop_td}, {chlorothiazide,td}]; + [{indapamide,chlorothiazide}, {stop_td}, {alert_dup}]; + [{doac,doac_fail}, {stop_doac}, {doac_danger}]; + [{doac,doac_danger}, {stop_doac}, {danger}]; + [{get_apixaban,get_diltiazem}, {}, {moderate}]; + [{get_apixaban,diltiazem}, {}, {moderate}]; + [{apixaban,get_diltiazem}, {}, {moderate}]; + [{apixaban,diltiazem}, {}, {moderate}]; + [{get_apixaban,get_verapamil}, {}, {moderate}]; + [{get_apixaban,verapamil}, {}, {moderate}]; + [{apixaban,get_verapamil}, {}, {moderate}]; + [{apixaban,verapamil}, {}, {moderate}]; + [{get_dabigatran,get_diltiazem}, {}, {moderate}]; + [{get_dabigatran,diltiazem}, {}, {moderate}]; + [{dabigatran,get_diltiazem}, {}, {moderate}]; + [{dabigatran,diltiazem}, {}, {moderate}]; + [{get_dabigatran,get_verapamil}, {}, {major}]; + [{get_dabigatran,verapamil}, {}, {major}]; + [{dabigatran,get_verapamil}, {}, {major}]; + [{dabigatran,verapamil}, {}, {major}]; + [{get_dabigatran,get_carvedilol}, {}, {moderate}]; + [{get_dabigatran,carvedilol}, {}, {moderate}]; + [{dabigatran,get_carvedilol}, {}, {moderate}]; + [{dabigatran,carvedilol}, {}, {moderate}]; + [{get_warfarin,get_benazepril}, {}, {minor}]; + [{get_warfarin,benazepril}, {}, {minor}]; + [{warfarin,get_benazepril}, {}, {minor}]; + [{warfarin,benazepril}, {}, {minor}]; + [{get_warfarin,get_indapamide}, {}, {minor}]; + [{get_warfarin,indapamide}, {}, {minor}]; + [{warfarin,get_indapamide}, {}, {minor}]; + [{warfarin,indapamide}, {}, {minor}]; + [{get_warfarin,get_chlorothiazide}, {}, {minor}]; + [{get_warfarin,chlorothiazide}, {}, {minor}]; + [{warfarin,get_chlorothiazide}, {}, {minor}]; + [{warfarin,chlorothiazide}, {}, {minor}]; + [{get_warfarin,get_propranolol}, {}, {minor}]; + [{get_warfarin,propranolol}, {}, {minor}]; + [{warfarin,get_propranolol}, {}, {minor}]; + [{warfarin,propranolol}, {}, {minor}]; + [{get_flecainide,get_diltiazem}, {}, {major}]; + [{get_flecainide,diltiazem}, {}, {major}]; + [{flecainide,get_diltiazem}, {}, {major}]; + [{flecainide,diltiazem}, {}, {major}]; + [{get_flecainide,get_verapamil}, {}, {major}]; + [{get_flecainide,verapamil}, {}, {major}]; + [{flecainide,get_verapamil}, {}, {major}]; + [{flecainide,verapamil}, {}, {major}]; + [{get_flecainide,get_bisoprolol}, {}, {moderate}]; + [{get_flecainide,bisoprolol}, {}, {moderate}]; + [{flecainide,get_bisoprolol}, {}, {moderate}]; + [{flecainide,bisoprolol}, {}, {moderate}]; + [{get_flecainide,get_atenolol}, {}, {moderate}]; + [{get_flecainide,atenolol}, {}, {moderate}]; + [{flecainide,get_atenolol}, {}, {moderate}]; + [{flecainide,atenolol}, {}, {moderate}]; + [{get_flecainide,get_propranolol}, {}, {moderate}]; + [{get_flecainide,propranolol}, {}, {moderate}]; + [{flecainide,get_propranolol}, {}, {moderate}]; + [{flecainide,propranolol}, {}, {moderate}]; + [{get_flecainide,get_carvedilol}, {}, {moderate}]; + [{get_flecainide,carvedilol}, {}, {moderate}]; + [{flecainide,get_carvedilol}, {}, {moderate}]; + [{flecainide,carvedilol}, {}, {moderate}]; + [{major}, {}, {major}]; + [{moderate}, {}, {moderate}]; + [{minor}, {}, {minor}]; + [{alert_dup}, {}, {alert_dup}]; + [{danger},{},{danger}] +) + +Digraph > Dot + | Hide + | Hide + | ! "white" + | ! "black" + > Save("out.dot")