用matlab写代码时不小心把系统自带的.m文件代码改了,不知道原来的代码了
求热心人复制个str2symInternal.m文件的代码,万分感谢!
用matlab写代码时不小心把系统自带的.m文件代码改了,不知道原来的代码了
求热心人复制个str2symInternal.m文件的代码,万分感谢!
function [mupstr, variables] = str2symInternal(S, options)
%str2symInternal Evaluate string representing symbolic expression.
% str2symInternal(S) evaluates S as a symbolic expression, where S can be
% a string, character vector, or cell array of character vectors.
%
% str2symInternal(S) behavior:
% Only executes functions on path when converting the string.
% Does not consider variables and values from workspace.
% Does not add variables in the input to the workspace.
% Does not support programming statements like IF.
% Treats assignments ('=') as equations ('==').
%
% For numeric strings, use SYM for exact symbolic numbers and
% VPA for floating-point numbers.
%
% See also SUBS, SYM, SYMFUN, SYMS, SYMVAR, VPA.
%
% Examples:
%
% >> str2symInternal("b^2 + pi/2 + sin(x) = x^3 - 2")
% ans =
% b^2 + pi/2 + sin(x) == x^3 - 2
%
% >> str2symInternal('int(x^3,x)')
% ans =
% x^4/4
%
% >> str2symInternal('y(0) = 42')
% ans =
% y(0) == 42
%
% >> str2symInternal({'42', 'a', 'y(0)'})
% ans =
% [ 42, a, y(0)]
%
% >> str2symInternal('[42, a, y(0)]')
% ans =
% [ 42, a, y(0)]
% Copyright 2017-2019 The MathWorks, Inc.
arguments
S string
options.Inert logical = false
end
inertMode = options.Inert;
% Convert newlines and tabs to spaces (newline are not supported by evalin)
if any(contains(S(:),newline))
warning(message("symbolic:str2sym:NewlinesIgnored"));
end
S = regexprep(S, "\s", " ");
% Treat assigmnments as equations by replacing "=" by "==".
% --- But do not replace "=" in "<=", ">=", "~=", and "==".
S = regexprep(S, "(?<![<>~=])=(?![<>~=])", "==");
% Keep size of input for output
mupstr = string(zeros(size(S)));
variables = string([]);
% Iterate linearly over string array and convert to SYM.
for idx = 1:numel(S)
% Behave like sym function on missing.
if ismissing(S(idx))
mupstr(idx) = "sym(NaN)";
continue;
end
% Empty string results in empty sym.
if replace(S(idx)," ","") == ""
mupstr(idx) = "sym([])";
continue;
end
if ~isempty(regexp(S(idx), "^\s*%{\s*$", "once")) || ~isempty(regexp(S(idx), "^\s*%}\s*$", "once"))
% Note that newlines were replaced by blanks before. The
% only valid occurance of "%{" and "%}" to start and end
% a block comment is in a new line with nothing else but
% spaces. Otherwise it is handled as single-line comment.
error(message("symbolic:str2sym:CommentsNotSupported"));
end
% Parse string and identify objects that must be treated as SYMs.
mt = mtree(S(idx), "-com");
% Handle syntax errors in input
if ~isnull(mtfind(mt, "Kind", "ERR"))
error(message("symbolic:str2sym:UnableToConvert", string(mt)));
end
% Handle left curly bracket node in input
if ~isnull(mtfind(mt, "Kind", "LC"))
error(message("symbolic:str2sym:CellArrayNotSupported"));
end
% Collect objects that need special treatment
objects = [];
% Comments
comments = mtfind(mt, "Kind", "COMMENT");
if ~isnull(comments)
comments = struct( ...
'type', "comment", ...
'name', "", ...
'start', num2cell(position(comments))', ...
'end', num2cell(endposition(comments))', ...
'infixstart', 0, ...
'infixend', 0, ...
'fnoargs', false, ...
'dcall', false, ...
'isi', false ...
);
objects = [objects comments]; %#ok<AGROW>
end
% Numbers
numbers = mtfind(mt, "Kind", {'INT', 'DOUBLE', 'HEX', 'BINARY'});
if ~isnull(numbers)
numbers = struct( ...
'type', "number", ...
'name', "", ...
'start', num2cell(position(numbers))', ...
'end', num2cell(endposition(numbers))', ...
'infixstart', 0, ...
'infixend', 0, ...
'fnoargs', false, ...
'dcall', false, ...
'isi', false ...
);
objects = [objects numbers]; %#ok<AGROW>
end
% Functions calls, variables and special values
calls = mtfind(mt, "Kind", {'CALL', 'DCALL'});
if ~isnull(calls)
funvars = struct( ...
'type', "function", ...
'name', strings(calls.Left), ...
'start', num2cell(position(calls.Left))', ...
'end', num2cell(endposition(calls.Left))', ...
'infixstart', 0, ...
'infixend', 0, ...
'fnoargs', false, ...
'dcall', false, ...
'isi', false ...
);
% Depending on type of call, name might need special treatment.
wrapName = true(1,count(calls));
cidx = indices(calls);
for i = 1:count(calls)
% Note that DCALL nodes always have arguments.
% The arguments for a DCALL call are always STRINGs.
funvars(i).dcall = iskind(select(calls,cidx(i)), "DCALL");
if ~funvars(i).dcall
funvars(i).fnoargs = ~isempty(regexp(S(idx),"^.{" + funvars(i).end + "}\s*\(\s*\)", "once"));
if isnull(Right(select(calls,cidx(i)))) && ~funvars(i).fnoargs
% Call has no arguments (= right branch of tree) and
% found no name() so it's a variable or special value
name = funvars(i).name;
if name == "i" || name == "j"
funvars(i).type = "special";
funvars(i).isi = true;
elseif any(name == ["pi", "catalan", "eulergamma", "inf", "Inf", "nan", "NaN", "missing", "symtrue", "symfalse"])
funvars(i).type = "special";
funvars(i).isi = false;
elseif any(name == ["true", "false"])
funvars(i).type = "logical";
funvars(i).isi = false;
else
funvars(i).type = "variable";
end
continue;
end
end
% We know: It's a function call with at least one argument.
% Respect functions on search path: do not add sym wrapper.
if inertMode
wh = string([]);
else
wh = string(evalin("caller", "which('" + funvars(i).name + "')"));
end
if ~isempty(wh) && ~strcmp(wh, "variable")
% Note: 'which' is *not* case-sensitive so we need to double-check!
% e.g.: S:\Bsymbolic\matlab\toolbox\symbolic\symbolic\@sym\sym.m % sym method
wh = regexprep(wh, "\s*%.*$", ""); % for robustness
% e.g.: built-in (S:\Bsymbolic\matlab\toolbox\matlab\datatypes\double)
wh = regexprep(wh, "\)$", "");
% e.g.: S:\Bsymbolic\matlab\toolbox\symbolic\symbolic\str2sym.m
wh = regexprep(wh, "\.m$", "");
% e.g.: plus is a built-in method % string method
if startsWith(wh, funvars(i).name) || endsWith(wh, funvars(i).name)
wrapName(i) = false;
end
end
end
objects = [objects funvars(wrapName)]; %#ok<AGROW>
end
if inertMode
infixNames = struct(...
'PLUS', "_plus", ...
'MINUS', "_subtract", ...
'MUL', "_mult", ...
'DIV', "_divide", ...
'EXP', "_power", ...
'EQ', "_equal");
infixOperators = mtfind(mt, "Kind", fieldnames(infixNames));
if ~isnull(infixOperators)
infixStruct = struct( ...
'type', "infix", ...
'name', num2cell(string(infixOperators.kinds))', ...
'start', num2cell(lefttreepos(infixOperators))', ...
'end', num2cell(righttreepos(infixOperators))', ...
'infixstart', num2cell(position(infixOperators))', ...
'infixend', num2cell(endposition(infixOperators))', ...
'fnoargs', false, ...
'dcall', false, ...
'isi', false ...
);
objects = [objects infixStruct]; %#ok<AGROW>
end
end
% Manipulate given string: take action on each identified object.
T = S(idx);
for i = 1:numel(objects)
switch objects(i).type
case "comment"
% Remove comment
[T, objects] = removeStringBetween(T, objects(i).start, objects(i).end, objects);
case "number"
% Rewrite <o> to sym("<o>")
[T, objects] = addStringAtStart(T, objects, i, "sym(""");
[T, objects] = addStringAtEnd (T, objects, i, """)");
case "variable"
if nargout >= 2
variables = union(variables, getString(T, objects(i)));
end
[T, objects] = addStringAtStart(T, objects, i, "sym(""");
[T, objects] = addStringAtEnd (T, objects, i, """)");
case "logical"
if inertMode
[T, objects] = addStringAtStart(T, objects, i, "sym(");
[T, objects] = addStringAtEnd(T, objects, i, ")");
end
case "special"
% Rewrite <o> to sym(<o>)
if objects(i).isi
[T, objects] = addStringAtStart(T, objects, i, "1");
end
[T, objects] = addStringAtStart(T, objects, i, "sym(");
[T, objects] = addStringAtEnd(T, objects, i, ")");
case "function"
% Rewrite 'f(<any>)' to 'feval_internal(symengine, sym("f"), <any>)'
if objects(i).dcall
continue;
end
pos = objects(i).end;
while(extractBetween(T,pos,pos) ~= "(")
pos = pos + 1;
end
T = replaceBetween(T, pos, pos, ",");
if inertMode
[T, objects] = insertStringBefore(T, pos, ")""", objects);
end
if objects(i).fnoargs
% Rewrite 'f()' to 'feval_internal(symengine, sym("f"), "null()")'
[T, objects] = insertStringAfter(T, pos, """null()""", objects);
end
if inertMode
[T, objects] = insertStringBefore(T, pos, """""", objects);
[T, objects] = addStringAtStart(T, objects, i, "feval_internal(symengine, ""symobj::inertproc(""""");
else
[T, objects] = addStringAtStart(T, objects, i, "feval_internal(symengine,sym(""");
[T, objects] = addStringAtEnd (T, objects, i, """)");
end
case "infix"
% can only happen in inert mode
% replace operator by comma
pos = objects(i).infixstart;
endpos = objects(i).infixend;
% replace the the operator by a comma, e.g., "2 * 2" -> "2 , 2"
[T, objects] = replaceStringBetween(T, objects, pos, endpos, ",");
% declare the object non-infix
objects(i).infixstart = 0;
objects(i).infixend = 0;
% Convert the MATLAB name to a MuPAD name,
% e.g., "MUL" -> "_mult"
mupname = infixNames.(objects(i).name);
% add MuPAD name in front
[T, objects] = addStringAtStart(T, objects, i, "feval_internal(symengine, ""hold(" + mupname + ")"",");
% close the bracket "feval_internal(..."
[T, objects] = addStringAtEnd(T, objects, i, ")");
end
end
if replace(T," ","") == ""
mupstr(idx) = "sym([])";
else
mupstr(idx) = T;
end
end
end
% Helper: getString
% returns the substring of text that corresponds to obj
function T = getString(text, obj)
range = obj.start : obj.end;
T = text{1}(range);
end
% Helper: removeStringBetween
function [text, objs] = removeStringBetween(text, left, right, objs)
text = replaceBetween(text, left, right, "");
objs = adaptStringPositions(objs, left, left-right-1);
end
% Helper: replaceStringBetween
% Replace the string between left and right by newtext
function [text, objs] = replaceStringBetween(text, objs, left, right, newtext)
text = replaceBetween(text, left, right, newtext);
len = strlength(newtext);
objs = adaptStartPositions(objs, left+1, left-right-1+len);
objs = adaptEndPositions(objs, left, left-right-1+len);
objs = adaptInfixPositions(objs, left, left-right-1+len);
end
% Helper: insertStringBefore
function [text, objs] = insertStringBefore(text, pos, textBefore, objs)
text = insertBefore(text, pos, textBefore);
objs = adaptStringPositions(objs, pos, strlength(textBefore));
end
% Helper addStringAtStart
% adds a string at the start of the n-th object
function [text, objs] = addStringAtStart(text, objs, n, textBefore)
len = strlength(textBefore);
startpos = objs(n).start;
text = insertBefore(text, startpos, textBefore);
for i = 1:numel(objs)
if objs(i).start == startpos && objs(i).end < objs(n).end ...
|| objs(i).start > startpos
% If objs(i) is an inner object (subtree) of objs(n),
% or if it is a later object, modify the start.
% Example: 2*2 + 3 -> _plus(2*2, 3). The start of 2*2 is shifted.
objs(i).start = objs(i).start + len;
% else objs(n) is a subtree of objs(i), or an object to the left.
% Do not modify the start.
% Example: 2*2 + 3 -> _mult(2, 2) + 3. The sum still begins at 1.
end
end
% adapt infix and end positions of all objects, including objs(n)
objs = adaptInfixPositions(objs, startpos, len);
objs = adaptEndPositions(objs, startpos, len);
end
% Helper addStringAtEnd
% adds a string at the end of the n-th object
function [text, objs] = addStringAtEnd(text, objs, n, textAfter)
len = strlength(textAfter);
endpos = objs(n).end;
text = insertAfter(text, endpos, textAfter);
for i = 1:numel(objs)
if objs(i).end == endpos && objs(i).start <= objs(n).start ...
|| objs(i).end > endpos
% If objs(n) is a subtree of objs(i), or i=n,
% or if objs(i) is a later object, modify the end of objs(i).
% Example: _plus(3 + 2*2 -> _plus(3, 2*2).
% The sum includes the closing bracket, the end of the product
% remains the same.
objs(i).end = objs(i).end + len;
end
end
% adapt infix and start positions of all objects on the right
% of objs(n).
objs = adaptInfixPositions(objs, endpos, len);
objs = adaptStartPositions(objs, endpos, len);
end
% Helper: insertStringAfter
function [text, objs] = insertStringAfter(text, pos, textAfter, objs)
text = insertAfter(text, pos, textAfter);
objs = adaptStringPositions(objs, pos, strlength(textAfter));
end
function objs = adaptInfixPositions(objs, pos, len)
for i=1:numel(objs)
if pos <= objs(i).infixstart
objs(i).infixstart = objs(i).infixstart + len;
end
if pos <= objs(i).infixend
objs(i).infixend = objs(i).infixend + len;
end
end
end
function objs = adaptStartPositions(objs, pos, len)
for i=1:numel(objs)
if pos <= objs(i).start
objs(i).start = objs(i).start + len;
end
end
end
function objs = adaptEndPositions(objs, pos, len)
for i=1:numel(objs)
if pos <= objs(i).end
objs(i).end = objs(i).end + len;
end
end
end
% Helper: adaptStringPositions
function objs = adaptStringPositions(objs, pos, len)
for i=1:numel(objs)
if pos <= objs(i).start
objs(i).start = objs(i).start + len;
end
if pos <= objs(i).end
objs(i).end = objs(i).end + len;
end
end
objs = adaptInfixPositions(objs, pos, len);
end