gusucode.com > appdesigner工具箱matlab源码程序 > appdesigner/+appdesigner/+internal/+codegeneration/MTreeCodeParser.m
classdef MTreeCodeParser %MTreeCodeParser parses user edited code and extracts code features % Parses M code defined in a class definition block. Extracts legal % code definition data and its position information (line number, % column number, etc). % Copyright 2015-2016 The MathWorks, Inc. properties (Access = private) % code content to parse CodeContent; % handle to the parse tree provided by MTree Tree; end properties(Access = private, Constant) % default attributes for class properties PropertiesAttributes = struct( ... 'Access', 'public'); % default attributes for class methods MethodsAttributes = struct( ... 'Access', 'public', ... 'Static', false); end methods %------------------------------------------------------------------ function obj = MTreeCodeParser() % constructor no-op end %------------------------------------------------------------------ function code = parse(obj, codeToParse) % parses code passed in as a cell array of lines of code obj.CodeContent = codeToParse; code = obj.parseWithMTree(obj.wrapContentWithClassDef()); end %------------------------------------------------------------------ end methods(Access = private) %------------------------------------------------------------------ function joinedText = wrapContentWithClassDef(obj) % wraps the code to parse in a classdef block. Adds one line % to the end of the code to parse wrappedText = ['classdef parseThisCode'; obj.CodeContent; 'end']; joinedText = strjoin(wrappedText, '\n'); end %------------------------------------------------------------------ function code = parseWithMTree(obj, text) % parses the code using mtree obj.Tree = mtree(text); % if there is a parse error MTREE will report a one node tree % with 'ERR' kind. In this case return the err string if(obj.Tree.count == 1 && strcmp(obj.Tree.kind(), 'ERR')) code = obj.Tree.string; else try code = obj.extractClassComponents(); catch e error('appdesigner:MTreeCodeParser:UnhandldedMTREEStructure', e.message); end end end %------------------------------------------------------------------ function parsedCode = extractClassComponents(obj) % gets the component blocks allowed in a class def block and % parses them individually kinds = {'PROPERTIES', 'METHODS'}; parsedCode = {}; for i = 1:length(kinds) classComp = mtfind(obj.Tree, 'Kind', kinds{i}); idxs = indices(classComp); for j = idxs % parse each section parsedCode{end+1} = obj.extractCodeItems(j, kinds{i}); end end end %------------------------------------------------------------------ function codeItem = extractCodeItems(obj, index, component) % for each kind of class component parse the code to get code % entitites import appdesigner.internal.codegeneration.MTreeCodeParser; % if the class component has attributes add them to the struct % generated for each block classComponent = obj.Tree.select(index); classComponentTree = classComponent.Tree; % get the position information for the class component blockLine = classComponent.lineno; blockCol = classComponent.charno; [blockEndL, blockendC] = classComponent.lastone; attributes = []; attrNodes = mtfind(classComponentTree, 'Kind', 'ATTRIBUTES'); if(~isempty(attrNodes)) attributes = MTreeCodeParser.extractPVListItems(attrNodes.Arg); end switch component case 'PROPERTIES' codeItem = obj.PropertiesAttributes; codeItem = MTreeCodeParser.assignAttributes(codeItem, attributes); codeItem.Type = 'PROPERTIES'; codeItem.Items = MTreeCodeParser.extractPVListItems(classComponentTree.Body); case 'METHODS' codeItem = obj.MethodsAttributes; codeItem = MTreeCodeParser.assignAttributes(codeItem, attributes); codeItem.Type = 'METHODS'; codeItem.Items = MTreeCodeParser.extractMethods(classComponentTree.Body); end % adds the end position information for the block codeItem.Line = blockLine - 1; codeItem.Column = blockCol; codeItem.EndLine = blockEndL - 1; codeItem.EndColumn = blockendC; end %------------------------------------------------------------------ end methods(Access = private, Static) %------------------------------------------------------------------ function items = extractMethods(tree) % extracts functions from a methods block. Handles external % functions and inner functions import appdesigner.internal.codegeneration.MTreeCodeParser; current = tree; items = {}; while(~isempty(current)) % get traditional function defined with 'function' keyword func = mtfind(current, 'Kind', 'FUNCTION'); % get external functions defined with just the function % name proto = mtfind(current, 'Kind', 'PROTO'); if(~isempty(func)) % loop over found functions to handle inner functions for i = indices(func) item = MTreeCodeParser.extractFunction(func.select(i)); items{end+1} = item; end end if(~isempty(proto)) item = MTreeCodeParser.extractExternalFunction(proto); items{end+1} = item; end current = current.Next; end end %------------------------------------------------------------------ function item = extractFunction(func) % gets function arguments, output, name, position, and end % posiiton for functions defined in a function block import appdesigner.internal.codegeneration.MTreeCodeParser; item = {}; if(~isempty(func.Ins)) item.Args = MTreeCodeParser.extractListItems(func.Ins); end if(~isempty(func.Outs)) item.Outputs = MTreeCodeParser.extractListItems(func.Outs); end name = func.Fname; item.Name = name.string; nameLine = name.lineno; nameCol = name.charno; item.NamePosition = [nameLine - 1, nameCol]; % subtract one line beacuse of line added with classdef item.Position = [func.lineno - 1, func.charno]; [endL, endC] = func.lastone; item.EndPosition = [endL - 1, endC]; end %------------------------------------------------------------------ function item = extractExternalFunction(proto) % gets the name and position info for external/abstract % functions item = {}; name = proto.Fname; item.Name = name.string; item.Position = [proto.lineno - 1, proto.charno]; end %------------------------------------------------------------------ function items = extractPVListItems(tree) % gets the property value pairs for Properties, Attributes, % Events, etc defined in a class section current = tree; items = {}; while(~isempty(current)) item = {}; name = current.Left; value = current.Right; if(strcmp(name.kind, 'PROPTYPEDECL')) item.Property.Name = string(name.VarName); elseif(strcmp(name.kind, 'ATBASE')) item.Property.Name = string(name.Left); name = name.Left; else item.Property.Name = name.string; end item.Property.Position = [name.lineno - 1, name.charno]; if(~value.isempty) item.Value.Name = value.tree2str; item.Value.Position = [value.lineno - 1, value.charno]; end items{end+1} = item; current = current.Next; end end %------------------------------------------------------------------ function items = extractListItems(tree) % gets the lists of items in function signatures current = tree; items = []; while(~isempty(current)) item = {}; % check if the argument is a '~' if strcmp(current.kind, 'NOT') item.Name = '~'; else item.Name = current.string; end item.Position = [current.lineno - 1, current.charno]; items{end+1} = item; %#ok<*AGROW> current = current.Next; end end %------------------------------------------------------------------ function classComponent = assignAttributes(classComponent, attributes) % assigns the attributes to the class component by updating its % value or adding to the default content if ~isempty(attributes) for i = 1:length(attributes) if(isfield(attributes{i},'Value')) classComponent.(attributes{i}.Property.Name) = attributes{i}.Value.Name; else classComponent.(attributes{i}.Property.Name) = true; end end end end %------------------------------------------------------------------ end end