0001 function [model,name_map] = dsParseModelEquations(text,varargin)
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048 options = dsCheckOptions(varargin,{'auto_gen_test_data_flag',0,{0,1}},false);
0049 if options.auto_gen_test_data_flag
0050 varargs = varargin;
0051 varargs{find(strcmp(varargs, 'auto_gen_test_data_flag'))+1} = 0;
0052 varargs(end+1:end+2) = {'unit_test_flag',1};
0053 argin = [{text}, varargs];
0054 end
0055
0056 model=[];
0057 name_map={};
0058
0059
0060
0061 if nargin>2
0062 keys=varargin(1:2:end);
0063 values=varargin(2:2:end);
0064 else
0065 keys=[];
0066 values=[];
0067 end
0068
0069
0070 if ~isempty(keys) && ismember('namespace',keys)
0071 namespace=values{ismember(keys,'namespace')};
0072 if ~isempty(namespace)
0073 namespace=[namespace '_'];
0074 else
0075 namespace='';
0076 end
0077 else
0078 namespace='';
0079 end
0080
0081
0082 if ~isempty(keys) && ismember('delimiter',keys)
0083 delimiter = values(ismember(keys,'delimiter'));
0084 else
0085 delimiter=';';
0086 end
0087
0088
0089 if ~ischar(namespace)
0090 error('model "namespace" must be a string.');
0091 end
0092
0093 if ~ischar(delimiter)
0094 error('expression "delimiter" must be a string.');
0095 end
0096
0097
0098
0099
0100 if ischar(text) && isempty(regexp(text,'[^\w.]','once')) && ~any(which(text))
0101
0102
0103
0104 [~,text]=dsLocateModelFiles(text);
0105 if iscell(text) && ~isempty(text)
0106 text=text{1};
0107 end
0108 end
0109
0110
0111 if ischar(text) && exist(text,'file')
0112 text = dsReadText(text);
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134
0135
0136
0137
0138
0139
0140
0141 end
0142
0143
0144 if ischar(text)
0145
0146 if text(end)==';'
0147 text=text(1:end-1);
0148 end
0149
0150
0151
0152
0153
0154 pattern='(if\([^;]+\)\s*\([^;\)]+);([^;]+\))';
0155 replace='$1,$2';
0156 text=regexprep(text,pattern,replace,'ignorecase');
0157
0158
0159 text = strtrim(regexp(text,delimiter,'split'));
0160 end
0161 if ~iscellstr(text)
0162 error('input not recognized. equations must be provided in single string, cell array of strings, or a text file');
0163 end
0164
0165
0166
0167 model.parameters=struct('');
0168 model.fixed_variables=struct('');
0169 model.functions=struct('');
0170 model.monitors=struct('');
0171 model.state_variables={};
0172 model.ODEs=struct('');
0173 model.ICs=struct('');
0174 model.conditionals=struct('');
0175 model.linkers=struct('');
0176 model.comments={};
0177 for index=1:length(text)
0178
0179 line=text{index};
0180 [line,comment]=remove_comment(line);
0181 if isempty(line)
0182 if ~isempty(comment)
0183 model.comments{end+1}=comment;
0184 end
0185 continue;
0186 end
0187
0188 switch dsClassifyEquation(line,delimiter)
0189 case 'parameter'
0190 rhs=regexp(line,'=(.+)$','tokens','once');
0191 lhs=regexp(line,'^([\w\.]+)\s*=','tokens','once');
0192 lhs{1}=strrep(lhs{1},'.','_');
0193 name=strtrim(lhs{1}); expression=rhs{1};
0194 model.parameters(1).([namespace name]) = expression;
0195 name_map(end+1,:) = {name,[namespace name],namespace,'parameters'};
0196 if ~isempty(comment)
0197 model.comments{end+1}=sprintf('%s (parameter): %s',[namespace name],comment);
0198 end
0199 case 'fixed_variable'
0200 lhs=regexp(line,'^(\w+)\s*=','tokens','once');
0201 if ~isempty(lhs)
0202 rhs=regexp(line,'=(.+)$','tokens','once');
0203 name=strtrim(lhs{1}); expression=rhs{1};
0204 model.fixed_variables(1).([namespace name]) = expression;
0205 name_map(end+1,:) = {name,[namespace name],namespace,'fixed_variables'};
0206 else
0207
0208 lhs=regexp(line,'^(.+)\(.*\)\s*=','tokens','once');
0209 name=strtrim(lhs{1});
0210 if isfield(model.fixed_variables(1),[namespace name])
0211
0212 expression=[model.fixed_variables(1).([namespace name]) ';' line];
0213 model.fixed_variables(1).([namespace name]) = expression;
0214 else
0215 warning('failed to set fixed variable.');
0216 end
0217 end
0218 if ~isempty(comment)
0219 model.comments{end+1}=sprintf('%s (fixed_variable): %s',[namespace name],comment);
0220 end
0221 case 'function'
0222
0223
0224
0225
0226 name=regexp(line,'^(.+)\(.*\)\s*=','tokens','once');
0227 vars=regexp(line,'\((.+)\)\s*=','tokens','once');
0228 rhs=regexp(line,'=(.+)$','tokens','once');
0229 name=strtrim(name{1});
0230 expression=sprintf('@(%s)%s',vars{1},rhs{1});
0231 model.functions(1).([namespace name]) = expression;
0232 name_map(end+1,:) = {name,[namespace name],namespace,'functions'};
0233 if ~isempty(comment)
0234 model.comments{end+1}=sprintf('%s (function): %s',[namespace name],comment);
0235 end
0236 case 'ODE'
0237 var=regexp(line,'^d(\w+)/dt\s*=','tokens','once');
0238 if isempty(var)
0239 var=regexp(line,'^(\w+)''\s*=','tokens','once');
0240 end
0241 rhs=regexp(line,'=(.+)$','tokens','once');
0242 state_variable=strtrim(var{1}); expression=rhs{1};
0243 model.ODEs(1).([namespace state_variable])=expression;
0244 if ~ismember([namespace state_variable],model.state_variables)
0245 name_map(end+1,:) = {state_variable,[namespace state_variable],namespace,'state_variables'};
0246 model.state_variables{end+1}=[namespace state_variable];
0247 end
0248 if ~isempty(comment)
0249 model.comments{end+1}=sprintf('d/dt %s (ODE): %s',[namespace state_variable],comment);
0250 end
0251 case 'IC'
0252 var=regexp(line,'^(\w+)\(','tokens','once');
0253 rhs=regexp(line,'=(.+)$','tokens','once');
0254 state_variable=strtrim(var{1}); expression=rhs{1};
0255 model.ICs(1).([namespace state_variable])=expression;
0256 if ~isempty(comment)
0257 model.comments{end+1}=sprintf('%s(0) (IC): %s',[namespace state_variable],comment);
0258 end
0259 case 'monitor'
0260
0261 lines=strtrim(regexp(line,',','split'));
0262
0263 for l=1:length(lines)
0264
0265 line=lines{l};
0266
0267
0268 lhs=regexp(line,'^monitor ([\w,@\s\.]+)','tokens','once');
0269 if isempty(lhs)
0270 lhs=regexp(line,'([\w,@\s\.]+)','tokens','once');
0271 end
0272 rhs=regexp(line,'=(.+)$','tokens','once');
0273
0274
0275 names=strtrim(regexp(lhs{1},',','split'));
0276 for i=1:length(names)
0277 name=names{i};
0278
0279
0280 if any(name=='.')
0281
0282 arg=regexp(line,[name '\(([-+]*\w+)\)'],'tokens','once');
0283
0284 if ~isempty(arg)
0285 rhs=arg;
0286 end
0287 end
0288
0289
0290 name=strrep(name,'.','_');
0291
0292 if ~isempty(rhs), expression=rhs{1}; else expression=[]; end
0293
0294 model.monitors(1).([namespace name]) = expression;
0295 name_map(end+1,:) = {name,[namespace name],namespace,'monitors'};
0296
0297 if ~isempty(comment)
0298 model.comments{end+1}=sprintf('%s (monitor): %s',[namespace name],comment);
0299 end
0300 end
0301
0302 end
0303
0304 case 'conditional'
0305 groups=regexp(line,'\)\(','split');
0306 condition=regexp(groups{1},'^if\s*\((.*)','tokens','once');
0307 if length(groups)==2
0308 if groups{2}(end)==')'
0309 groups{2}=groups{2}(1:end-1);
0310 end
0311
0312 then_action=groups{2};
0313 else_action=[];
0314 elseif numel(groups==3)
0315 if groups{3}(end)==')'
0316 groups{3}=groups{3}(1:end-1);
0317 end
0318
0319 then_action=groups{2};
0320 else_action=groups{3};
0321 end
0322
0323 model.conditionals(end+1).namespace=namespace;
0324 model.conditionals(end).condition=condition{1};
0325 model.conditionals(end).action=strrep(then_action,',',';');
0326
0327 if length(groups)>2
0328 model.conditionals(end).else=else_action;
0329 else
0330 model.conditionals(end).else=[];
0331 end
0332
0333 if ~isempty(comment)
0334 model.comments{end+1}=sprintf('%s conditional(%s): %s',namespace,condition,comment);
0335 end
0336
0337 case 'linker'
0338
0339 line=regexprep(line,'^link (\s*\w)','$1');
0340 lhs=regexp(line,'^([^\+\-*/=]+)','tokens','once');
0341 rhs=regexp(line,'=>?(.+)$','tokens','once');
0342 model.linkers(end+1).namespace=namespace;
0343
0344 if ~isempty(lhs), target=strtrim(lhs{1}); else target=[]; end
0345
0346 if ~isempty(rhs), expression=strtrim(rhs{1}); else expression=[]; end
0347
0348 if expression(end)==';', expression=expression(1:end-1); end
0349
0350 if isempty(target), target=expression; end
0351
0352 if isempty(expression), expression=target; end
0353
0354 model.linkers(end).target=target;
0355 model.linkers(end).expression=expression;
0356 model.linkers(end).operation='+=';
0357
0358 if ~isempty(comment)
0359 model.comments{end+1}=sprintf('%s linkers(%s->%s): %s',namespace,target,expression,comment);
0360 end
0361
0362 if ~isempty(comment)
0363 model.linkers(end).comment=comment;
0364 end
0365 otherwise
0366 warning('ignoring line, failed to classify :%s',line);
0367 end
0368 end
0369
0370
0371 if options.auto_gen_test_data_flag
0372 argout = {model, name_map};
0373
0374 dsUnitSaveAutoGenTestData(argin, argout);
0375 end
0376
0377 end
0378
0379
0380
0381 function [line,comment]=remove_comment(line)
0382
0383 index=find(line=='%',1,'first');
0384 if isempty(index)
0385
0386 index=find(line=='#',1,'first');
0387 end
0388
0389 if isempty(index)
0390 comment='';
0391 else
0392 comment=line(index:end);
0393 line=line(1:index-1);
0394 end
0395
0396 end