function calledNames = whatToAnalyze( code, workspace )
% Basic assumptions are that code represents valid MATLAB code and that
% workspace is the return of 'whos' at the point that the code is about to
% be executed. The return is a cell array of character vectors that are the
% names of functions that the code is dependent on.
%
% NOTE: There are several situations where this function will not correctly
% identify dependencies:
%  1. Where code contains something like obj.method we do not deduce that
%     method is required if obj is a variable in the workspace.
%  2. If workspace contains objects, function handles or other data the
%     represents the ability to call code, we do not deduce that.
%  3. Code such as 'varOrFun = 1;clear varOrFun;varOrFun(10)' will not
%     identify that it needs a function called 'varOrFun' since we do not
%     analyze the actual executed statements to deduce that there is a
%     clear command in the code.

%   Copyright 2012-2016 The MathWorks, Inc.

assert( ischar( code ) && isvector( code ) && size( code, 1 ) == 1, ...
    'Code must be an array of char' );

% MTREE supports a -param variable that allows us to indicate which names
% are actually variables prior to parsing the code. Do this so that we can
% distinguish between functions and variables
variables = fieldnames( workspace );
varDefPattern = sprintf( '%s,', variables{:} );
% Don't forget to strip off the trailing comma with end - 1
varDefPattern = sprintf( '-param=%s', varDefPattern(1:end-1) );

% Generate an AST from the code
tree = mtree( code, varDefPattern );

% Look for all the CALL and DCALL sites in the MTREE. These are calls to
% functions or scripts. Do note that we will NOT pick up calls to classes
% that use dot notation ( for example obj.method(args{:}) ) here. Neither
% will we pick up MCOS package functions, statics, etc. as they will appear
% as subsref and dots in the MTREE. The node to the left of the CALL site
% is always the name of the function so the test below is a call where the
% LEFT node isa function name.
bareCallNames = strings( Left( mtfind( tree, 'Kind', {'CALL' 'DCALL'}, 'Left.Isfun', true )));

% Lets try to pick up package scoped stuff by looking at anything of the
% form a.b.c.d. If something like this exists and 'a' is a function name
% then this must represent a package call. We look for DOT calls where the
% LEFT node is the name of a function. This will leave us at the node that
% represents the a.b from the overall tree
rightDotFunNodes = mtfind(tree, 'Kind', 'DOT', 'Left.Isfun', true );
% What we want is actually the left most DOT node so that we can get the
% whole name of the function. We do this by iterating up the tree until we
% have grown the set to include all parents of this node that are
% themselves DOT nodes.
fullDotNodes = fixedpoint( rightDotFunNodes, @nGrowToAllDOTNodes );
% Finally we want to end up with nodes that are the right most-only. So
% remove all nodes whose parent is also in the set.
callDotNodes = fullDotNodes & ~mtfind( fullDotNodes, 'Parent.Member', fullDotNodes );
% Get the indices to these nodes so we can select them individually and
% generate the full string from them - this is what we will do requirements
% on.
callDotIndex = indices(callDotNodes);
dotCallNames = cell(size(callDotIndex));
for i = 1:numel(callDotIndex)
    dotCallNames{i} = tree2str( select( callDotNodes, callDotIndex(i) ));
end
% Concatenate the bare CALL, DCALL names with the package names
calledNames = [bareCallNames dotCallNames];

    function o = nGrowToAllDOTNodes( o )
        o = mtfind( tree, 'Kind', 'DOT', 'Left.Member', o ) | o; 
    end

end
