2025-10-17 19:45:20 +02:00
|
|
|
use std::str::FromStr;
|
|
|
|
|
use lalrpop_util::ParseError;
|
|
|
|
|
|
2025-10-27 21:12:43 +01:00
|
|
|
use assert::grouping;
|
|
|
|
|
use rsprocess::{set, label};
|
2025-10-17 19:45:20 +02:00
|
|
|
use rsprocess::translator::Translator;
|
|
|
|
|
use crate::custom_error;
|
|
|
|
|
|
|
|
|
|
grammar(translator: &mut Translator);
|
|
|
|
|
|
|
|
|
|
extern {
|
|
|
|
|
type Error = custom_error::UserError;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
// Helpers
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
// order
|
|
|
|
|
match {
|
2025-10-27 21:12:43 +01:00
|
|
|
"!", "!=", "%", "&&", "'", "(", ")", "*", "+", ",", "-", "..", "/", ":",
|
|
|
|
|
"::", ";", "<", "<=", "=", "==", ">", ">=", "AllInhibitors", "AllReactants",
|
2025-12-15 16:59:33 +01:00
|
|
|
"AvailableEntities", "Context", "Entities", "getGuardInhibitors",
|
|
|
|
|
"getGuardProducts", "getGuardReactants", "getIdentifier",
|
|
|
|
|
"getNextProcesses", "getRepeatedCounter", "getRepeatedProcess", "getSet",
|
|
|
|
|
"Inhibitors", "InhibitorsPresent", "isGuarded", "isIdentifier", "isNill",
|
|
|
|
|
"isNondeterministicChoice", "isRepeated", "isSet", "isSummation",
|
|
|
|
|
"Products", "Reactants", "ReactantsAbsent", "SystemContext",
|
|
|
|
|
"SystemEntities", "[", "\"", "]", "^", "^^", "edge", "else", "empty",
|
|
|
|
|
"entities", "false", "for", "if", "in", "label", "length", "let",
|
|
|
|
|
"neighbours", "node", "not", "rand", "return", "source", "system", "target",
|
|
|
|
|
"then", "toel", "tostr", "true", "{", "||", "}",
|
2025-10-17 19:45:20 +02:00
|
|
|
} 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(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Num: i64 = {
|
|
|
|
|
<sign: "-"?> <start: @L> <n: NUMBER> <end: @R> =>? {
|
|
|
|
|
if sign.is_some() {
|
|
|
|
|
i64::from_str(n)
|
|
|
|
|
.map(|n| -n)
|
|
|
|
|
.map_err(|_| ParseError::User {
|
|
|
|
|
error: custom_error::UserError {
|
|
|
|
|
token: (start, n.into(), end),
|
|
|
|
|
error: custom_error::UserErrorTypes::NumberTooBigi64
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
i64::from_str(n)
|
|
|
|
|
.map_err(|_| ParseError::User {
|
|
|
|
|
error: custom_error::UserError {
|
|
|
|
|
token: (start, n.into(), end),
|
|
|
|
|
error: custom_error::UserErrorTypes::NumberTooBigi64
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// macro for matching sequence of patterns with C as separator
|
|
|
|
|
Separated<T, C>: Vec<T> = {
|
|
|
|
|
<mut v:(<T> C)+> <e:T?> => match e {
|
|
|
|
|
None => v,
|
|
|
|
|
Some(e) => {
|
|
|
|
|
v.push(e);
|
|
|
|
|
v
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Separated_Or<T, C>: Vec<T> = {
|
|
|
|
|
<v: T> => vec![v],
|
|
|
|
|
<v: Separated<T, C>> => v
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Separated_Empty<LP, T, C, RP>: Vec<T> = {
|
|
|
|
|
LP RP => vec![],
|
|
|
|
|
LP <v: T> RP => vec![v],
|
|
|
|
|
LP <v: Separated<T, C>> RP => v
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
// SetParser
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
2025-10-27 21:12:43 +01:00
|
|
|
Set: set::Set = {
|
2025-10-17 19:45:20 +02:00
|
|
|
<s: Separated_Empty<"{", Literal, ",", "}">> =>
|
2025-10-27 21:12:43 +01:00
|
|
|
set::Set::from(s.into_iter().map(|t| translator.encode(t))
|
2025-10-17 19:45:20 +02:00
|
|
|
.collect::<Vec<_>>())
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
// LabelParser
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
2025-10-27 21:12:43 +01:00
|
|
|
Label: label::Label = {
|
2025-10-17 19:45:20 +02:00
|
|
|
"["
|
|
|
|
|
"Entities" ":" <e: Set> ","
|
|
|
|
|
"Context" ":" <c: Set> ","
|
|
|
|
|
"Reactants" ":" <r: Set> ","
|
|
|
|
|
"ReactantsAbsent" ":" <r_a: Set> ","
|
|
|
|
|
"Inhibitors" ":" <i: Set> ","
|
|
|
|
|
"InhibitorsPresent" ":" <i_p: Set> ","
|
|
|
|
|
"Products" ":" <p: Set> ","?
|
|
|
|
|
"]" => label::Label::create(e, c, r, r_a, i, i_p, p)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
// GroupingParser
|
|
|
|
|
// -----------------------------------------------------------------------------
|
2025-10-27 21:12:43 +01:00
|
|
|
pub Group: Box<grouping::Assert> = {
|
|
|
|
|
"node" "{" <f: GroupTree> "}" =>
|
2025-10-17 19:45:20 +02:00
|
|
|
Box::new(grouping::Assert{tree: f}),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
GroupTree: grouping::Tree = {
|
|
|
|
|
<t1: GroupTree2> <t2: GroupTree> =>
|
|
|
|
|
grouping::Tree::Concat(Box::new(t1), Box::new(t2)),
|
|
|
|
|
<t: GroupTree2> => t,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GroupTree2: grouping::Tree = {
|
|
|
|
|
#[precedence(level="1")]
|
|
|
|
|
"if" <e: GroupExpression>
|
|
|
|
|
"then" "{" <t: GroupTree> "}" ";"? =>
|
|
|
|
|
grouping::Tree::If(Box::new(e), Box::new(t)),
|
|
|
|
|
|
|
|
|
|
#[precedence(level="0")]
|
|
|
|
|
"if" <e: GroupExpression>
|
|
|
|
|
"then" "{" <t1: GroupTree> "}"
|
|
|
|
|
"else" "{" <t2: GroupTree> "}" ";"? =>
|
|
|
|
|
grouping::Tree::IfElse(Box::new(e), Box::new(t1), Box::new(t2)),
|
|
|
|
|
|
|
|
|
|
"let" <v: GroupVariable> <q: GroupQualifier?> "=" <e: GroupExpression> ";"
|
|
|
|
|
=> grouping::Tree::Assignment(v, q, Box::new(e)),
|
|
|
|
|
|
|
|
|
|
"return" <e: GroupExpression> ";" =>
|
|
|
|
|
grouping::Tree::Return(Box::new(e)),
|
|
|
|
|
|
|
|
|
|
"for" <v: GroupVariable> "in" <r: GroupRange> "{" <t: GroupTree> "}" ";"? =>
|
|
|
|
|
grouping::Tree::For(v, r, Box::new(t)),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GroupVariable: grouping::Variable = {
|
|
|
|
|
#[precedence(level="0")]
|
|
|
|
|
"entities" => grouping::Variable::Special(grouping::Special::Entities),
|
|
|
|
|
"node" => grouping::Variable::Special(grouping::Special::Node),
|
|
|
|
|
#[precedence(level="1")]
|
|
|
|
|
<v: Literal> => grouping::Variable::Id(v),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GroupExpression: grouping::Expression = {
|
|
|
|
|
#[precedence(level="100")]
|
|
|
|
|
<unp: GroupUnaryPrefix> "(" <e: GroupExpression> ")" =>
|
|
|
|
|
grouping::Expression::Unary(unp, Box::new(e)),
|
|
|
|
|
|
|
|
|
|
#[precedence(level="50")]
|
|
|
|
|
<e: GroupExpression> "." <uns: GroupUnarySuffix> =>
|
|
|
|
|
grouping::Expression::Unary(uns, Box::new(e)),
|
|
|
|
|
|
|
|
|
|
#[precedence(level="100")] #[assoc(side="left")]
|
|
|
|
|
<e1: GroupExpression> <b: GroupBinary> <e2: GroupExpression> =>
|
|
|
|
|
grouping::Expression::Binary(b, Box::new(e1), Box::new(e2)),
|
|
|
|
|
|
|
|
|
|
#[precedence(level="100")]
|
|
|
|
|
<b: GroupBinaryPrefix>
|
|
|
|
|
"(" <e1: GroupExpression> "," <e2: GroupExpression> ")" =>
|
|
|
|
|
grouping::Expression::Binary(b, Box::new(e1), Box::new(e2)),
|
|
|
|
|
|
|
|
|
|
#[precedence(level="0")]
|
|
|
|
|
<t: GroupTerm> => t,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GroupTerm: grouping::Expression = {
|
|
|
|
|
"true" => grouping::Expression::True,
|
|
|
|
|
"false" => grouping::Expression::False,
|
|
|
|
|
|
|
|
|
|
<v: GroupVariable> => grouping::Expression::Var(v),
|
|
|
|
|
|
|
|
|
|
// If changing IntegerType in assert.rs, also change from Num to another
|
|
|
|
|
// similar parser with different return type
|
|
|
|
|
<i: Num> => grouping::Expression::Integer(i),
|
|
|
|
|
|
|
|
|
|
<lab: Label> => grouping::Expression::Label(Box::new(lab)),
|
|
|
|
|
<set: Set> => grouping::Expression::Set(set),
|
|
|
|
|
"'" <el: Literal> "'" =>
|
|
|
|
|
grouping::Expression::Element(translator.encode(el)),
|
|
|
|
|
|
|
|
|
|
// strings
|
|
|
|
|
PATH => grouping::Expression::String(<>.trim_end_matches("\"")
|
|
|
|
|
.trim_start_matches("\"")
|
|
|
|
|
.to_string()),
|
|
|
|
|
// allow arbitrary parenthesis
|
|
|
|
|
"(" <e: GroupExpression> ")" => e,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GroupRange: grouping::Range = {
|
|
|
|
|
"{" <e: GroupExpression> "}" =>
|
|
|
|
|
grouping::Range::IterateOverSet(Box::new(e)),
|
|
|
|
|
"{" <e1: GroupExpression> ".." <e2: GroupExpression> "}" =>
|
|
|
|
|
grouping::Range::IterateInRange(Box::new(e1), Box::new(e2)),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GroupUnaryPrefix: grouping::Unary = {
|
|
|
|
|
"not" => grouping::Unary::Not,
|
|
|
|
|
"rand" => grouping::Unary::Rand,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GroupUnarySuffix: grouping::Unary = {
|
|
|
|
|
#[precedence(level="0")]
|
|
|
|
|
"empty" => grouping::Unary::Empty,
|
|
|
|
|
"length" => grouping::Unary::Length,
|
|
|
|
|
"tostr" => grouping::Unary::ToStr,
|
|
|
|
|
"toel" => grouping::Unary::ToEl,
|
|
|
|
|
|
|
|
|
|
#[precedence(level="1")]
|
|
|
|
|
<q: GroupQualifier> => grouping::Unary::Qualifier(q),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GroupQualifierRestricted: grouping::QualifierRestricted = {
|
|
|
|
|
"Entities" => grouping::QualifierRestricted::Entities,
|
|
|
|
|
"Context" => grouping::QualifierRestricted::Context,
|
|
|
|
|
"Reactants" => grouping::QualifierRestricted::Reactants,
|
|
|
|
|
"ReactantsAbsent" => grouping::QualifierRestricted::ReactantsAbsent,
|
|
|
|
|
"Inhibitors" => grouping::QualifierRestricted::Inhibitors,
|
|
|
|
|
"InhibitorsPresent" => grouping::QualifierRestricted::InhibitorsPresent,
|
|
|
|
|
"Products" => grouping::QualifierRestricted::Products,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GroupQualifierLabel: grouping::QualifierLabel = {
|
|
|
|
|
"AvailableEntities" => grouping::QualifierLabel::AvailableEntities,
|
|
|
|
|
"AllReactants" => grouping::QualifierLabel::AllReactants,
|
|
|
|
|
"AllInhibitors" => grouping::QualifierLabel::AllInhibitors,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GroupQualifierSystem: grouping::QualifierSystem = {
|
|
|
|
|
"SystemEntities" => grouping::QualifierSystem::Entities,
|
|
|
|
|
"SystemContext" => grouping::QualifierSystem::Context,
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-15 16:59:33 +01:00
|
|
|
GroupQualifierContext: grouping::QualifierContext = {
|
|
|
|
|
"isNill" => grouping::QualifierContext::IsNill,
|
|
|
|
|
"isIdentifier" => grouping::QualifierContext::IsIdentifier,
|
|
|
|
|
"isSet" => grouping::QualifierContext::IsSet,
|
|
|
|
|
"isGuarded" => grouping::QualifierContext::IsGuarded,
|
|
|
|
|
"isRepeated" => grouping::QualifierContext::IsRepeated,
|
|
|
|
|
"isSummation" => grouping::QualifierContext::IsSummation,
|
|
|
|
|
"isNondeterministicChoice" => grouping::QualifierContext::IsNondeterministicChoice,
|
|
|
|
|
|
|
|
|
|
"getIdentifier" => grouping::QualifierContext::GetIdentifier,
|
|
|
|
|
"getSet" => grouping::QualifierContext::GetSet,
|
|
|
|
|
"getGuardReactants" => grouping::QualifierContext::GetGuardReactants,
|
|
|
|
|
"getGuardInhibitors" => grouping::QualifierContext::GetGuardInhibitors,
|
|
|
|
|
"getGuardProducts" => grouping::QualifierContext::GetGuardProducts,
|
|
|
|
|
"getRepeatedCounter" => grouping::QualifierContext::GetRepeatedCounter,
|
|
|
|
|
"getRepeatedProcess" => grouping::QualifierContext::GetRepeatedProcess,
|
|
|
|
|
|
|
|
|
|
"getNextProcesses" => grouping::QualifierContext::GetNextProcesses,
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-17 19:45:20 +02:00
|
|
|
GroupQualifierEdge: grouping::QualifierEdge = {
|
|
|
|
|
"source" => grouping::QualifierEdge::Source,
|
|
|
|
|
"target" => grouping::QualifierEdge::Target,
|
2025-11-15 19:03:24 +01:00
|
|
|
"label" => grouping::QualifierEdge::Label,
|
2025-10-17 19:45:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GroupQualifierNode: grouping::QualifierNode = {
|
|
|
|
|
"neighbours" => grouping::QualifierNode::Neighbours,
|
|
|
|
|
"system" => grouping::QualifierNode::System,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GroupQualifier: grouping::Qualifier = {
|
|
|
|
|
<q: GroupQualifierSystem> => grouping::Qualifier::System(q),
|
2025-12-15 16:59:33 +01:00
|
|
|
<q: GroupQualifierContext> => grouping::Qualifier::Context(q),
|
2025-10-17 19:45:20 +02:00
|
|
|
<q: GroupQualifierLabel> => grouping::Qualifier::Label(q),
|
|
|
|
|
<q: GroupQualifierRestricted> => grouping::Qualifier::Restricted(q),
|
|
|
|
|
<q: GroupQualifierEdge> => grouping::Qualifier::Edge(q),
|
|
|
|
|
<q: GroupQualifierNode> => grouping::Qualifier::Node(q),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GroupBinary: grouping::Binary = {
|
|
|
|
|
"&&" => grouping::Binary::And,
|
|
|
|
|
"||" => grouping::Binary::Or,
|
|
|
|
|
"^^" => grouping::Binary::Xor,
|
|
|
|
|
"<" => grouping::Binary::Less,
|
|
|
|
|
"<=" => grouping::Binary::LessEq,
|
|
|
|
|
">" => grouping::Binary::More,
|
|
|
|
|
">=" => grouping::Binary::MoreEq,
|
|
|
|
|
"==" => grouping::Binary::Eq,
|
|
|
|
|
"!=" => grouping::Binary::NotEq,
|
|
|
|
|
"+" => grouping::Binary::Plus,
|
|
|
|
|
"-" => grouping::Binary::Minus,
|
|
|
|
|
"*" => grouping::Binary::Times,
|
|
|
|
|
"^" => grouping::Binary::Exponential,
|
|
|
|
|
"/" => grouping::Binary::Quotient,
|
|
|
|
|
"%" => grouping::Binary::Reminder,
|
|
|
|
|
"::" => grouping::Binary::Concat,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GroupBinaryPrefix: grouping::Binary = {
|
|
|
|
|
"substr" => grouping::Binary::SubStr,
|
|
|
|
|
"min" => grouping::Binary::Min,
|
|
|
|
|
"max" => grouping::Binary::Max,
|
|
|
|
|
"commonsubstr" => grouping::Binary::CommonSubStr,
|
|
|
|
|
}
|