bentopy 0.2.0a10__cp313-cp313-manylinux_2_34_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. bentopy-0.2.0a10.data/scripts/bentopy-init +0 -0
  2. bentopy-0.2.0a10.data/scripts/bentopy-pack +0 -0
  3. bentopy-0.2.0a10.data/scripts/bentopy-render +0 -0
  4. bentopy-0.2.0a10.data/scripts/bentopy-solvate +0 -0
  5. bentopy-0.2.0a10.dist-info/METADATA +358 -0
  6. bentopy-0.2.0a10.dist-info/RECORD +58 -0
  7. bentopy-0.2.0a10.dist-info/WHEEL +5 -0
  8. bentopy-0.2.0a10.dist-info/entry_points.txt +4 -0
  9. bentopy-0.2.0a10.dist-info/licenses/LICENSE.txt +13 -0
  10. bentopy-0.2.0a10.dist-info/top_level.txt +8 -0
  11. check/check.py +128 -0
  12. core/config/bent/lexer.rs +338 -0
  13. core/config/bent/parser.rs +1180 -0
  14. core/config/bent/writer.rs +205 -0
  15. core/config/bent.rs +149 -0
  16. core/config/compartment_combinations.rs +300 -0
  17. core/config/legacy.rs +768 -0
  18. core/config.rs +362 -0
  19. core/mod.rs +4 -0
  20. core/placement.rs +100 -0
  21. core/utilities.rs +1 -0
  22. core/version.rs +32 -0
  23. init/example.bent +74 -0
  24. init/main.rs +235 -0
  25. mask/config.py +153 -0
  26. mask/mask.py +308 -0
  27. mask/utilities.py +38 -0
  28. merge/merge.py +175 -0
  29. pack/args.rs +77 -0
  30. pack/main.rs +121 -0
  31. pack/mask.rs +940 -0
  32. pack/session.rs +176 -0
  33. pack/state/combinations.rs +31 -0
  34. pack/state/compartment.rs +44 -0
  35. pack/state/mask.rs +196 -0
  36. pack/state/pack.rs +187 -0
  37. pack/state/segment.rs +72 -0
  38. pack/state/space.rs +98 -0
  39. pack/state.rs +440 -0
  40. pack/structure.rs +185 -0
  41. pack/voxelize.rs +85 -0
  42. render/args.rs +109 -0
  43. render/limits.rs +73 -0
  44. render/main.rs +12 -0
  45. render/render.rs +393 -0
  46. render/structure.rs +264 -0
  47. solvate/args.rs +324 -0
  48. solvate/convert.rs +25 -0
  49. solvate/cookies.rs +185 -0
  50. solvate/main.rs +177 -0
  51. solvate/placement.rs +380 -0
  52. solvate/solvate.rs +244 -0
  53. solvate/structure.rs +160 -0
  54. solvate/substitute.rs +113 -0
  55. solvate/water/martini.rs +409 -0
  56. solvate/water/models.rs +150 -0
  57. solvate/water/tip3p.rs +658 -0
  58. solvate/water.rs +115 -0
@@ -0,0 +1,1180 @@
1
+ use std::path::PathBuf;
2
+
3
+ use chumsky::input::BorrowInput;
4
+ use chumsky::prelude::*;
5
+
6
+ use crate::core::config::bent::lexer::{Number, Token};
7
+ use crate::core::config::{
8
+ Anchor, Axes, Axis, Compartment, Config, Constraint, Dimensions, Expr, General, Limit, Mask,
9
+ Op, Quantity, RearrangeMethod, Rule, Segment, Shape, Space,
10
+ };
11
+
12
+ /// A shorter alias for this otherwise rather unwieldy type.
13
+ type E<'src, 'tokens> = extra::Err<Rich<'tokens, Token<'src>>>;
14
+
15
+ mod components {
16
+ use super::*;
17
+
18
+ pub(crate) fn ident<'ts, 's: 'ts, I>(
19
+ word: &'static str,
20
+ ) -> impl Parser<'ts, I, Token<'s>, E<'s, 'ts>>
21
+ where
22
+ I: BorrowInput<'ts, Token = Token<'s>, Span = SimpleSpan>,
23
+ {
24
+ just(Token::Ident(word)).boxed()
25
+ }
26
+
27
+ pub(crate) fn axis<'ts, 's: 'ts, I>() -> impl Parser<'ts, I, Axis, E<'s, 'ts>>
28
+ where
29
+ I: BorrowInput<'ts, Token = Token<'s>, Span = SimpleSpan>,
30
+ {
31
+ select! {
32
+ Token::Ident("x") => Axis::X,
33
+ Token::Ident("y") => Axis::Y,
34
+ Token::Ident("z") => Axis::Z,
35
+ }
36
+ .labelled("axis")
37
+ }
38
+
39
+ pub(crate) fn anchor<'ts, 's: 'ts, I>() -> impl Parser<'ts, I, Anchor, E<'s, 'ts>>
40
+ where
41
+ I: BorrowInput<'ts, Token = Token<'s>, Span = SimpleSpan>,
42
+ {
43
+ let point = select! { Token::Point(point) => point }.boxed();
44
+ choice((
45
+ point.map(Anchor::Point),
46
+ components::ident("start").to(Anchor::Start),
47
+ components::ident("center").to(Anchor::Center),
48
+ components::ident("end").to(Anchor::End),
49
+ ))
50
+ .boxed()
51
+ }
52
+
53
+ pub(crate) fn limit<'ts, 's: 'ts, I>() -> impl Parser<'ts, I, Limit, E<'s, 'ts>>
54
+ where
55
+ I: BorrowInput<'ts, Token = Token<'s>, Span = SimpleSpan>,
56
+ {
57
+ let op = select! {
58
+ Token::GreaterThan => Op::GreaterThan,
59
+ Token::LessThan => Op::LessThan,
60
+ }
61
+ .labelled("inequality operator");
62
+ let value = select! { Token::Number(n) => n.as_float() }.labelled("number");
63
+
64
+ // For example, x > 10.
65
+ let axis_op_value = group((axis(), op, value))
66
+ .map(|(axis, op, value)| Limit { axis, op, value })
67
+ .boxed();
68
+ // For example, 10 < x.
69
+ let value_op_axis = group((value, op, axis()))
70
+ .map(|(value, rev_op, axis)| Limit {
71
+ axis,
72
+ op: rev_op.reverse(),
73
+ value,
74
+ })
75
+ .boxed();
76
+
77
+ choice((axis_op_value, value_op_axis)).labelled("limit")
78
+ }
79
+
80
+ pub fn expr_parser<'ts, 's: 'ts, I, T: 'ts>(
81
+ atom: impl Parser<'ts, I, T, E<'s, 'ts>> + 'ts,
82
+ ) -> impl Parser<'ts, I, Expr<T>, E<'s, 'ts>>
83
+ where
84
+ I: BorrowInput<'ts, Token = Token<'s>, Span = SimpleSpan>,
85
+ {
86
+ use chumsky::pratt::*;
87
+
88
+ recursive(|expr| {
89
+ let atom = atom.map(Expr::Term).boxed();
90
+ let group = expr
91
+ .delimited_by(just(Token::ParenOpen), just(Token::ParenClose))
92
+ .boxed();
93
+ choice((atom, group))
94
+ .pratt((
95
+ prefix(3, just(Token::Not), |_op, rhs, _| Expr::Not(Box::new(rhs))).boxed(),
96
+ infix(left(2), just(Token::And), |lhs, _op, rhs, _| {
97
+ Expr::And(Box::new(lhs), Box::new(rhs))
98
+ })
99
+ .boxed(),
100
+ infix(left(1), just(Token::Or), |lhs, _op, rhs, _| {
101
+ Expr::Or(Box::new(lhs), Box::new(rhs))
102
+ })
103
+ .boxed(),
104
+ ))
105
+ .labelled("expression")
106
+ .as_context()
107
+ .boxed()
108
+ })
109
+ }
110
+ }
111
+
112
+ pub fn terms_parser<'ts, 's: 'ts, I>() -> impl Parser<'ts, I, Expr<String>, E<'s, 'ts>>
113
+ where
114
+ I: BorrowInput<'ts, Token = Token<'s>, Span = SimpleSpan>,
115
+ {
116
+ components::expr_parser(select! { Token::Ident(s) => s.to_owned() })
117
+ .labelled("combination expression")
118
+ .as_context()
119
+ }
120
+
121
+ pub fn limits_parser<'ts, 's: 'ts, I>() -> impl Parser<'ts, I, Expr<Limit>, E<'s, 'ts>>
122
+ where
123
+ I: BorrowInput<'ts, Token = Token<'s>, Span = SimpleSpan>,
124
+ {
125
+ components::expr_parser(components::limit())
126
+ .labelled("limits expression")
127
+ .as_context()
128
+ }
129
+
130
+ pub fn include_parser<'ts, 's: 'ts, I>() -> impl Parser<'ts, I, PathBuf, E<'s, 'ts>>
131
+ where
132
+ I: BorrowInput<'ts, Token = Token<'s>, Span = SimpleSpan>,
133
+ {
134
+ select! { Token::String(s) => s.into() }
135
+ }
136
+
137
+ pub(crate) fn compartment_parser<'ts, 's: 'ts, I>() -> impl Parser<'ts, I, Compartment, E<'s, 'ts>>
138
+ where
139
+ I: BorrowInput<'ts, Token = Token<'s>, Span = SimpleSpan>,
140
+ {
141
+ let number = select! { Token::Number(n) => n.as_float() as f32 }.boxed();
142
+ let id = select! { Token::Ident(id) => id.to_string() };
143
+
144
+ let sphere = {
145
+ let sphere_center = components::anchor();
146
+ let diameter = components::ident("diameter")
147
+ .ignore_then(number.clone())
148
+ .map(|d| d * 0.5);
149
+ let radius = components::ident("radius").ignore_then(number.clone());
150
+ components::ident("sphere")
151
+ .ignore_then(components::ident("at"))
152
+ .ignore_then(sphere_center)
153
+ .then_ignore(components::ident("with"))
154
+ .then(choice((diameter, radius)))
155
+ .map(|(center, radius)| Shape::Sphere { center, radius })
156
+ .boxed()
157
+ };
158
+ let cuboid = {
159
+ components::ident("cuboid")
160
+ .ignore_then(components::ident("from"))
161
+ .ignore_then(components::anchor())
162
+ .then_ignore(components::ident("to"))
163
+ .then(components::anchor())
164
+ .map(|(start, end)| Shape::Cuboid { start, end })
165
+ .boxed()
166
+ };
167
+ let voxels = components::ident("from")
168
+ .ignore_then(select! { Token::String(s) => s.into() }.labelled("quoted path"))
169
+ .map(Mask::Voxels)
170
+ .boxed();
171
+ let shape = components::ident("as")
172
+ .ignore_then(choice((sphere, cuboid)))
173
+ .map(Mask::Shape)
174
+ .boxed();
175
+ let limits = components::ident("where")
176
+ .ignore_then(limits_parser())
177
+ .map(Mask::Limits)
178
+ .boxed();
179
+ let within = components::ident("within")
180
+ .ignore_then(select! { Token::Number(n) => n.as_float() }.labelled("distance"))
181
+ .then_ignore(components::ident("of"))
182
+ .then(id.labelled("compartment id"))
183
+ .map(|(distance, id)| Mask::Within {
184
+ distance: distance as f32,
185
+ id,
186
+ })
187
+ .boxed();
188
+ let combination = components::ident("combines")
189
+ .ignore_then(terms_parser())
190
+ .map(Mask::Combination)
191
+ .labelled("compartment combination expression")
192
+ .boxed();
193
+ let all = group(["is", "all"].map(components::ident))
194
+ .to(Mask::All)
195
+ .boxed();
196
+
197
+ id.then(choice((voxels, shape, limits, within, combination, all)))
198
+ .map(|(id, mask)| Compartment { id, mask })
199
+ .labelled("compartment declaration")
200
+ .as_context()
201
+ }
202
+
203
+ pub(crate) fn constraint_parser<'ts, 's: 'ts, I>() -> impl Parser<'ts, I, Constraint, E<'s, 'ts>>
204
+ where
205
+ I: BorrowInput<'ts, Token = Token<'s>, Span = SimpleSpan>,
206
+ {
207
+ let id = select! { Token::Ident(id) => id.to_string() };
208
+ let rotation_axes = components::ident("rotates")
209
+ .ignore_then(
210
+ components::axis()
211
+ .separated_by(just(Token::Comma))
212
+ .collect::<Vec<_>>()
213
+ .map(|axes| Axes {
214
+ x: axes.contains(&Axis::X),
215
+ y: axes.contains(&Axis::Y),
216
+ z: axes.contains(&Axis::Z),
217
+ }),
218
+ )
219
+ .map(Rule::RotationAxes)
220
+ .boxed();
221
+
222
+ id.then(rotation_axes)
223
+ .map(|(id, rule)| Constraint { id, rule })
224
+ .labelled("constraint declaration")
225
+ .as_context()
226
+ }
227
+
228
+ pub(crate) fn segment_parser<'ts, 's: 'ts, I>() -> impl Parser<'ts, I, Segment, E<'s, 'ts>>
229
+ where
230
+ I: BorrowInput<'ts, Token = Token<'s>, Span = SimpleSpan>,
231
+ {
232
+ let identifier = select! { Token::Ident(ident) => ident.to_owned() };
233
+ let name = identifier;
234
+ let tag = identifier;
235
+ let id = name.then(just(Token::Colon).ignore_then(tag).or_not());
236
+ let number = select! { Token::Number(Number::Integer(n)) => n }.map(Quantity::Number);
237
+ let molarity = select! { Token::Concentration(c) => c }.map(Quantity::Concentration);
238
+ let quantity = molarity.or(number);
239
+ let from = components::ident("from");
240
+ let path = from.ignore_then(select! { Token::String(s) => s.into() });
241
+ let ids = select! { Token::Ident(id) => id.to_owned() }
242
+ .separated_by(just(Token::Comma))
243
+ .collect::<Vec<_>>()
244
+ .map(Into::into);
245
+ let compartments = components::ident("in").ignore_then(ids.clone());
246
+ let satisfies = components::ident("satisfies");
247
+ let rules = satisfies
248
+ .ignore_then(ids.labelled("rule names").as_context())
249
+ .or_not()
250
+ .map(Option::unwrap_or_default);
251
+
252
+ group((id, quantity, path, compartments, rules))
253
+ .map(
254
+ |((name, tag), quantity, path, compartment_ids, rules)| Segment {
255
+ name,
256
+ tag,
257
+ quantity,
258
+ path,
259
+ compartment_ids,
260
+ rules,
261
+ },
262
+ )
263
+ .labelled("segment declaration")
264
+ .as_context()
265
+ }
266
+
267
+ pub fn section_parser<'ts, 's: 'ts, I, T: 'ts>(
268
+ header: &'static str,
269
+ item: impl Parser<'ts, I, T, E<'s, 'ts>>,
270
+ ) -> impl Parser<'ts, I, Vec<T>, E<'s, 'ts>>
271
+ where
272
+ I: BorrowInput<'ts, Token = Token<'s>, Span = SimpleSpan>,
273
+ {
274
+ just(Token::Section(header))
275
+ .ignore_then(item.repeated().collect())
276
+ .labelled(format!("{header} section"))
277
+ .as_context()
278
+ }
279
+
280
+ pub fn general_parser<'ts, 's: 'ts, I>() -> impl Parser<'ts, I, General, E<'s, 'ts>>
281
+ where
282
+ I: BorrowInput<'ts, Token = Token<'s>, Span = SimpleSpan>,
283
+ {
284
+ enum Field {
285
+ Title(String),
286
+ Seed(u64),
287
+ BeadRadius(f64),
288
+ RearrangeMethod(RearrangeMethod),
289
+ MaxTriesMult(u64),
290
+ MaxTriesRotDiv(u64),
291
+ }
292
+
293
+ let title = components::ident("title")
294
+ .ignore_then(select! { Token::String(s) => s.to_string() }.labelled("quoted string"))
295
+ .map(Field::Title);
296
+ let seed = components::ident("seed")
297
+ .ignore_then(select! { Token::Number(Number::Integer(n)) => n })
298
+ .map(Field::Seed);
299
+ let bead_radius = components::ident("bead-radius")
300
+ .ignore_then(select! { Token::Number(n) => n.as_float() })
301
+ .map(Field::BeadRadius);
302
+ let rearrange_method = components::ident("rearrange")
303
+ .ignore_then(choice((
304
+ components::ident("moment").to(RearrangeMethod::Moment),
305
+ components::ident("volume").to(RearrangeMethod::Volume),
306
+ components::ident("bounding-sphere").to(RearrangeMethod::BoundingSphere),
307
+ components::ident("none").to(RearrangeMethod::None),
308
+ )))
309
+ .map(Field::RearrangeMethod);
310
+ let max_tries_mult = components::ident("max-tries-mult")
311
+ .ignore_then(select! { Token::Number(Number::Integer(n)) => n })
312
+ .map(Field::MaxTriesMult);
313
+ let max_tries_rot_div = components::ident("max-tries-rot-div")
314
+ .ignore_then(select! { Token::Number(Number::Integer(n)) => n })
315
+ .map(Field::MaxTriesRotDiv);
316
+
317
+ let fields = choice((
318
+ title,
319
+ seed,
320
+ bead_radius,
321
+ rearrange_method,
322
+ max_tries_mult,
323
+ max_tries_rot_div,
324
+ ))
325
+ .labelled("general declaration")
326
+ .as_context()
327
+ .repeated()
328
+ .collect::<Vec<_>>()
329
+ .map(|fields| {
330
+ let mut general = General::default();
331
+ for field in fields {
332
+ match field {
333
+ Field::Title(t) => general.title = Some(t),
334
+ Field::Seed(s) => general.seed = Some(s),
335
+ Field::BeadRadius(r) => general.bead_radius = Some(r),
336
+ Field::RearrangeMethod(m) => general.rearrange_method = Some(m),
337
+ Field::MaxTriesMult(n) => general.max_tries_mult = Some(n),
338
+ Field::MaxTriesRotDiv(n) => general.max_tries_rot_div = Some(n),
339
+ }
340
+ }
341
+ general
342
+ });
343
+
344
+ just(Token::Section("general"))
345
+ .ignore_then(fields)
346
+ .labelled("general section")
347
+ .as_context()
348
+ }
349
+
350
+ pub fn space_parser<'ts, 's: 'ts, I>() -> impl Parser<'ts, I, Space, E<'s, 'ts>>
351
+ where
352
+ I: BorrowInput<'ts, Token = Token<'s>, Span = SimpleSpan>,
353
+ {
354
+ enum Field {
355
+ Dimensions(Dimensions),
356
+ Resolution(f64),
357
+ Periodic(bool),
358
+ }
359
+
360
+ let dimensions = components::ident("dimensions")
361
+ .ignore_then(select! { Token::Point(point) => point })
362
+ .map(Field::Dimensions);
363
+ let resolution = components::ident("resolution")
364
+ .ignore_then(select! { Token::Number(n) => n.as_float() })
365
+ .map(Field::Resolution);
366
+ let periodic = components::ident("periodic")
367
+ .ignore_then(choice((
368
+ components::ident("true").to(true),
369
+ components::ident("false").to(false),
370
+ )))
371
+ .map(Field::Periodic);
372
+
373
+ let fields = choice((dimensions, resolution, periodic))
374
+ .labelled("space declaration")
375
+ .as_context()
376
+ .repeated()
377
+ .collect::<Vec<_>>()
378
+ .map(|fields| {
379
+ let mut space = Space::default();
380
+ for field in fields {
381
+ match field {
382
+ Field::Dimensions(d) => space.dimensions = Some(d),
383
+ Field::Resolution(r) => space.resolution = Some(r),
384
+ Field::Periodic(b) => space.periodic = Some(b),
385
+ }
386
+ }
387
+
388
+ space
389
+ });
390
+
391
+ just(Token::Section("space"))
392
+ .ignore_then(fields)
393
+ .labelled("space section")
394
+ .as_context()
395
+ }
396
+
397
+ pub fn includes_parser<'ts, 's: 'ts, I>() -> impl Parser<'ts, I, Vec<PathBuf>, E<'s, 'ts>>
398
+ where
399
+ I: BorrowInput<'ts, Token = Token<'s>, Span = SimpleSpan>,
400
+ {
401
+ section_parser("includes", include_parser())
402
+ }
403
+
404
+ pub fn compartments_parser<'ts, 's: 'ts, I>() -> impl Parser<'ts, I, Vec<Compartment>, E<'s, 'ts>>
405
+ where
406
+ I: BorrowInput<'ts, Token = Token<'s>, Span = SimpleSpan>,
407
+ {
408
+ section_parser("compartments", compartment_parser())
409
+ }
410
+
411
+ pub fn constraints_parser<'ts, 's: 'ts, I>() -> impl Parser<'ts, I, Vec<Constraint>, E<'s, 'ts>>
412
+ where
413
+ I: BorrowInput<'ts, Token = Token<'s>, Span = SimpleSpan>,
414
+ {
415
+ section_parser("constraints", constraint_parser())
416
+ }
417
+
418
+ pub fn segments_parser<'ts, 's: 'ts, I>() -> impl Parser<'ts, I, Vec<Segment>, E<'s, 'ts>>
419
+ where
420
+ I: BorrowInput<'ts, Token = Token<'s>, Span = SimpleSpan>,
421
+ {
422
+ section_parser("segments", segment_parser())
423
+ }
424
+
425
+ pub fn parser<'ts, 's: 'ts, I>() -> impl Parser<'ts, I, Config, E<'s, 'ts>>
426
+ where
427
+ I: BorrowInput<'ts, Token = Token<'s>, Span = SimpleSpan>,
428
+ {
429
+ enum Section {
430
+ General(General),
431
+ Space(Space),
432
+ Includes(Vec<PathBuf>),
433
+ Constraints(Vec<Constraint>),
434
+ Compartments(Vec<Compartment>),
435
+ Segments(Vec<Segment>),
436
+ }
437
+
438
+ let general = general_parser().map(Section::General);
439
+ let space = space_parser().map(Section::Space);
440
+ let includes = includes_parser().map(Section::Includes);
441
+ let constraints = constraints_parser().map(Section::Constraints);
442
+ let compartments = compartments_parser().map(Section::Compartments);
443
+ let segments = segments_parser().map(Section::Segments);
444
+
445
+ choice((
446
+ general,
447
+ space,
448
+ includes,
449
+ constraints,
450
+ compartments,
451
+ segments,
452
+ ))
453
+ .repeated()
454
+ .collect::<Vec<_>>()
455
+ .map(|sections| {
456
+ let mut general = General::default();
457
+ let mut space = Space::default();
458
+ let mut includes = Vec::new();
459
+ let mut constraints = Vec::new();
460
+ let mut compartments = Vec::new();
461
+ let mut segments = Vec::new();
462
+
463
+ for section in sections {
464
+ match section {
465
+ Section::General(g) => {
466
+ // TODO: Find something for this update pattern.
467
+ general = General {
468
+ title: g.title.or(general.title),
469
+ seed: g.seed.or(general.seed),
470
+ bead_radius: g.bead_radius.or(general.bead_radius),
471
+ rearrange_method: g.rearrange_method.or(general.rearrange_method),
472
+ max_tries_mult: g.max_tries_mult.or(general.max_tries_mult),
473
+ max_tries_rot_div: g.max_tries_rot_div.or(general.max_tries_rot_div),
474
+ }
475
+ }
476
+ Section::Space(s) => {
477
+ // TODO: Find something for this update pattern.
478
+ space = Space {
479
+ dimensions: s.dimensions.or(space.dimensions),
480
+ resolution: s.resolution.or(space.resolution),
481
+ periodic: s.periodic.or(space.periodic),
482
+ }
483
+ }
484
+ Section::Includes(items) => includes.extend(items),
485
+ Section::Constraints(items) => constraints.extend(items),
486
+ Section::Compartments(items) => compartments.extend(items),
487
+ Section::Segments(items) => segments.extend(items),
488
+ }
489
+ }
490
+
491
+ Config {
492
+ general,
493
+ space,
494
+ includes,
495
+ constraints,
496
+ compartments,
497
+ segments,
498
+ }
499
+ })
500
+ }
501
+
502
+ #[cfg(test)]
503
+ mod tests {
504
+ use chumsky::Parser;
505
+
506
+ use crate::core::config::bent::{lexer::lexer, make_input};
507
+
508
+ use super::*;
509
+
510
+ macro_rules! lex_and_parse {
511
+ ($parser:path => $src:ident) => {{
512
+ let src = $src;
513
+ // HACK: This sucks, but it allows us to move on and just write the nice tests.
514
+ let tokens = Box::new(lexer().parse(src).into_result().expect("correct tokens")).leak();
515
+ let tokens = make_input((0..src.len()).into(), tokens);
516
+ $parser().parse(tokens).into_result()
517
+ }};
518
+ }
519
+
520
+ mod components {
521
+ use super::*;
522
+
523
+ // Some helpers to ease the pain of boxing everything.
524
+ type E = Expr<Limit>;
525
+
526
+ fn limit(axis: Axis, op: Op, value: f64) -> E {
527
+ term(Limit { axis, op, value })
528
+ }
529
+
530
+ fn term(t: Limit) -> E {
531
+ E::Term(t)
532
+ }
533
+
534
+ fn not(e: E) -> E {
535
+ E::Not(Box::new(e))
536
+ }
537
+
538
+ fn or(lhs: E, rhs: E) -> E {
539
+ E::Or(Box::new(lhs), Box::new(rhs))
540
+ }
541
+
542
+ fn and(lhs: E, rhs: E) -> E {
543
+ E::And(Box::new(lhs), Box::new(rhs))
544
+ }
545
+
546
+ #[test]
547
+ fn terms() {
548
+ let src = "not ((a or b)) and (not c)";
549
+ let res = lex_and_parse!(terms_parser => src);
550
+ assert_eq!(
551
+ res,
552
+ Ok(Expr::And(
553
+ Box::new(Expr::Not(Box::new(Expr::Or(
554
+ Box::new(Expr::Term("a".to_string())),
555
+ Box::new(Expr::Term("b".to_string()))
556
+ )))),
557
+ Box::new(Expr::Not(Box::new(Expr::Term("c".to_string()))))
558
+ ))
559
+ );
560
+ }
561
+
562
+ #[test]
563
+ fn limits_parens() {
564
+ let src = "not ((x < 10 or (y > 3.1))) and (not 40 > z)";
565
+ let res = lex_and_parse!(limits_parser => src);
566
+ assert_eq!(
567
+ res,
568
+ Ok(and(
569
+ not(or(
570
+ limit(Axis::X, Op::LessThan, 10.0),
571
+ limit(Axis::Y, Op::GreaterThan, 3.1)
572
+ )),
573
+ not(limit(Axis::Z, Op::LessThan, 40.0))
574
+ ))
575
+ );
576
+ }
577
+
578
+ #[test]
579
+ fn limits_many_parens() {
580
+ let src = "(not ((x < 10 or (y > 3.1))) and (((not 40 > z))))";
581
+ let res = lex_and_parse!(limits_parser => src);
582
+ assert_eq!(
583
+ res,
584
+ Ok(and(
585
+ not(or(
586
+ limit(Axis::X, Op::LessThan, 10.0),
587
+ limit(Axis::Y, Op::GreaterThan, 3.1)
588
+ )),
589
+ not(limit(Axis::Z, Op::LessThan, 40.0))
590
+ ))
591
+ );
592
+ }
593
+
594
+ /// This expression can be handled nicely by the Pratt parser.
595
+ #[test]
596
+ fn limits_pratt() {
597
+ let src = "not x < 10 or y > 3.1 and not 40 > z";
598
+ let res = lex_and_parse!(limits_parser => src);
599
+ assert_eq!(
600
+ res,
601
+ Ok(or(
602
+ not(limit(Axis::X, Op::LessThan, 10.0)),
603
+ and(
604
+ limit(Axis::Y, Op::GreaterThan, 3.1),
605
+ not(limit(Axis::Z, Op::LessThan, 40.0))
606
+ )
607
+ ))
608
+ );
609
+ }
610
+ }
611
+
612
+ mod general {
613
+ use super::*;
614
+
615
+ #[test]
616
+ fn empty() {
617
+ let src = "[ general ]
618
+ ";
619
+ let res = lex_and_parse!(general_parser => src);
620
+ assert_eq!(res, Ok(General::default()))
621
+ }
622
+
623
+ #[test]
624
+ fn some() {
625
+ let src = r#"[ general ]
626
+ title "abc"
627
+ seed 1234"#;
628
+ let res = lex_and_parse!(general_parser => src);
629
+ assert_eq!(
630
+ res,
631
+ Ok(General {
632
+ title: Some("abc".to_string()),
633
+ seed: Some(1234),
634
+ ..Default::default()
635
+ })
636
+ )
637
+ }
638
+
639
+ #[test]
640
+ fn all() {
641
+ let src = r#"[ general ]
642
+ title "abc"
643
+ seed 1234
644
+ bead-radius 0.3
645
+
646
+ rearrange moment
647
+ max-tries-mult
648
+ 1000
649
+ max-tries-rot-div
650
+ 100
651
+ "#;
652
+ let res = lex_and_parse!(general_parser => src);
653
+ assert_eq!(
654
+ res,
655
+ Ok(General {
656
+ title: Some("abc".to_string()),
657
+ seed: Some(1234),
658
+ bead_radius: Some(0.3),
659
+ rearrange_method: Some(RearrangeMethod::Moment),
660
+ max_tries_mult: Some(1000),
661
+ max_tries_rot_div: Some(100),
662
+ })
663
+ )
664
+ }
665
+ }
666
+
667
+ mod space {
668
+ use super::*;
669
+
670
+ #[test]
671
+ fn empty() {
672
+ let src = "[ space ]
673
+ ";
674
+ let res = lex_and_parse!(space_parser => src);
675
+ assert_eq!(res, Ok(Space::default()))
676
+ }
677
+
678
+ #[test]
679
+ fn dimensions() {
680
+ let src = "[ space ]
681
+ dimensions 10,10,10.0";
682
+ let res = lex_and_parse!(space_parser => src);
683
+ assert_eq!(
684
+ res,
685
+ Ok(Space {
686
+ dimensions: Some([10.0; 3]),
687
+ resolution: None,
688
+ periodic: None
689
+ })
690
+ )
691
+ }
692
+
693
+ #[test]
694
+ fn all() {
695
+ let src = "[ space ]
696
+ dimensions 10,10,10.0
697
+ resolution 0.5
698
+ periodic false
699
+ ";
700
+ let res = lex_and_parse!(space_parser => src);
701
+ assert_eq!(
702
+ res,
703
+ Ok(Space {
704
+ dimensions: Some([10.0; 3]),
705
+ resolution: Some(0.5),
706
+ periodic: Some(false)
707
+ })
708
+ )
709
+ }
710
+ }
711
+
712
+ mod includes {
713
+ use super::*;
714
+
715
+ #[test]
716
+ fn include() {
717
+ let src = r#""forcefield/martini.itp""#;
718
+ let res = lex_and_parse!(include_parser => src);
719
+ assert_eq!(res, Ok("forcefield/martini.itp".into()))
720
+ }
721
+
722
+ #[test]
723
+ fn single() {
724
+ let src = r#"[ includes ]
725
+ "forcefield/martini.itp""#;
726
+ let res = lex_and_parse!(includes_parser => src);
727
+ assert_eq!(res, Ok(vec!["forcefield/martini.itp".into()]))
728
+ }
729
+
730
+ #[test]
731
+ fn multiple() {
732
+ let src = r#"[ includes ]
733
+ "forcefield/martini.itp"
734
+ "structures/*.itp""#;
735
+ let res = lex_and_parse!(includes_parser => src);
736
+ assert_eq!(
737
+ res,
738
+ Ok(vec![
739
+ "forcefield/martini.itp".into(),
740
+ "structures/*.itp".into()
741
+ ])
742
+ )
743
+ }
744
+
745
+ #[test]
746
+ fn single_with_trailing_space() {
747
+ let src = "[ includes ]\t\t
748
+ \"forcefield/martini.itp\" ";
749
+ let res = lex_and_parse!(includes_parser => src);
750
+ assert_eq!(res, Ok(vec!["forcefield/martini.itp".into()]))
751
+ }
752
+
753
+ #[test]
754
+ fn quoted_path_with_spaces() {
755
+ let src = r#"[ includes ]
756
+ "quoted-without-spaces"
757
+ "quoted with spaces"
758
+ "#;
759
+ let res = lex_and_parse!(includes_parser => src);
760
+ assert_eq!(
761
+ res,
762
+ Ok(vec![
763
+ "quoted-without-spaces".into(),
764
+ "quoted with spaces".into(),
765
+ ])
766
+ )
767
+ }
768
+ }
769
+
770
+ mod compartment {
771
+ use super::*;
772
+
773
+ #[test]
774
+ fn all() {
775
+ let src = "space is all";
776
+ let res = lex_and_parse!(compartment_parser => src);
777
+ assert_eq!(
778
+ res,
779
+ Ok(Compartment {
780
+ id: "space".to_string(),
781
+ mask: Mask::All,
782
+ })
783
+ )
784
+ }
785
+
786
+ #[test]
787
+ fn sphere_center() {
788
+ let src = "space as sphere at center with radius 10.0";
789
+ let res = lex_and_parse!(compartment_parser => src);
790
+ assert_eq!(
791
+ res,
792
+ Ok(Compartment {
793
+ id: "space".to_string(),
794
+ mask: Mask::Shape(Shape::Sphere {
795
+ center: Anchor::Center,
796
+ radius: 10.0
797
+ }),
798
+ })
799
+ )
800
+ }
801
+
802
+ #[test]
803
+ fn sphere_point() {
804
+ let src = "space as sphere at 1,2.0,3 with radius 10.0";
805
+ let res = lex_and_parse!(compartment_parser => src);
806
+ assert_eq!(
807
+ res,
808
+ Ok(Compartment {
809
+ id: "space".to_string(),
810
+ mask: Mask::Shape(Shape::Sphere {
811
+ center: Anchor::Point([1.0, 2.0, 3.0]),
812
+ radius: 10.0
813
+ }),
814
+ })
815
+ )
816
+ }
817
+
818
+ #[test]
819
+ fn sphere_start() {
820
+ let src = "space as sphere at start with radius 10.0";
821
+ let res = lex_and_parse!(compartment_parser => src);
822
+ assert_eq!(
823
+ res,
824
+ Ok(Compartment {
825
+ id: "space".to_string(),
826
+ mask: Mask::Shape(Shape::Sphere {
827
+ center: Anchor::Start,
828
+ radius: 10.0
829
+ }),
830
+ })
831
+ )
832
+ }
833
+
834
+ #[test]
835
+ fn sphere_end() {
836
+ let src = "space as sphere at end with radius 10.0";
837
+ let res = lex_and_parse!(compartment_parser => src);
838
+ assert_eq!(
839
+ res,
840
+ Ok(Compartment {
841
+ id: "space".to_string(),
842
+ mask: Mask::Shape(Shape::Sphere {
843
+ center: Anchor::End,
844
+ radius: 10.0
845
+ }),
846
+ })
847
+ )
848
+ }
849
+
850
+ #[test]
851
+ fn sphere_diameter() {
852
+ let src = "space as sphere at center with diameter 20.0";
853
+ let res = lex_and_parse!(compartment_parser => src);
854
+ assert_eq!(
855
+ res,
856
+ Ok(Compartment {
857
+ id: "space".to_string(),
858
+ mask: Mask::Shape(Shape::Sphere {
859
+ center: Anchor::Center,
860
+ radius: 10.0
861
+ }),
862
+ })
863
+ )
864
+ }
865
+
866
+ #[test]
867
+ fn cuboid_anchored() {
868
+ let src = "space as cuboid from center to end";
869
+ let res = lex_and_parse!(compartment_parser => src);
870
+ assert_eq!(
871
+ res,
872
+ Ok(Compartment {
873
+ id: "space".to_string(),
874
+ mask: Mask::Shape(Shape::Cuboid {
875
+ start: Anchor::Center,
876
+ end: Anchor::End
877
+ })
878
+ })
879
+ )
880
+ }
881
+
882
+ #[test]
883
+ fn cuboid_point() {
884
+ let src = "space as cuboid from 3,14,1.5 to end";
885
+ let res = lex_and_parse!(compartment_parser => src);
886
+ assert_eq!(
887
+ res,
888
+ Ok(Compartment {
889
+ id: "space".to_string(),
890
+ mask: Mask::Shape(Shape::Cuboid {
891
+ start: Anchor::Point([3.0, 14.0, 1.5]),
892
+ end: Anchor::End
893
+ })
894
+ })
895
+ )
896
+ }
897
+
898
+ #[test]
899
+ fn close() {
900
+ let src = "close within 5 of mask";
901
+ let res = lex_and_parse!(compartment_parser => src);
902
+ assert_eq!(
903
+ res,
904
+ Ok(Compartment {
905
+ id: "close".to_string(),
906
+ mask: Mask::Within {
907
+ distance: 5.0,
908
+ id: "mask".to_string()
909
+ }
910
+ })
911
+ )
912
+ }
913
+
914
+ #[test]
915
+ fn limits() {
916
+ let src = "limited where not x > 10.0 and 3 < y";
917
+ let res = lex_and_parse!(compartment_parser => src);
918
+ assert_eq!(
919
+ res,
920
+ Ok(Compartment {
921
+ id: "limited".to_string(),
922
+ mask: Mask::Limits(Expr::And(
923
+ Box::new(Expr::Not(Box::new(Expr::Term(Limit {
924
+ axis: Axis::X,
925
+ op: Op::GreaterThan,
926
+ value: 10.0
927
+ })))),
928
+ Box::new(Expr::Term(Limit {
929
+ axis: Axis::Y,
930
+ op: Op::GreaterThan,
931
+ value: 3.0
932
+ }))
933
+ ))
934
+ })
935
+ )
936
+ }
937
+
938
+ #[test]
939
+ fn limits_simple() {
940
+ let src = "simple-example where x > 10.0";
941
+ let res = lex_and_parse!(compartment_parser => src);
942
+ assert_eq!(
943
+ res,
944
+ Ok(Compartment {
945
+ id: "simple-example".to_string(),
946
+ mask: Mask::Limits(Expr::Term(Limit {
947
+ axis: Axis::X,
948
+ op: Op::GreaterThan,
949
+ value: 10.0
950
+ })),
951
+ })
952
+ )
953
+ }
954
+
955
+ #[test]
956
+ fn combination() {
957
+ let src = "space combines not ((a) and b) or (c or d)";
958
+ let res = lex_and_parse!(compartment_parser => src);
959
+ assert_eq!(
960
+ res,
961
+ Ok(Compartment {
962
+ id: "space".to_string(),
963
+ mask: Mask::Combination(Expr::Or(
964
+ Box::new(Expr::Not(Box::new(Expr::And(
965
+ Box::new(Expr::Term("a".to_string(),)),
966
+ Box::new(Expr::Term("b".to_string(),)),
967
+ )),)),
968
+ Box::new(Expr::Or(
969
+ Box::new(Expr::Term("c".to_string(),)),
970
+ Box::new(Expr::Term("d".to_string(),)),
971
+ )),
972
+ ),),
973
+ })
974
+ )
975
+ }
976
+
977
+ #[test]
978
+ fn combination_weird_whitespace() {
979
+ let src = "space combines a";
980
+ let res = lex_and_parse!(compartment_parser => src);
981
+ assert_eq!(
982
+ res,
983
+ Ok(Compartment {
984
+ id: "space".to_string(),
985
+ mask: Mask::Combination(Expr::Term("a".to_string()))
986
+ })
987
+ )
988
+ }
989
+
990
+ #[test]
991
+ fn section_single() {
992
+ let src = "[ compartments ]
993
+
994
+ simple-example where x > 10.0";
995
+ let res = lex_and_parse!(compartments_parser => src);
996
+ assert_eq!(
997
+ res,
998
+ Ok(vec![Compartment {
999
+ id: "simple-example".to_string(),
1000
+ mask: Mask::Limits(Expr::Term(Limit {
1001
+ axis: Axis::X,
1002
+ op: Op::GreaterThan,
1003
+ value: 10.0
1004
+ })),
1005
+ }])
1006
+ )
1007
+ }
1008
+
1009
+ #[test]
1010
+ fn section_double() {
1011
+ let src = "[ compartments ]
1012
+ simple-example where x > 10.0
1013
+ simple-example where x > 10.0";
1014
+ let res = lex_and_parse!(compartments_parser => src);
1015
+ assert_eq!(
1016
+ res,
1017
+ Ok(vec![
1018
+ Compartment {
1019
+ id: "simple-example".to_string(),
1020
+ mask: Mask::Limits(Expr::Term(Limit {
1021
+ axis: Axis::X,
1022
+ op: Op::GreaterThan,
1023
+ value: 10.0
1024
+ })),
1025
+ };
1026
+ 2
1027
+ ])
1028
+ )
1029
+ }
1030
+ }
1031
+
1032
+ mod segment {
1033
+ use super::*;
1034
+
1035
+ fn base_segment() -> Segment {
1036
+ Segment {
1037
+ name: "3lyz".to_string(),
1038
+ tag: None,
1039
+ quantity: Quantity::Number(1),
1040
+ path: "structures/3lyz.gro".into(),
1041
+ compartment_ids: vec!["sphere".to_string()].into(),
1042
+ rules: vec!["rule".to_string()].into(),
1043
+ }
1044
+ }
1045
+
1046
+ #[test]
1047
+ fn tag() {
1048
+ let src = r#"3lyz:tag 1 from "structures/3lyz.gro" in sphere satisfies rule"#;
1049
+ let res = lex_and_parse!(segment_parser => src);
1050
+ assert_eq!(
1051
+ res,
1052
+ Ok(Segment {
1053
+ name: "3lyz".to_string(),
1054
+ tag: Some("tag".to_string()),
1055
+ ..base_segment()
1056
+ })
1057
+ )
1058
+ }
1059
+
1060
+ #[test]
1061
+ fn no_tag() {
1062
+ let src = r#"3lyz 1 from "structures/3lyz.gro" in sphere satisfies rule"#;
1063
+ let res = lex_and_parse!(segment_parser => src);
1064
+ assert_eq!(
1065
+ res,
1066
+ Ok(Segment {
1067
+ name: "3lyz".to_string(),
1068
+ tag: None,
1069
+ ..base_segment()
1070
+ })
1071
+ )
1072
+ }
1073
+
1074
+ #[test]
1075
+ fn number() {
1076
+ let src = r#"3lyz 1000120 from "structures/3lyz.gro" in sphere satisfies rule"#;
1077
+ let res = lex_and_parse!(segment_parser => src);
1078
+ assert_eq!(
1079
+ res,
1080
+ Ok(Segment {
1081
+ quantity: Quantity::Number(1000120),
1082
+ ..base_segment()
1083
+ })
1084
+ )
1085
+ }
1086
+
1087
+ #[test]
1088
+ fn concentration() {
1089
+ let src = r#"3lyz 0.05M from "structures/3lyz.gro" in sphere satisfies rule"#;
1090
+ let res = lex_and_parse!(segment_parser => src);
1091
+ assert_eq!(
1092
+ res,
1093
+ Ok(Segment {
1094
+ quantity: Quantity::Concentration(0.05),
1095
+ ..base_segment()
1096
+ })
1097
+ )
1098
+ }
1099
+
1100
+ #[test]
1101
+ fn two_compartments() {
1102
+ let src = r#"3lyz 1 from "structures/3lyz.gro" in a,b satisfies rule"#;
1103
+ let res = lex_and_parse!(segment_parser => src);
1104
+ assert_eq!(
1105
+ res,
1106
+ Ok(Segment {
1107
+ compartment_ids: vec!["a".to_string(), "b".to_string()].into(),
1108
+ ..base_segment()
1109
+ })
1110
+ )
1111
+ }
1112
+
1113
+ #[test]
1114
+ fn no_rules() {
1115
+ let src = r#"3lyz 1 from "structures/3lyz.gro" in sphere"#;
1116
+ let res = lex_and_parse!(segment_parser => src);
1117
+ assert_eq!(
1118
+ res,
1119
+ Ok(Segment {
1120
+ rules: Default::default(),
1121
+ ..base_segment()
1122
+ })
1123
+ )
1124
+ }
1125
+
1126
+ #[test]
1127
+ fn two_rules() {
1128
+ let src = r#"3lyz 1 from "structures/3lyz.gro" in sphere satisfies rule1,rule2"#;
1129
+ let res = lex_and_parse!(segment_parser => src);
1130
+ assert_eq!(
1131
+ res,
1132
+ Ok(Segment {
1133
+ rules: vec!["rule1".to_string(), "rule2".to_string()].into(),
1134
+ ..base_segment()
1135
+ })
1136
+ )
1137
+ }
1138
+
1139
+ #[test]
1140
+ fn section_normal() {
1141
+ let src = r#"[ segments ]
1142
+ 3lyz 1 from "structures/3lyz.gro" in sphere satisfies rule
1143
+ 3lyz 1 from "structures/3lyz.gro" in sphere satisfies rule"#;
1144
+ let res = lex_and_parse!(segments_parser => src);
1145
+ assert_eq!(res, Ok(vec![base_segment(); 2]));
1146
+ }
1147
+
1148
+ #[test]
1149
+ fn section_weird_whitespace_lines() {
1150
+ let src = "[ segments ]
1151
+ \t
1152
+ 3lyz 1 from \"structures/3lyz.gro\" in sphere satisfies rule
1153
+
1154
+
1155
+
1156
+ 3lyz 1 from \"structures/3lyz.gro\" in sphere satisfies rule
1157
+ ";
1158
+ let res = lex_and_parse!(segments_parser => src);
1159
+ assert_eq!(res, Ok(vec![base_segment(); 2]));
1160
+ }
1161
+
1162
+ #[test]
1163
+ fn section_wrapped_line() {
1164
+ let src = r#"[ segments ]
1165
+
1166
+ 3lyz
1167
+ 1
1168
+ from "structures/3lyz.gro"
1169
+ in sphere
1170
+ satisfies rule
1171
+ 3lyz 1 from
1172
+
1173
+ "structures/3lyz.gro"
1174
+ in sphere satisfies rule
1175
+ "#;
1176
+ let res = lex_and_parse!(segments_parser => src);
1177
+ assert_eq!(res, Ok(vec![base_segment(); 2]));
1178
+ }
1179
+ }
1180
+ }