File: addmethodassign.lpr

package info (click to toggle)
lazarus 2.0.0%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 214,460 kB
  • sloc: pascal: 1,862,622; xml: 265,709; cpp: 56,595; sh: 3,008; java: 609; makefile: 535; perl: 297; sql: 222; ansic: 137
file content (150 lines) | stat: -rw-r--r-- 5,763 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
{
 ***************************************************************************
 *                                                                         *
 *   This source is free software; you can redistribute it and/or modify   *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This code is distributed in the hope that it will be useful, but      *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
 *   General Public License for more details.                              *
 *                                                                         *
 *   A copy of the GNU General Public License is available on the World    *
 *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
 *   obtain it by writing to the Free Software Foundation,                 *
 *   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.        *
 *                                                                         *
 ***************************************************************************

  Author: Mattias Gaertner

  Abstract:
    Demonstrating, how to add a method Assign to a class.
}
program AddMethodAssign;

{$mode objfpc}{$H+}

uses
  Classes, SysUtils,
  // LazUtils
  Laz_AVL_Tree,
  // CodeTools
  CodeCache, CodeToolManager, FileProcs,
  BasicCodeTools, CodeTree, FindDeclarationTool, AssignExample1;

const
  ConfigFilename = 'codetools.config';
var
  Filename: string;
  Code: TCodeBuffer;
  Tool: TCodeTool;
  AssignDeclNode: TCodeTreeNode;
  MemberNodeExts: TAVLTree;
  AssignBodyNode: TCodeTreeNode;
  AVLNode: TAVLTreeNode;
  NodeExt: TCodeTreeNodeExtension;
  NextAVLNode: TAVLTreeNode;
  ClassNode: TCodeTreeNode;
  InheritedDeclContext: TFindContext;
  ParamName: String;
  ParamType: String;
  ParamNode: TCodeTreeNode;
  InheritedIsTPersistent: boolean;
  InheritedClassNode: TCodeTreeNode;
  AssignMembers: TFPList;
  NewPos: TCodeXYPosition;
  NewTopline: integer;
  i: Integer;
begin
  CodeToolBoss.SimpleInit(ConfigFilename);

  // load the file
  Filename:=ExpandFileName(SetDirSeparators('scanexamples/assignexample1.pas'));
  Code:=CodeToolBoss.LoadFile(Filename,false,false);
  if Code=nil then
    raise Exception.Create('loading failed '+Filename);

  // parse the unit, check if in a class with an Assign method
  AssignMembers:=TFPList.Create;
  try
    MemberNodeExts:=nil;
    if not CodeToolBoss.FindAssignMethod(Code,3,18,
      Tool,ClassNode,AssignDeclNode,MemberNodeExts,AssignBodyNode,
      InheritedDeclContext) then
      raise Exception.Create('parser error');

    debugln(['Assign declaration found: ',AssignDeclNode<>nil]);
    debugln(['Assign body found: ',AssignBodyNode<>nil]);
    debugln(['Inherited Assign found: ',InheritedDeclContext.Node<>nil]);

    // remove nodes which are written by a property
    if MemberNodeExts<>nil then begin
      AVLNode:=MemberNodeExts.FindLowest;
      while AVLNode<>nil do begin
        NextAVLNode:=MemberNodeExts.FindSuccessor(AVLNode);
        NodeExt:=TCodeTreeNodeExtension(AVLNode.Data);
        if NodeExt.Data<>nil then begin
          debugln(['skipping identifier ',NodeExt.Txt,' because it is written by a property']);
        end else begin
          debugln('assigning identifier ',NodeExt.Txt,' ...');
          AssignMembers.Add(NodeExt);
          MemberNodeExts.Delete(AVLNode);
        end;
        AVLNode:=NextAVLNode;
      end;
    end;
    if (AssignMembers.Count=0) then begin
      debugln('no assignable members found');
      exit;
    end;

    ParamName:='Source';
    ParamType:='TObject';
    InheritedIsTPersistent:=false;

    // check if inherited exists, if it is TPersistent.Assign and use the
    // inherited parameter name and type
    if InheritedDeclContext.Node<>nil then begin
      InheritedClassNode:=InheritedDeclContext.Tool.FindClassOrInterfaceNode(InheritedDeclContext.Node);
      InheritedIsTPersistent:=(InheritedClassNode<>nil)
        and (InheritedClassNode.Parent.Desc=ctnTypeDefinition)
        and (CompareIdentifiers('TPersistent',@InheritedDeclContext.Tool.Src[InheritedClassNode.Parent.StartPos])=0);
      ParamNode:=InheritedDeclContext.Tool.GetProcParamList(InheritedDeclContext.Node);
      if ParamNode<>nil then begin
        ParamNode:=ParamNode.FirstChild;
        if ParamNode<>nil then begin
          ParamName:=InheritedDeclContext.Tool.ExtractDefinitionName(ParamNode);
          if (ParamNode.FirstChild<>nil) and (ParamNode.FirstChild.Desc=ctnIdentifier) then
            ParamType:=GetIdentifier(@InheritedDeclContext.Tool.Src[ParamNode.FirstChild.StartPos]);
        end;
      end;
    end;

    // add assign method
    if AssignDeclNode=nil then begin
      if not Tool.AddAssignMethod(ClassNode,AssignMembers,
             'Assign',ParamName,ParamType,
             InheritedDeclContext.Node<>nil,true,InheritedIsTPersistent,
             CodeToolBoss.SourceChangeCache,NewPos,NewTopline)
      then
        raise Exception.Create('AddAssignMethod failed');
    end else begin
      debugln(['there is already an Assign method']);
    end;

  finally
    DisposeAVLTree(MemberNodeExts);
    for i:=0 to AssignMembers.Count-1 do
      TObject(AssignMembers[i]).Free;
    FreeAndNil(AssignMembers);
  end;
  // write the new source:
  writeln('-----------------------------------');
  writeln('New source:');
  writeln(Code.Source);
  writeln('-----------------------------------');
end.