use std::rc::Rc; use std::str::FromStr; use lalrpop_util::ParseError; use crate::rsprocess::{set, reaction, process, environment, system, label}; use crate::rsprocess::assert::types; use crate::rsprocess::translator::{ Translator, IdType }; use crate::rsprocess::presets; use crate::rsprocess::graph; grammar(translator: &mut Translator); // ----------------------------------------------------------------------------- // Helpers // ----------------------------------------------------------------------------- // order match { ".", ",", ";", "nill", "{", "}", "[", "]", "(", ")", "<", ">", "r:", "i:", "p:", "-", "^", "true", "false", "Environment", "Initial Entities", "Context", "Reactions", "Weights", "Sets", "Print", "Save", "Dot", "GraphML", "Serialize", "Stats", "Target", "Run", "Loop", "Frequency", "LimitFrequency", "FastFrequency", "Digraph", "Bisimilarity", "Deserialize", "?", "Hide", "Entities", "MaskEntities", "UncommonEntities", "UncommonMaskEntities", "MaskContext", "UncommonContext", "UncommonMaskContext", "Products", "MaskProducts", "UncommonProducts", "UncommonMaskProducts", "Union", "MaskUnion", "UncommonUnion", "UncommonMaskUnion", "Difference", "MaskDifference", "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", "&&", "||", "^^", "<=", ">=", "==", "!=", "+", "*", "/", "%", "::", "substr", "min", "max", "commonsubstr", "SystemEntities", "SystemContext", "AvailableEntities", "AllReactants", "AllInhibitors", "relabel", } else { r"[0-9]+" => NUMBER } else { r"([[:alpha:]])([[:word:]])*" => WORD // r"(\p{L}|\p{Emoji})(\p{L}|\p{Emoji}|\p{Dash}|\p{N})*" => WORD, } else { r#""[^"]+""# => PATH, // " <- ignore comment, its for the linter in emacs } else { _ } // matches words (letter followed by numbers, letters or _) Literal: 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" }) }; Path: String = { PATH => <>.trim_end_matches("\"").trim_start_matches("\"").to_string() }; // macro for matching sequence of patterns with C as separator Separated: Vec = { C)+> => match e { None => v, Some(e) => { v.push(e); v } } }; Separated_Or: Vec = { => vec![v], > => v } // ----------------------------------------------------------------------------- // SetParser // ----------------------------------------------------------------------------- pub Set: set::Set = { => s }; Set_of_entities: set::Set = { "{" "}" => set::Set::from(vec![]), "{" > "}" => set::Set::from(t.into_iter().map(|t| translator.encode(t)) .collect::>()) }; // ----------------------------------------------------------------------------- // ReactionParser // ----------------------------------------------------------------------------- pub Reactions: Vec = { "(" ")" => vec![], "(" > ")" => s } Reaction: reaction::Reaction = { #[precedence(level="1")] "[" "," "," "]" => reaction::Reaction::from(r, i, p), #[precedence(level="0")] "[" "r:" "," "i:" "," "p:" "]" => reaction::Reaction::from(r, i, p), } // ----------------------------------------------------------------------------- // ContextParser // ----------------------------------------------------------------------------- pub Context: process::Process = { "[" "]" => process::Process::NondeterministicChoice{ children: vec![] }, "[" > "]" => process::Process::NondeterministicChoice{ children: t } }; Boxed_CTX_process: Rc = { => Rc::new(t) } CTX_process: process::Process = { "nill" => process::Process::Nill, "." => process::Process::EntitySet{ entities: c, next_process: Rc::new(k) }, "(" ")" => k, "(" > ")" => process::Process::Summation{ children: k.into_iter().map(Rc::new).collect::>() }, "?" "?" "." => process::Process::Guarded{ reaction: r, next_process: Rc::new(k) }, "<" "," ">" "." => process::Process::WaitEntity{ repeat: n, repeated_process: Rc::new(k1), next_process: Rc::new(k) }, => process::Process::RecursiveIdentifier{ identifier: translator.encode(identifier) } }; // ----------------------------------------------------------------------------- // EnvironmentParser // ----------------------------------------------------------------------------- pub Environment: Box = { "[" "]" => Box::new(environment::Environment::new()), "[" > "]" => Box::new(environment::Environment::from(t)) }; Env_term: (IdType, process::Process) = { "=" => (translator.encode(identifier), k) }; // ----------------------------------------------------------------------------- // AssertParser // ----------------------------------------------------------------------------- pub Assert: Box = { "label" "{" "}" => Box::new(types::Assert{tree: f}), }; AssertTree: types::Tree = { ";" => types::Tree::Concat(Box::new(t1), Box::new(t2)), => t, } AssertTree2: types::Tree = { #[precedence(level="1")] "if" "then" "{" "}" => types::Tree::If(Box::new(e), Box::new(t)), #[precedence(level="0")] "if" "then" "{" "}" "else" "{" "}" => types::Tree::IfElse(Box::new(e), Box::new(t1), Box::new(t2)), #[precedence(level="2")] "let" "=" => types::Tree::Assignment(v, q, Box::new(e)), #[precedence(level="3")] "return" => types::Tree::Return(Box::new(e)), #[precedence(level="4")] "for" "in" "{" "}" => types::Tree::For(v, r, Box::new(t)), } AssertVariable: types::Variable = { #[precedence(level="0")] "label" => types::Variable::Special(types::Special::Label), #[precedence(level="1")] "edge" => types::Variable::Special(types::Special::Edge), #[precedence(level="2")] => types::Variable::Id(v), } AssertExpression: types::Expression = { // Unary #[precedence(level="0")] => types::Expression::Unary(unp, Box::new(e)), #[precedence(level="2")] => types::Expression::Unary(uns, Box::new(e)), // binary #[precedence(level="3")] #[assoc(side="left")] => types::Expression::Binary(b, Box::new(e1), Box::new(e2)), #[precedence(level="1")] "(" "," ")" => types::Expression::Binary(b, Box::new(e1), Box::new(e2)), #[precedence(level="4")] "(" ")" => e, "true" => types::Expression::True, "false" => types::Expression::False, #[precedence(level="5")] => types::Expression::Var(v), // If changing IntegerType in assert.rs, also change from Num to another // similar parser with different return type #[precedence(level="6")] => types::Expression::Integer(i), #[precedence(level="7")] => types::Expression::Label(Box::new(lab)), => types::Expression::Set(set), "'" "'" => types::Expression::Element(translator.encode(el)), // strings #[precedence(level="8")] PATH => types::Expression::String(<>.trim_end_matches("\"") .trim_start_matches("\"") .to_string()), } AssertRange: types::Range = { "{" "}" => types::Range::IterateOverSet(Box::new(e)), "{" ".." "}" => types::Range::IterateInRange(Box::new(e1), Box::new(e2)), } AssertUnaryPrefix: types::Unary = { "not" => types::Unary::Not, "rand" => types::Unary::Rand, } AssertUnarySuffix: types::Unary = { #[precedence(level="0")] "." "empty" => types::Unary::Empty, "." "length" => types::Unary::Length, "." "tostr" => types::Unary::ToStr, "." "toel" => types::Unary::ToEl, #[precedence(level="1")] "." => types::Unary::Qualifier(q), } AssertQualifierRestricted: types::QualifierRestricted = { "Entities" => types::QualifierRestricted::Entities, "Context" => types::QualifierRestricted::Context, "Reactants" => types::QualifierRestricted::Reactants, "ReactantsAbsent" => types::QualifierRestricted::ReactantsAbsent, "Inhibitors" => types::QualifierRestricted::Inhibitors, "InhibitorsPresent" => types::QualifierRestricted::InhibitorsPresent, "Products" => types::QualifierRestricted::Products, } AssertQualifierLabel: types::QualifierLabel = { "AvailableEntities" => types::QualifierLabel::AvailableEntities, "AllReactants" => types::QualifierLabel::AllReactants, "AllInhibitors" => types::QualifierLabel::AllInhibitors, } AssertQualifierSystem: types::QualifierSystem = { "SystemEntities" => types::QualifierSystem::Entities, "SystemContext" => types::QualifierSystem::Context, } AssertQualifierEdge: types::QualifierEdge = { "source" => types::QualifierEdge::Source, "target" => types::QualifierEdge::Target, } AssertQualifierNode: types::QualifierNode = { "neighbours" => types::QualifierNode::Neighbours, "system" => types::QualifierNode::System, } AssertQualifier: types::Qualifier = { => types::Qualifier::System(q), => types::Qualifier::Label(q), => types::Qualifier::Restricted(q), => types::Qualifier::Edge(q), => types::Qualifier::Node(q), } AssertBinary: types::Binary = { "&&" => types::Binary::And, "||" => types::Binary::Or, "^^" => types::Binary::Xor, "<" => types::Binary::Less, "<=" => types::Binary::LessEq, ">" => types::Binary::More, ">=" => types::Binary::MoreEq, "==" => types::Binary::Eq, "!=" => types::Binary::NotEq, "+" => types::Binary::Plus, "-" => types::Binary::Minus, "*" => types::Binary::Times, "^" => types::Binary::Exponential, "/" => types::Binary::Quotient, "%" => types::Binary::Reminder, "::" => types::Binary::Concat, } AssertBinaryPrefix: types::Binary = { "substr" => types::Binary::SubStr, "min" => types::Binary::Min, "max" => types::Binary::Max, "commonsubstr" => types::Binary::CommonSubStr, } AssertLabel: label::Label = { "[" "Entities" ":" "," "Context" ":" "," "Reactants" ":" "," "ReactantsAbsent" ":" "," "Inhibitors" ":" "," "InhibitorsPresent" ":" "," "Products" ":" "," "]" => label::Label::create(e, c, r, r_a, i, i_p, p) } // ----------------------------------------------------------------------------- // BHMLParser // ----------------------------------------------------------------------------- // pub BHML: Box = { // => Box::new(g) // }; // Formula_BHML: RSBHML = { // "true" => RSBHML::True, // "false" => RSBHML::False, // "(" > ")" => RSBHML::Or(g), // "(" > ")" => RSBHML::And(g), // "<" ">" => // RSBHML::Diamond(Box::new(f), Box::new(g)), // "[" "]" => // RSBHML::Box(Box::new(f), Box::new(g)), // }; // ---------------------------------------------------------------------------- // File Parsing // ---------------------------------------------------------------------------- // system // a system is an environment, a set of entities as initial state, a context and // a set of reaction rules. pub System: system::System = { "Environment" ":" "Initial Entities" ":" "Context" ":" "Reactions" ":" => system::System::from(delta.into(), available_entities, context_process, Rc::new(reaction_rules)) } // 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" ":" > => (w.into_iter().map(|x| x as u32).collect::>(), s), } // ~~~~~~~~~~~~~~~~~~~ // Instruction Parsing // ~~~~~~~~~~~~~~~~~~~ /// Decides whetherer to print to stdout or to save to file Helper_SO: presets::SaveOptions = { "Print" => presets::SaveOptions {print: true, save: None}, "Save" "(" ")" => presets::SaveOptions {print: false, save: Some(vec![p])} } /// we could need to save to multiple files SaveOptions: presets::SaveOptions = { > => { p.into_iter() .reduce(|mut acc, mut e| {acc.combine(&mut e); acc}) .unwrap_or_default() } } /// Match for strings between nodes formatters LiteralSeparatorNode: graph::NodeDisplayBase = { PATH => graph::NodeDisplayBase::String { string: <>.trim_end_matches("\"") .trim_start_matches("\"") .to_string() } }; /// Match for strings between edge formatters LiteralSeparatorEdge: graph::EdgeDisplayBase = { PATH => graph::EdgeDisplayBase::String { string: <>.trim_end_matches("\"") .trim_start_matches("\"") .to_string() } }; NodeDisplayBase: graph::NodeDisplayBase = { "Hide" => graph::NodeDisplayBase::Hide, "Entities" => graph::NodeDisplayBase::Entities, "MaskEntities" => graph::NodeDisplayBase::MaskEntities{mask}, "ExcludeEntities" => graph::NodeDisplayBase::ExcludeEntities{mask}, "Context" => graph::NodeDisplayBase::Context, "UncommonEntities" => graph::NodeDisplayBase::UncommonEntities, "MaskUncommonentities" => graph::NodeDisplayBase::MaskUncommonEntities{mask}, } /// Node display formatters separated by arbitrary strings in quotes SeparatorNode: graph::NodeDisplay = { => graph::NodeDisplay {base: vec![v]}, )+> => match e { None => graph::NodeDisplay { base: v.iter().fold(vec![], |mut acc, (a, b)| { acc.push(a.clone()); acc.push(b.clone()); acc.clone() }) }, Some(e) => { let mut v = v.iter().fold(vec![], |mut acc, (a, b)| { acc.push(a.clone()); acc.push(b.clone()); acc.clone() }); v.push(e); graph::NodeDisplay { base: v } } } } EdgeDisplay: graph::EdgeDisplayBase = { "Hide" => graph::EdgeDisplayBase::Hide, "Products" => graph::EdgeDisplayBase::Products { mask: None, filter_common: false }, "MaskProducts" => graph::EdgeDisplayBase::Entities { mask: Some(mask), filter_common: false }, "UncommonProducts" => graph::EdgeDisplayBase::Products { mask: None, filter_common: true }, "UncommonMaskProducts" => graph::EdgeDisplayBase::Entities { mask: Some(mask), filter_common: true }, "Entities" => graph::EdgeDisplayBase::Entities { mask: None, filter_common: false }, "MaskEntities" => graph::EdgeDisplayBase::Entities { mask: Some(mask), filter_common: false }, "UncommonEntities" => graph::EdgeDisplayBase::Entities { mask: None, filter_common: true }, "UncommonMaskEntities" => graph::EdgeDisplayBase::Entities { mask: Some(mask), filter_common: true }, "Context" => graph::EdgeDisplayBase::Context { mask: None, filter_common: false }, "MaskContext" => graph::EdgeDisplayBase::Context { mask: Some(mask), filter_common: false }, "UncommonContext" => graph::EdgeDisplayBase::Context { mask: None, filter_common: true }, "UncommonMaskContext" => graph::EdgeDisplayBase::Context { mask: Some(mask), filter_common: true }, "Union" => graph::EdgeDisplayBase::Union { mask: None, filter_common: false }, "MaskUnion" => graph::EdgeDisplayBase::Union { mask: Some(mask), filter_common: false }, "UncommonUnion" => graph::EdgeDisplayBase::Union { mask: None, filter_common: true }, "UncommonMaskUnion" => graph::EdgeDisplayBase::Union { mask: Some(mask), filter_common: true }, "Difference" => graph::EdgeDisplayBase::Difference { mask: None, filter_common: false }, "MaskDifference" => graph::EdgeDisplayBase::Difference { mask: Some(mask), filter_common: false }, "UncommonDifference" => graph::EdgeDisplayBase::Difference { mask: None, filter_common: true }, "UncommonMaskDifference" => graph::EdgeDisplayBase::Difference { mask: Some(mask), filter_common: true }, "EntitiesDeleted" => graph::EdgeDisplayBase::EntitiesDeleted { mask: None, filter_common: false }, "MaskEntitiesDeleted" => graph::EdgeDisplayBase::EntitiesDeleted { mask: Some(mask), filter_common: false }, "UncommonEntitiesDeleted" => graph::EdgeDisplayBase::EntitiesDeleted { mask: None, filter_common: true }, "UncommonMaskEntitiesDeleted" => graph::EdgeDisplayBase::EntitiesDeleted { mask: Some(mask), filter_common: true }, "EntitiesAdded" => graph::EdgeDisplayBase::EntitiesAdded { mask: None, filter_common: false }, "MaskEntitiesAdded" => graph::EdgeDisplayBase::EntitiesAdded { mask: Some(mask), filter_common: false }, "UncommonEntitiesAdded" => graph::EdgeDisplayBase::EntitiesAdded { mask: None, filter_common: true }, "UncommonMaskEntitiesAdded" => graph::EdgeDisplayBase::EntitiesAdded { mask: Some(mask), filter_common: true }, } /// Edge display formatters separated by arbitrary strings in quotes SeparatorEdge: graph::EdgeDisplay = { => graph::EdgeDisplay{ base: vec![v] }, )+> => match e { None => graph::EdgeDisplay{ base: v.iter().fold(vec![], |mut acc, (a, b)| { acc.push(a.clone()); acc.push(b.clone()); acc.clone() }) }, Some(e) => { let mut v = v.iter().fold(vec![], |mut acc, (a, b)| { acc.push(a.clone()); acc.push(b.clone()); acc.clone() }); v.push(e); graph::EdgeDisplay{ base: v } } } } Operation: graph::OperationType = { "==" => graph::OperationType::Equals, "=" => graph::OperationType::Equals, "<" => graph::OperationType::Subset, "⊂" => graph::OperationType::Subset, "<=" => graph::OperationType::SubsetEqual, "⊆" => graph::OperationType::SubsetEqual, ">" => graph::OperationType::Superset, "⊃" => graph::OperationType::Superset, ">=" => graph::OperationType::SupersetEqual, "⊇" => graph::OperationType::SupersetEqual } NodeColorConditional: (graph::NodeColorConditional, String) = { "Entities" "?" => (graph::NodeColorConditional::EntitiesConditional(op, set), color.to_string()), "Context.Nill" "?" => (graph::NodeColorConditional::ContextConditional( graph::ContextColorConditional::Nill), color.to_string()), "Context.RecursiveIdentifier" "(" ")" "?" => (graph::NodeColorConditional::ContextConditional( graph::ContextColorConditional::RecursiveIdentifier( translator.encode(x) )), color.to_string()), "Context.EntitySet" "?" => (graph::NodeColorConditional::ContextConditional( graph::ContextColorConditional::EntitySet(op, set)), color.to_string()), "Context.NonDeterministicChoice" "?" => (graph::NodeColorConditional::ContextConditional( graph::ContextColorConditional::NonDeterministicChoice), color.to_string()), "Context.Summation" "?" => (graph::NodeColorConditional::ContextConditional( graph::ContextColorConditional::Summation), color.to_string()), "Context.WaitEntity" "?" => (graph::NodeColorConditional::ContextConditional( graph::ContextColorConditional::WaitEntity), color.to_string()), } /// Node color formatter ColorNode: graph::NodeColor = { > "!" => graph::NodeColor { conditionals, base_color: base_color.to_string() }, "!" => graph::NodeColor { conditionals: vec![], base_color: base_color.to_string() }, } EdgeColorConditional: (graph::EdgeColorConditional, String) = { "Entities" "?" => (graph::EdgeColorConditional::Entities(op, set), color.to_string()), "Context" "?" => (graph::EdgeColorConditional::Context(op, set), color.to_string()), "T" "?" => (graph::EdgeColorConditional::T(op, set), color.to_string()), "Reactants" "?" => (graph::EdgeColorConditional::Reactants(op, set), color.to_string()), "AbsentReactants" "?" => (graph::EdgeColorConditional::ReactantsAbsent(op, set), color.to_string()), "Inhibitors" "?" => (graph::EdgeColorConditional::Inhibitors(op, set), color.to_string()), "PresentInhibitors" "?" => (graph::EdgeColorConditional::InhibitorsPresent(op, set), color.to_string()), "Products" "?" => (graph::EdgeColorConditional::Products(op, set), color.to_string()), } ColorEdge: graph::EdgeColor = { > "!" => graph::EdgeColor { conditionals, base_color: base_color.to_string() }, "!" => graph::EdgeColor { conditionals: vec![], base_color: base_color.to_string() }, } GraphSaveOptions: presets::GraphSaveOptions = { "Dot" "|"? "|" "|" "|" ">" => presets::GraphSaveOptions::Dot { node_display: s_node, edge_display: s_edge, node_color: c_node, edge_color: c_edge, so }, "GraphML" "|"? "|" ">" => presets::GraphSaveOptions::GraphML { node_display: s_node, edge_display: s_edge, so }, "Serialize" "(" ")" => presets::GraphSaveOptions::Serialize { path }, } Instruction: presets::Instruction = { "Stats" ">" => presets::Instruction::Stats { so }, "Target" ">" => presets::Instruction::Target { so }, "Run" ">" => presets::Instruction::Run { so }, "Loop" "(" ")" ">" => presets::Instruction::Loop { symbol, so }, "Frequency" ">" => presets::Instruction::Frequency { so }, "LimitFrequency" "(" ")" ">" => presets::Instruction::LimitFrequency { experiment: p, so }, "FastFrequency" "(" ")" ">" => presets::Instruction::FastFrequency { experiment: p, so }, "Digraph" ">" > => presets::Instruction::Digraph { gso }, // "Bisimilarity" "(" ")" "relabel" ">" => presets::Instruction::Bisimilarity { system_b: p, edge_relabeler, so }, } pub Run: presets::Instructions = { #[precedence(level="0")] > => presets::Instructions { system: presets::System::System { sys }, instructions: instr }, #[precedence(level="1")] => presets::Instructions { system: presets::System::System { sys }, instructions: vec![] }, #[precedence(level="2")] "Deserialize" "(" ")" > => presets::Instructions { system: presets::System::Deserialize { path }, instructions: instr }, }