@@ -293,16 +293,17 @@ struct ReactionStruct
293
293
substrates:: Vector{ReactantStruct}
294
294
products:: Vector{ReactantStruct}
295
295
rate:: ExprValues
296
- only_use_rate :: Bool
296
+ metadata :: Expr
297
297
298
- function ReactionStruct (sub_line:: ExprValues , prod_line:: ExprValues , rate:: ExprValues ,
299
- only_use_rate :: Bool )
298
+ function ReactionStruct (sub_line:: ExprValues , prod_line:: ExprValues , rate:: ExprValues ,
299
+ metadata_line :: ExprValues )
300
300
sub = recursive_find_reactants! (sub_line, 1 , Vector {ReactantStruct} (undef, 0 ))
301
301
prod = recursive_find_reactants! (prod_line, 1 , Vector {ReactantStruct} (undef, 0 ))
302
- new (sub, prod, rate, only_use_rate)
302
+ metadata = extract_metadata (metadata_line)
303
+ new (sub, prod, rate, metadata)
303
304
end
304
305
end
305
-
306
+
306
307
# ## Functions rephrasing the macro input as a ReactionSystem structure. ###
307
308
308
309
function forbidden_variable_check (v)
@@ -445,7 +446,7 @@ function make_reaction(ex::Expr)
445
446
pexprs = get_pexpr (parameters, Dict {Symbol, Expr} ())
446
447
rxexpr = get_rxexprs (reaction)
447
448
iv = :(@variables $ (DEFAULT_IV_SYM))
448
-
449
+
449
450
# Returns the rephrased expression.
450
451
quote
451
452
$ pexprs
@@ -565,8 +566,8 @@ function get_rxexprs(rxstruct)
565
566
prod_init = isempty (rxstruct. products) ? nothing : :([])
566
567
prod_stoich_init = deepcopy (prod_init)
567
568
reaction_func = :(Reaction ($ (recursive_expand_functions! (rxstruct. rate)), $ subs_init,
568
- $ prod_init, $ subs_stoich_init, $ prod_stoich_init,
569
- only_use_rate = $ (rxstruct. only_use_rate) ))
569
+ $ prod_init, $ subs_stoich_init, $ prod_stoich_init,
570
+ metadata = $ (rxstruct. metadata), ))
570
571
for sub in rxstruct. substrates
571
572
push! (reaction_func. args[3 ]. args, sub. reactant)
572
573
push! (reaction_func. args[5 ]. args, sub. stoichiometry)
@@ -580,70 +581,87 @@ end
580
581
581
582
# ## Functions for extracting the reactions from a DSL expression, and putting them ReactionStruct vector. ###
582
583
583
- # Reads a line and creates the corresponding ReactionStruct.
584
+ # Reads a single line and creates the corresponding ReactionStruct.
584
585
function get_reaction (line)
585
- (rate, r_line) = line. args
586
- (r_line. head == :--> ) && (r_line = Expr (:call , :→ , r_line. args[1 ], r_line. args[2 ]))
587
-
588
- arrow = r_line. args[1 ]
589
- in (arrow, double_arrows) && error (" Double arrows not allowed for single reactions." )
590
-
591
- only_use_rate = in (arrow, pure_rate_arrows)
592
- if in (arrow, fwd_arrows)
593
- rs = create_ReactionStruct (r_line. args[2 ], r_line. args[3 ], rate, only_use_rate)
594
- elseif in (arrow, bwd_arrows)
595
- rs = create_ReactionStruct (r_line. args[3 ], r_line. args[2 ], rate, only_use_rate)
596
- else
597
- throw (" Malformed reaction, invalid arrow type used in: $(MacroTools. striplines (line)) " )
586
+ reaction = get_reactions ([line])
587
+ if (length (reaction) != 1 )
588
+ error (" Malformed reaction. @reaction macro only creates a single reaction. E.g. double arrows, such as `<-->` are not supported." )
598
589
end
599
-
600
- rs
590
+ return reaction[1 ]
601
591
end
592
+
602
593
# Generates a vector containing a number of reaction structures, each containing the information about one reaction.
603
594
function get_reactions (exprs:: Vector{Expr} , reactions = Vector {ReactionStruct} (undef, 0 ))
604
595
for line in exprs
605
- (rate, r_line) = line . args
606
- (r_line . head == : --> ) && (r_line = Expr ( :call , : → , r_line . args[ 1 ], r_line . args[ 2 ]) )
596
+ # Reads core reaction information.
597
+ arrow, rate, reaction, metadata = read_reaction_line (line )
607
598
608
- arrow = r_line. args[1 ]
609
- only_use_rate = in (arrow, pure_rate_arrows)
599
+ # Checks the type of arrow used, and creates the corresponding reaction(s). Returns them in an array.
610
600
if in (arrow, double_arrows)
611
- ( typeof (rate) == Expr && rate. head == :tuple ) ||
601
+ if typeof (rate) != Expr || rate. head != :tuple
612
602
error (" Error: Must provide a tuple of reaction rates when declaring a bi-directional reaction." )
613
- push_reactions! (reactions, r_line. args[2 ], r_line. args[3 ], rate. args[1 ],
614
- only_use_rate)
615
- push_reactions! (reactions, r_line. args[3 ], r_line. args[2 ], rate. args[2 ],
616
- only_use_rate)
603
+ end
604
+ push_reactions! (reactions, reaction. args[2 ], reaction. args[3 ], rate. args[1 ], metadata. args[1 ], arrow)
605
+ push_reactions! (reactions, reaction. args[3 ], reaction. args[2 ], rate. args[2 ], metadata. args[2 ], arrow)
617
606
elseif in (arrow, fwd_arrows)
618
- push_reactions! (reactions, r_line . args[2 ], r_line . args[3 ], rate, only_use_rate )
607
+ push_reactions! (reactions, reaction . args[2 ], reaction . args[3 ], rate, metadata, arrow )
619
608
elseif in (arrow, bwd_arrows)
620
- push_reactions! (reactions, r_line . args[3 ], r_line . args[2 ], rate, only_use_rate )
609
+ push_reactions! (reactions, reaction . args[3 ], reaction . args[2 ], rate, metadata, arrow )
621
610
else
622
611
throw (" Malformed reaction, invalid arrow type used in: $(MacroTools. striplines (line)) " )
623
612
end
624
613
end
625
- reactions
626
- end
614
+ return reactions
615
+ end
616
+
617
+ # Extracts the rate, reaction, and metadata fields (the last one optional) from a reaction line.
618
+ function read_reaction_line (line:: Expr )
619
+ # Handles rate, reaction, and arrow.
620
+ # Special routine required for the`-->` case, which creates different expression from all other cases.
621
+ rate = line. args[1 ]
622
+ reaction = line. args[2 ]
623
+ (reaction. head == :--> ) && (reaction = Expr (:call , :→ , reaction. args[1 ], reaction. args[2 ]))
624
+ arrow = reaction. args[1 ]
625
+
626
+ # Handles metadata. If not provided, empty metadata is created.
627
+ if length (line. args) == 2
628
+ metadata = in (arrow, double_arrows) ? :(([], [])) : :([])
629
+ elseif length (line. args) == 3
630
+ metadata = line. args[3 ]
631
+ else
632
+ error (" The following reaction line: \" $line \" was malformed. It should have a form \" rate, reaction\" or a form \" rate, reaction, metadata\" . It has neither." )
633
+ end
627
634
628
- # Creates a ReactionStruct from the information in a single line.
629
- function create_ReactionStruct (sub_line:: ExprValues , prod_line:: ExprValues ,
630
- rate:: ExprValues , only_use_rate:: Bool )
631
- all (== (1 ), (tup_leng (sub_line), tup_leng (prod_line), tup_leng (rate))) ||
632
- error (" Malformed reaction, line appears to be defining multiple reactions incorrectly: rate=$rate , subs=$sub_line , prods=$prod_line ." )
633
- ReactionStruct (get_tup_arg (sub_line, 1 ), get_tup_arg (prod_line, 1 ),
634
- get_tup_arg (rate, 1 ), only_use_rate)
635
+ return arrow, rate, reaction, metadata
635
636
end
636
637
637
- # Takes a reaction line and creates reactions from it and pushes those to the reaction array. Used to create multiple reactions from, for instance, 1.0, (X,Y) --> 0.
638
- function push_reactions! (reactions:: Vector{ReactionStruct} , sub_line:: ExprValues ,
639
- prod_line:: ExprValues , rate:: ExprValues , only_use_rate:: Bool )
640
- lengs = (tup_leng (sub_line), tup_leng (prod_line), tup_leng (rate))
641
- for i in 1 : maximum (lengs)
642
- (count (lengs .== 1 ) + count (lengs .== maximum (lengs)) < 3 ) &&
643
- (throw (" Malformed reaction, rate=$rate , subs=$sub_line , prods=$prod_line ." ))
644
- push! (reactions,
645
- ReactionStruct (get_tup_arg (sub_line, i), get_tup_arg (prod_line, i),
646
- get_tup_arg (rate, i), only_use_rate))
638
+ # Takes a reaction line and creates reaction(s) from it and pushes those to the reaction array.
639
+ # Used to create multiple reactions from, for instance, `k, (X,Y) --> 0`.
640
+ # Handles metadata, e.g. `1.0, Z --> 0, [noisescaling=η]`.
641
+ function push_reactions! (reactions:: Vector{ReactionStruct} , sub_line:: ExprValues , prod_line:: ExprValues ,
642
+ rate:: ExprValues , metadata:: ExprValues , arrow:: Symbol )
643
+ # The rates, substrates, products, and metadata may be in a tupple form (e.g. `k, (X,Y) --> 0`).
644
+ # This finds the length of these tuples (or 1 if not in tuple forms). Errors if lengs inconsistent.
645
+ lengs = (tup_leng (sub_line), tup_leng (prod_line), tup_leng (rate), tup_leng (metadata))
646
+ if any (! (leng == 1 || leng == maximum (lengs)) for leng in lengs)
647
+ throw (" Malformed reaction, rate=$rate , subs=$sub_line , prods=$prod_line , metadata=$metadata ." )
648
+ end
649
+
650
+ # Loops through each reaction encoded by the reaction composites. Adds the reaction to the reactions vector.
651
+ for i in 1 : maximum (lengs)
652
+ # If the `only_use_rate` metadata was not provided, this has to be infered from the arrow used.
653
+ metadata_i = get_tup_arg (metadata, i)
654
+ if all (arg. args[1 ] != :only_use_rate for arg in metadata_i. args)
655
+ push! (metadata_i. args, :(only_use_rate = $ (in (arrow, pure_rate_arrows))))
656
+ end
657
+
658
+ # Checks that metadata fields are unqiue.
659
+ if ! allunique (arg. args[1 ] for arg in metadata_i. args)
660
+ error (" Some reaction metadata fields where repeated: $(metadata_entries) " )
661
+ end
662
+
663
+ push! (reactions, ReactionStruct (get_tup_arg (sub_line, i), get_tup_arg (prod_line, i),
664
+ get_tup_arg (rate, i), metadata_i))
647
665
end
648
666
end
649
667
@@ -687,6 +705,17 @@ function recursive_find_reactants!(ex::ExprValues, mult::ExprValues,
687
705
reactants
688
706
end
689
707
708
+ # Finds the metadata from a metadata expresion (`[key=val, ...]`) and returns as a Vector{Pair{Symbol,ExprValues}}.
709
+ function extract_metadata (metadata_line:: Expr )
710
+ metadata = :([])
711
+ for arg in metadata_line. args
712
+ (arg. head != :(= )) && error (" Malformatted metadata line: $metadata_line . Each entry in the vector should contain a `=`." )
713
+ (arg. args[1 ] isa Symbol) || error (" Malformatted metadata entry: $arg . Entries left-hand-side should be a single symbol." )
714
+ push! (metadata. args, :($ (QuoteNode (arg. args[1 ])) => $ (arg. args[2 ])))
715
+ end
716
+ return metadata
717
+ end
718
+
690
719
# ## DSL Options Handling ###
691
720
# Most options handled in previous sections, when code re-organised, these should ideally be moved to the same place.
692
721
888
917
# end
889
918
# return defaults
890
919
# end
920
+
921
+
922
+ # # Creates a ReactionStruct from the information in a single line.
923
+ # function create_ReactionStruct(sub_line::ExprValues, prod_line::ExprValues,
924
+ # rate::ExprValues, only_use_rate::Bool)
925
+ # all(==(1), (tup_leng(sub_line), tup_leng(prod_line), tup_leng(rate))) ||
926
+ # error("Malformed reaction, line appears to be defining multiple reactions incorrectly: rate=$rate, subs=$sub_line, prods=$prod_line.")
927
+ # ReactionStruct(get_tup_arg(sub_line, 1), get_tup_arg(prod_line, 1),
928
+ # get_tup_arg(rate, 1), only_use_rate)
929
+ # end
0 commit comments