exception Bad of string
let raise_bad msg = raise (Bad msg)
type sb_math_operator =
| MPlus
| MMinus
| MTimes
| MDivide
| MPower
| MRoot
| MAbs
| MExp
| MLn
| MLog
| MFloor
| MCeiling
| MFactorial
| MEq
| MNeq
| MGt
| MLt
| MGeq
| MLeq
| MAnd
| MOr
| MXor
| MNot
| MSin
| MCos
| MTan
| MArcsin
| MArccos
| MArctan
| MDelay
| MFundef of string
type sb_math =
| MApply of sb_math_operator * (sb_math list)
| MLambda of (string list) * sb_math
| MPiecewise of ((string * sb_math) list) * string
| MFloatNumber of float
| MIntNumber of int
| MIdentifier of string
| MTime
| MTrue
| MFalse
| MNAN
| MPi
| MExponent
| MInfinity
| MNoMath
type sb_unit = {
unit_kind: string;
unit_exponent: int;
unit_scale: int;
unit_multiplier: float;
}
type sb_function_definition = {
fundef_id: string;
fundef_name: string;
fundef_math: sb_math;
}
type sb_unit_definition = {
unitdef_id: string;
unitdef_name: string;
unitdef_unitlist: sb_unit list;
}
type sb_compartment = {
compart_id: string;
compart_name: string;
compart_spatialDimensions: int;
compart_size: float;
compart_units: string;
compart_outside: string;
compart_constant: bool;
}
type sb_species_ref = {
specref_species: string;
specref_id: string;
specref_name: string;
specref_stoichiometry: int;
}
type sb_species = {
species_id: string;
species_name: string;
species_type: string;
species_compartment: string;
species_initialAmount: float;
species_initialConcentration: float;
species_substanceUnits: string;
species_hasOnlySubstanceUnits: bool;
species_boundaryCondition: bool;
species_constant: bool;
}
type sb_parameter = {
param_id: string;
param_name: string;
param_value: float;
param_units: string;
param_constant: bool;
}
type sb_kinetic_law = {
klaw_math: sb_math;
klaw_parameters: sb_parameter list;
}
type sb_reaction = {
react_id: string;
react_name: string;
react_boundaryCondition: bool;
react_fast: bool;
react_reactants: sb_species_ref list;
react_products: sb_species_ref list;
react_kineticLaw: sb_kinetic_law;
}
type sb_initial_assignment = {
ia_symbol: string;
ia_math: sb_math;
}
type sb_algebraic_rule = {
ar_math: sb_math;
}
type sb_generic_rule = {
gr_variable: string;
gr_math: sb_math;
}
type sb_rule = RateRule of sb_generic_rule | AssignmentRule of sb_generic_rule | AlgebraicRule of sb_algebraic_rule
type sb_math_container = {
math: sb_math;
}
type sb_delay = Delay of sb_math_container
type sb_trigger = Trigger of sb_math_container
type sb_event_assignment = {
ea_variable: string;
ea_math: sb_math;
}
type sb_event = {
event_id: string;
event_name: string;
event_useValuesFromTriggerTime: bool;
event_trigger: sb_trigger;
event_delay: sb_delay;
event_assignments: sb_event_assignment list;
}
type sb_L = LFunctionDefinition of sb_function_definition | LUnitDefinition of sb_unit_definition |
LCompartment of sb_compartment | LSpecies of sb_species | LReaction of sb_reaction |
LParameter of sb_parameter | LInitialAssignment of sb_initial_assignment | LRule of sb_rule |
LEvent of sb_event | LEventAssignment of sb_event_assignment | LSpecieRef of sb_species_ref |
LUnit of sb_unit
type sb_model = {
sbm_id: string;
sbm_name: string;
sbm_functionDefinitions : sb_function_definition list;
sbm_unitDefinitions : sb_unit_definition list;
sbm_compartments : sb_compartment list;
sbm_species : sb_species list;
sbm_reactions : sb_reaction list;
sbm_parameters : sb_parameter list;
sbm_initialAssignments : sb_initial_assignment list;
sbm_rules : sb_rule list;
sbm_events : sb_event list;
}
module MathML = struct
let rec math_to_string math =
let operator_to_string oper =
match oper with
| MPlus -> "+"
| MMinus -> "-"
| MTimes -> "*"
| MPower -> "^"
| MAbs -> "ABS"
| MExp -> "EXP"
| MFactorial -> "FACTORIAL"
| MCeiling -> "CEILING"
| MLt -> "<"
| MGt -> ">"
| MLeq -> "<="
| MGeq -> ">="
| MDelay -> "DELAY"
| MFundef oper -> oper
| _ -> raise_bad "can't convert unknown operator"
in
match math with
| MApply (oper, exprlist) -> "(" ^ (operator_to_string oper) ^ " " ^ (String.concat " " (List.map math_to_string exprlist)) ^ ")"
| MLambda (bvarlist, lambda_expr) -> "(LAMBDA (" ^ (String.concat " " bvarlist) ^ ") " ^ (math_to_string lambda_expr) ^ ")"
| MPiecewise (piecelist, otherwise) -> "(PIECEWISE " ^ (String.concat " " (List.map
(fun next -> let (var, varexpr) = next in "(" ^ (math_to_string varexpr) ^ " " ^ var ^ ")") piecelist))
^ " " ^ otherwise ^ ")"
| MFloatNumber f -> (string_of_float f)
| MIntNumber i -> (string_of_int i)
| MIdentifier s -> s
| MTime -> "<time>"
| MExponent -> "e"
| MNoMath -> "/no math/"
| _ -> raise_bad "can't convert unknown math expr"
let extract_string i depth errmsg =
let rec skip_tags i depth =
if depth > 0 then begin ignore (Xmlm.input i); skip_tags i (depth - 1) end else ()
in
skip_tags i depth;
let result = match Xmlm.input i with
| `Data dat -> dat
| _ -> raise_bad errmsg
in
skip_tags i depth;
result
let unpack_string s =
match s with
| MIdentifier(str) -> str
| _ -> raise_bad "not a packed string"
let unpack_symbol_type attrs =
let (_,sbmlUrl) = List.find (fun next -> let ((_,tag), _) = next in tag = "definitionURL") attrs in
let splitUrl = Core.Core_string.split ~on:'/' sbmlUrl in
List.nth splitUrl (List.length splitUrl - 1)
let parse_bvarlist i =
let rec bvarlist_iter i bvarlist =
match Xmlm.peek i with
| `El_start ((_, "bvar"), _) -> bvarlist_iter i ((extract_string i 2 "malformed lambda expr in bvar") :: bvarlist)
| `El_start ((_, _), _) -> bvarlist
| _ -> raise_bad "malformed lambda expr in bvar list"
in
List.rev (bvarlist_iter i [])
let rec parse_mathexpr i =
let rec mathexpr_iter i formula =
match Xmlm.input i with
| `El_start ((_, "apply"), _) -> let operator = parse_operator i in
let exprlist = parse_exprlist i in mathexpr_iter i (MApply (operator, exprlist))
| `El_start ((_, "lambda"), _) -> let bvars = parse_bvarlist i in
let lambda_expr = parse_mathexpr i in mathexpr_iter i (MLambda (bvars, lambda_expr))
| `El_start ((_, "piecewise"), _) -> let pieces = parse_piecelist i in
let otherwise = extract_string i 2 "malformed otherwise expr" in mathexpr_iter i (MPiecewise (pieces, otherwise))
| `El_start ((_, "ci"), _) -> mathexpr_iter i (MIdentifier (unpack_string (parse_mathexpr i)))
| `El_start ((_, "cn"), attrs) -> if (List.length attrs) = 1
then match List.hd attrs with
| ((_, "type"), "integer") -> mathexpr_iter i (MIntNumber (int_of_string (unpack_string (parse_mathexpr i))))
| ((_, "type"), "e-notation") ->
mathexpr_iter i (MFloatNumber (float_of_string (unpack_string (parse_mathexpr i))))
| ((_, _), _) -> raise_bad "malformed cn tag"
else mathexpr_iter i (MFloatNumber (float_of_string (unpack_string (parse_mathexpr i))))
| `El_start ((_, "sep"), _) -> mathexpr_iter i (MIdentifier ((unpack_string (formula)) ^ "e" ^ (unpack_string (parse_mathexpr i))))
| `El_start ((_, "csymbol"), attrs) ->
if (unpack_symbol_type attrs) = "time" then
begin ignore (Xmlm.input i); mathexpr_iter i (MTime) end
else raise_bad "malformed csymbol expr"
| `El_start ((_, "exponentiale"), _) -> mathexpr_iter i (MExponent)
| `El_start ((_, tag), _) -> print_endline tag; raise_bad "unknown math tag"
| `Data dat -> MIdentifier (dat)
| `El_end -> formula
| `Dtd _ -> assert false
in
mathexpr_iter i MNoMath
and
parse_operator i =
let oper = match Xmlm.input i with
| `El_start ((_, "plus"), _) -> MPlus
| `El_start ((_, "minus"), _) -> MMinus
| `El_start ((_, "times"), _) -> MTimes
| `El_start ((_, "power"), _) -> MPower
| `El_start ((_, "abs"), _) -> MAbs
| `El_start ((_, "exp"), _) -> MExp
| `El_start ((_, "factorial"), _) -> MFactorial
| `El_start ((_, "ceiling"), _) -> MCeiling
| `El_start ((_, "lt"), _) -> MLt
| `El_start ((_, "gt"), _) -> MGt
| `El_start ((_, "leq"), _) -> MLeq
| `El_start ((_, "geq"), _) -> MGeq
| `El_start ((_, "csymbol"), attrs) ->
if (unpack_symbol_type attrs) = "delay" then
begin ignore (Xmlm.input i); MDelay end
else raise_bad "malformed csymbol expr"
| `El_start ((_, "ci"), _) -> MFundef (unpack_string (parse_mathexpr i))
| _ -> raise_bad "malformed apply expr"
in ignore (Xmlm.input i); oper
and
parse_exprlist i =
let rec exprlist_iter i exprlist =
match Xmlm.peek i with
| `El_start ((_, _), _) -> exprlist_iter i ((parse_mathexpr i) :: exprlist)
| `El_end -> exprlist
| _ -> raise_bad "malformed mathml in apply"
in
List.rev (exprlist_iter i [])
and
parse_piecelist i =
let rec piecelist_iter i piecelist =
match Xmlm.peek i with
| `El_start ((_, "piece"), _) -> ignore (Xmlm.input i);
let piece_var = extract_string i 1 "malformed piece expr" in
let piece_expr = parse_mathexpr i in
ignore (Xmlm.input i);
piecelist_iter i ((piece_var, piece_expr) :: piecelist)
| `El_start ((_, "otherwise"), _) -> piecelist
| _ -> raise_bad "malformed piecewise expr"
in
List.rev (piecelist_iter i [])
let parse_math attrs i =
let sbm = parse_mathexpr i in
ignore (Xmlm.input i);
sbm
end
let parse_math = MathML.parse_math
let math_to_string = MathML.math_to_string
module SBMLParser = struct
let store_attrs attrs =
let parse_hash = Hashtbl.create 10 in
let store_attr attr =
match attr with
| ((_, nam), value) -> Hashtbl.add parse_hash nam value
in List.iter store_attr attrs; parse_hash
let parse_list i assoclist =
let rec iter_list i templist =
match Xmlm.input i with
| `El_start ((_, tagname), attrs) -> iter_list i ((try ((List.assoc tagname assoclist) attrs i) with Not_found -> raise_bad tagname) :: templist)
| `El_end -> templist
| `Data dat -> iter_list i templist
| `Dtd _ -> assert false
in
iter_list i []
let parse_record i list_dict record_dict =
let list_hash = Hashtbl.create 10 in
let record_hash = Hashtbl.create 10 in
let rec iter_record i =
match Xmlm.input i with
| `El_start ((_, tagname), attrs) -> (if (String.compare (String.sub tagname 0 4) "list")=0
then (try (Hashtbl.add list_hash tagname (parse_list i (List.assoc tagname list_dict))) with Not_found -> raise_bad tagname)
else (try (Hashtbl.add record_hash tagname ((List.assoc tagname record_dict) attrs i)) with Not_found -> raise_bad tagname));
iter_record i
| `El_end -> ()
| `Data dat -> iter_record i
| `Dtd _ -> assert false
in iter_record i; (list_hash, record_hash)
let parse_unit attrs i =
ignore (Xmlm.input i); let parse_hash = (store_attrs attrs) in
LUnit ({
unit_kind = (Hashtbl.find parse_hash "kind");
unit_exponent = (try (int_of_string (Hashtbl.find parse_hash "exponent")) with Not_found -> 1);
unit_scale = (try int_of_string (Hashtbl.find parse_hash "scale") with Not_found -> 0);
unit_multiplier = (try (float_of_string (Hashtbl.find parse_hash "multiplier")) with Not_found -> 1.0)
})
let parse_compartment attrs i =
ignore (Xmlm.input i); let parse_hash = (store_attrs attrs) in
LCompartment ({
compart_id = (Hashtbl.find parse_hash "id");
compart_name = (try (Hashtbl.find parse_hash "name") with Not_found -> "");
compart_size = (try (float_of_string (Hashtbl.find parse_hash "size")) with Not_found -> 0.0);
compart_spatialDimensions = (try (int_of_string (Hashtbl.find parse_hash "spatialDimensions")) with Not_found -> 3);
compart_units = (try (Hashtbl.find parse_hash "units") with Not_found -> "");
compart_outside = (try (Hashtbl.find parse_hash "outside") with Not_found -> "");
compart_constant = (try (bool_of_string (Hashtbl.find parse_hash "constant")) with Not_found -> true);
})
let parse_species attrs i =
ignore (Xmlm.input i); let parse_hash = (store_attrs attrs) in
LSpecies ({
species_id = (Hashtbl.find parse_hash "id");
species_name = (try (Hashtbl.find parse_hash "name") with Not_found -> "");
species_type = (try (Hashtbl.find parse_hash "speciesType") with Not_found -> "");
species_compartment = (Hashtbl.find parse_hash "compartment");
species_initialAmount = (try (float_of_string (Hashtbl.find parse_hash "initialAmount")) with Not_found -> 0.0);
species_initialConcentration = (try (float_of_string (Hashtbl.find parse_hash "initialConcentration")) with Not_found -> 0.0);
species_substanceUnits = (try (Hashtbl.find parse_hash "substanceUnits") with Not_found -> "");
species_hasOnlySubstanceUnits = (try (bool_of_string (Hashtbl.find parse_hash "hasOnlySubstanceUnits")) with Not_found -> false);
species_boundaryCondition = (try (bool_of_string (Hashtbl.find parse_hash "boundaryCondition")) with Not_found -> false);
species_constant = (try (bool_of_string (Hashtbl.find parse_hash "constant")) with Not_found -> false);
})
let parse_spreference attrs i =
ignore (Xmlm.input i); let parse_hash = (store_attrs attrs) in
LSpecieRef ({
specref_species = (Hashtbl.find parse_hash "species");
specref_id = (try (Hashtbl.find parse_hash "id") with Not_found -> "");
specref_name = (try (Hashtbl.find parse_hash "name") with Not_found -> "");
specref_stoichiometry = (try (int_of_string (Hashtbl.find parse_hash "stoichiometry")) with Not_found -> 1)
})
let parse_parameter attrs i =
ignore (Xmlm.input i); let parse_hash = (store_attrs attrs) in
LParameter ({
param_id = (try (Hashtbl.find parse_hash "id") with Not_found -> raise_bad "no id for parameter") ;
param_name = (try (Hashtbl.find parse_hash "name") with Not_found -> "");
param_value = (try (float_of_string (Hashtbl.find parse_hash "value")) with Not_found -> 0.0);
param_units = (try (Hashtbl.find parse_hash "units") with Not_found -> "");
param_constant = (try (bool_of_string (Hashtbl.find parse_hash "constant")) with Not_found -> true);
})
let parse_fundef attrs i =
let parse_hash = (store_attrs attrs) in
let (list_hash,record_hash) = (parse_record i []
[("math",parse_math)]) in
LFunctionDefinition ({
fundef_id = (Hashtbl.find parse_hash "id");
fundef_name = (try (Hashtbl.find parse_hash "name") with Not_found -> "");
fundef_math = (try (Hashtbl.find record_hash "math") with Not_found -> MNoMath);
})
let parse_unitdef attrs i =
let parse_hash = (store_attrs attrs) in
let (list_hash,record_hash) = (parse_record i [("listOfUnits",[("unit",parse_unit)])]
[]) in
LUnitDefinition ({
unitdef_id = (Hashtbl.find parse_hash "id");
unitdef_name = (try (Hashtbl.find parse_hash "name") with Not_found -> "");
unitdef_unitlist = (try (List.rev_map (function LUnit(t) -> t | _ -> raise_bad "unit")
(Hashtbl.find record_hash "listOfUnits")) with Not_found -> []);
})
let parse_iassignment attrs i =
let parse_hash = (store_attrs attrs) in
let (list_hash,record_hash) = (parse_record i []
[("math",parse_math)]) in
LInitialAssignment ({
ia_symbol = (Hashtbl.find parse_hash "symbol");
ia_math = (try (Hashtbl.find record_hash "math") with Not_found -> MNoMath);
})
let parse_generic_rule attrs i =
let parse_hash = (store_attrs attrs) in
let (list_hash,record_hash) = (parse_record i []
[("math",parse_math)]) in
{
gr_variable = (Hashtbl.find parse_hash "variable");
gr_math = (try (Hashtbl.find record_hash "math") with Not_found -> MNoMath);
}
let parse_algebraic_rule attrs i =
let (list_hash,record_hash) = (parse_record i []
[("math",parse_math)]) in
LRule (AlgebraicRule ({
ar_math = (try (Hashtbl.find record_hash "math") with Not_found -> MNoMath);
}))
let parse_assignment_rule attrs i = LRule (AssignmentRule (parse_generic_rule attrs i))
let parse_rate_rule attrs i = LRule (RateRule (parse_generic_rule attrs i))
let parse_kineticlaw attrs i =
let (list_hash,record_hash) = (parse_record i [("listOfParameters",[("parameter",parse_parameter)])]
[("math",parse_math)]) in
{
klaw_parameters = (try (List.rev_map (function LParameter(t) -> t | _ -> raise_bad "parameter")
(Hashtbl.find list_hash "listOfParameters")) with Not_found -> []);
klaw_math = (try (Hashtbl.find record_hash "math") with Not_found -> MNoMath);
}
let parse_reaction attrs i =
let parse_hash = (store_attrs attrs) in
let (list_hash,record_hash) = (parse_record i [("listOfReactants",[("speciesReference",parse_spreference)]);
("listOfProducts",[("speciesReference",parse_spreference)])]
[("kineticLaw",parse_kineticlaw)]) in
LReaction ({
react_id = (Hashtbl.find parse_hash "id");
react_name = (try (Hashtbl.find parse_hash "name") with Not_found -> "");
react_boundaryCondition = (try (bool_of_string (Hashtbl.find parse_hash "reversible")) with Not_found -> true);
react_fast = (try (bool_of_string (Hashtbl.find parse_hash "fast")) with Not_found -> false);
react_reactants = (try (List.rev_map (function LSpecieRef(t) -> t | _ -> raise_bad "malformed specieReference")
(Hashtbl.find list_hash "listOfReactants")) with Not_found -> []);
react_products = (try (List.rev_map (function LSpecieRef(t) -> t | _ -> raise_bad "malformed specieReference")
(Hashtbl.find list_hash "listOfProducts")) with Not_found -> []);
react_kineticLaw = (try (Hashtbl.find record_hash "kineticLaw")
with Not_found -> {klaw_parameters = []; klaw_math = MNoMath});
})
let parse_eassignment attrs i =
let parse_hash = (store_attrs attrs) in
let (list_hash,record_hash) = (parse_record i []
[("math",parse_math)]) in
LEventAssignment ({
ea_variable = (Hashtbl.find parse_hash "variable");
ea_math = (try (Hashtbl.find record_hash "math") with Not_found -> MNoMath);
})
let parse_math_container attrs i =
let (list_hash,record_hash) = (parse_record i []
[("math",parse_math)]) in
{
math = (try (Hashtbl.find record_hash "math") with Not_found -> MNoMath);
}
let parse_event attrs i =
let parse_hash = (store_attrs attrs) in
let (list_hash,record_hash) = (parse_record i [("listOfEventAssignments",[("eventAssignment",parse_eassignment)])]
[("trigger",parse_math_container);
("delay",parse_math_container)]) in
LEvent ({
event_id = (try (Hashtbl.find parse_hash "id") with Not_found -> "");
event_name = (try (Hashtbl.find parse_hash "name") with Not_found -> "");
event_useValuesFromTriggerTime = (try (bool_of_string (Hashtbl.find parse_hash "useValuesFromTriggerTime")) with Not_found -> true);
event_trigger = (try Trigger (Hashtbl.find record_hash "trigger") with Not_found -> raise_bad "trigger not found in event");
event_delay = (try Delay (Hashtbl.find record_hash "delay") with Not_found -> Delay ({math = MNoMath}));
event_assignments = (try (List.rev_map (function LEventAssignment(t) -> t | _ -> raise_bad "malformed eventAssignment")
(Hashtbl.find list_hash "listOfEventAssignments")) with Not_found -> []);
})
let parse_model attrs i =
let parse_hash = (store_attrs attrs) in
let (list_hash,record_hash) = (parse_record i [("listOfFunctionDefinitions",[("functionDefinition",parse_fundef)]);
("listOfUnitDefinitions",[("unitDefinition",parse_unitdef)]);
("listOfCompartments",[("compartment",parse_compartment)]);
("listOfSpecies",[("species",parse_species)]);
("listOfReactions",[("reaction",parse_reaction)]);
("listOfParameters",[("parameter",parse_parameter)]);
("listOfInitialAssignments",[("initialAssignment",parse_iassignment)]);
("listOfRules",[("assignmentRule",parse_assignment_rule);
("rateRule",parse_rate_rule);
("algebraicRule",parse_algebraic_rule)]);
("listOfEvents",[("event",parse_event)])]
[]) in
{
sbm_id = (try (Hashtbl.find parse_hash "id") with Not_found -> "");
sbm_name = (try (Hashtbl.find parse_hash "name") with Not_found -> "");
sbm_functionDefinitions = (try (List.rev_map (function LFunctionDefinition(t) -> t | _ -> raise_bad "malformed functionDefinition")
(Hashtbl.find list_hash "listOfFunctionDefinitions")) with Not_found -> []);
sbm_unitDefinitions = (try (List.rev_map (function LUnitDefinition(t) -> t | _ -> raise_bad "malformed unitDefinition")
(Hashtbl.find list_hash "listOfUnitDefinitions")) with Not_found -> []);
sbm_compartments = (try (List.rev_map (function LCompartment(t) -> t | _ -> raise_bad "malformed compartment")
(Hashtbl.find list_hash "listOfCompartments")) with Not_found -> []);
sbm_species = (try (List.rev_map (function LSpecies(t) -> t | _ -> raise_bad "malformed species")
(Hashtbl.find list_hash "listOfSpecies")) with Not_found -> []);
sbm_reactions = (try (List.rev_map (function LReaction(t) -> t | _ -> raise_bad "malformed reaction")
(Hashtbl.find list_hash "listOfReactions")) with Not_found -> []);
sbm_parameters = (try (List.rev_map (function LParameter(t) -> t | _ -> raise_bad "malformed parameter")
(Hashtbl.find list_hash "listOfParameters")) with Not_found -> []);
sbm_initialAssignments = (try (List.rev_map (function LInitialAssignment(t) -> t | _ -> raise_bad "malformed initialAssignment")
(Hashtbl.find list_hash "listOfInitialAssignments")) with Not_found -> []);
sbm_rules = (try (List.rev_map (function LRule(t) -> t | _ -> raise_bad "malformed rule")
(Hashtbl.find list_hash "listOfRules")) with Not_found -> []);
sbm_events = (try (List.rev_map (function LEvent(t) -> t | _ -> raise_bad "malformed event")
(Hashtbl.find list_hash "listOfEvents")) with Not_found -> []);
}
let in_sbml ichan =
let i = (Xmlm.make_input ~strip:true (`Channel ichan)) in
ignore (Xmlm.input i);
ignore (Xmlm.input i);
let model =
match Xmlm.input i with
| `El_start ((_, "model"), attrs) -> parse_model attrs i
| _ -> raise_bad "malformed sbml" in
ignore (Xmlm.input i);
if not (Xmlm.eoi i) then raise_bad "sbml too long";
model
end
let in_sbml = SBMLParser.in_sbml