source: dev/trunk/abdev/BasicCompiler_Common/src/ProcedureImpl.cpp@ 192

Last change on this file since 192 was 185, checked in by dai_9181, 17 years ago
File size: 17.6 KB
Line 
1#include <jenga/include/smoothie/SmoothieException.h>
2#include <jenga/include/smoothie/LexicalAnalysis.h>
3
4#include <ProcedureImpl.h>
5
6#include "../common.h"
7#ifdef _AMD64_
8#include "../../BasicCompiler64/opcode.h"
9#else
10#include "../../BasicCompiler32/opcode.h"
11#endif
12
13bool UserProcImpl::SetParamsAndReturnType( const char *sourceOfParams, int nowLine, bool isStatic ){
14 int i = 0;
15 int i2,i3,sw;
16 char temporary[8192],temp2[VN_SIZE];
17
18 //ソースコードの位置
19 this->codePos = nowLine;
20
21 //パラメータ
22 if(sourceOfParams[i]!='('){
23 SmoothieException::Throw(1,NULL,nowLine);
24 return 0;
25 }
26 i++;
27 if(sourceOfParams[i]!=')'&& this->pParentClass ){
28 //クラスのメンバ関数の場合のみ、デストラクタにパラメータがある場合にエラーをだす
29 if(this->GetName()[0]=='~'){
30 SmoothieException::Throw(114,NULL,nowLine);
31 i=JumpStringInPare(sourceOfParams,i);
32 }
33 }
34 while(1){
35 if(sourceOfParams[i]==')') break;
36
37 //ByRef
38 bool isRef;
39 if(sourceOfParams[i]==1&&sourceOfParams[i+1]==ESC_BYVAL){
40 isRef = false;
41 i+=2;
42 }
43 else if(sourceOfParams[i]==1&&sourceOfParams[i+1]==ESC_BYREF){
44 isRef = true;
45 i+=2;
46 }
47 else isRef = false;
48
49 //パラメータ名
50 bool isArray = false;
51 int subScripts[MAX_ARRAYDIM];
52 char name[VN_SIZE];
53 sw=0;
54 for(i2=0;;i++,i2++){
55 if(sourceOfParams[i]=='('){
56 if(!sw) sw=1;
57
58 i3=GetStringInPare(name+i2,sourceOfParams+i);
59 i2+=i3-1;
60 i+=i3-1;
61 continue;
62 }
63 if(sourceOfParams[i]=='['){
64 if(!sw) sw=1;
65
66 i3=GetStringInBracket(name+i2,sourceOfParams+i);
67 i2+=i3-1;
68 i+=i3-1;
69 continue;
70 }
71 if(!IsVariableChar(sourceOfParams[i])){
72 name[i2]=0;
73 break;
74 }
75 name[i2]=sourceOfParams[i];
76 }
77 if(sw){
78 //配列パラメータ
79 if( isRef == false ) SmoothieException::Throw(29,NULL,nowLine);
80 isArray = true;
81
82 if((name[i2-2]=='('&&name[i2-1]==')')||
83 (name[i2-2]=='['&&name[i2-1]==']')){
84 subScripts[0]=LONG_MAX;
85 subScripts[1]=-1;
86
87 name[i2-2]=0;
88 }
89 else{
90 GetArrange(name,temp2,subScripts);
91 lstrcpy(name,temp2);
92 }
93
94 i2=lstrlen(name);
95 }
96
97 Type type( DEF_NON );
98 char initValue[8192] = "";
99 if( sourceOfParams[i] == '=' ){
100 i++;
101 i = GetOneParameter( sourceOfParams, i, initValue );
102
103 // TODO: エラー用 fix me!!!
104 //cp = nowLine;
105
106 NumOpe_GetType( initValue, Type::String(), type );
107 }
108 else if(sourceOfParams[i]==1&&sourceOfParams[i+1]==ESC_AS){
109 // As指定
110 i+=2;
111
112 i2=0;
113 while(sourceOfParams[i]=='*'){
114 temporary[i2]=sourceOfParams[i];
115 i++;
116 i2++;
117 }
118 for(;;i++,i2++){
119 if(!IsVariableChar(sourceOfParams[i])){
120 if(sourceOfParams[i]==1&&(sourceOfParams[i+1]==ESC_FUNCTION||sourceOfParams[i+1]==ESC_SUB)){
121 temporary[i2++]=sourceOfParams[i++];
122 temporary[i2]=sourceOfParams[i];
123 continue;
124 }
125 temporary[i2]=0;
126 break;
127 }
128 temporary[i2]=sourceOfParams[i];
129 }
130
131 Type::StringToType( temporary, type );
132
133 if( type.IsNull() ){
134 SmoothieException::Throw(3,temporary,nowLine);
135 type.SetBasicType( DEF_PTR_VOID );
136 }
137
138 if( type.IsObject() ){
139 if( type.GetClass().IsBlittableType() ){
140 // Blittable型のときは基本型として扱う
141 type = type.GetClass().GetBlittableType();
142 }
143 }
144 }
145 else{
146 type.SetBasicType( Type::GetBasicTypeFromSimpleName(temporary) );
147 SmoothieException::Throw(-103,temporary,nowLine);
148 }
149
150 Parameter *pParam = new Parameter( name, type, isRef, initValue );
151 if( isArray ){
152 pParam->SetArray( subScripts );
153 }
154
155 //パラメータを追加
156 this->params.push_back( pParam );
157
158 if(sourceOfParams[i]==','){
159 i++;
160 continue;
161 }
162 else if(sourceOfParams[i]==')') continue;
163 else{
164 SmoothieException::Throw(1,NULL,nowLine);
165 break;
166 }
167 }
168 this->secondParmNum = (int)this->params.size();
169 i++;
170 if(sourceOfParams[i]=='('){
171 i++;
172 while(1){
173 if(sourceOfParams[i]==')') break;
174
175 //ByRef
176 bool isRef;
177 if(sourceOfParams[i]==1&&sourceOfParams[i+1]==ESC_BYVAL){
178 isRef = false;
179 i+=2;
180 }
181 else if(sourceOfParams[i]==1&&sourceOfParams[i+1]==ESC_BYREF){
182 isRef = true;
183 i+=2;
184 }
185 else isRef = false;
186
187 //パラメータ名
188 bool isArray = false;
189 int subScripts[MAX_ARRAYDIM];
190 char name[VN_SIZE];
191 sw=0;
192 for(i2=0;;i++,i2++){
193 if(sourceOfParams[i]=='('){
194 if(!sw) sw=1;
195
196 i3=GetStringInPare(name+i2,sourceOfParams+i);
197 i2+=i3-1;
198 i+=i3-1;
199 continue;
200 }
201 if(sourceOfParams[i]=='['){
202 if(!sw) sw=1;
203
204 i3=GetStringInBracket(name+i2,sourceOfParams+i);
205 i2+=i3-1;
206 i+=i3-1;
207 continue;
208 }
209 if(!IsVariableChar(sourceOfParams[i])){
210 name[i2]=0;
211 break;
212 }
213 name[i2]=sourceOfParams[i];
214 }
215 if(sw){
216 //配列パラメータ
217 if( isRef == false ) SmoothieException::Throw(29,NULL,nowLine);
218 isArray = true;
219
220 if((name[i2-2]=='('&&name[i2-1]==')')||
221 (name[i2-2]=='['&&name[i2-1]==']')){
222 subScripts[0]=LONG_MAX;
223 subScripts[1]=-1;
224
225 name[i2-2]=0;
226 }
227 else{
228 GetArrange(name,temp2,subScripts);
229 lstrcpy(name,temp2);
230 }
231
232 i2=lstrlen(name);
233 }
234
235 //型
236 Type type( DEF_NON );
237 if(sourceOfParams[i]==1&&sourceOfParams[i+1]==ESC_AS){
238 i+=2;
239
240 i2=0;
241 while(sourceOfParams[i]=='*'){
242 temporary[i2]=sourceOfParams[i];
243 i++;
244 i2++;
245 }
246 for(;;i++,i2++){
247 if(!IsVariableChar(sourceOfParams[i])){
248 if(sourceOfParams[i]==1&&(sourceOfParams[i+1]==ESC_FUNCTION||sourceOfParams[i+1]==ESC_SUB)){
249 temporary[i2++]=sourceOfParams[i++];
250 temporary[i2]=sourceOfParams[i];
251 continue;
252 }
253 temporary[i2]=0;
254 break;
255 }
256 temporary[i2]=sourceOfParams[i];
257 }
258
259 Type::StringToType( temporary, type );
260
261 if( type.IsNull() ){
262 SmoothieException::Throw(3,temporary,nowLine);
263 type.SetBasicType( DEF_PTR_VOID );
264 }
265 }
266 else{
267 type.SetBasicType( Type::GetBasicTypeFromSimpleName(temporary) );
268 SmoothieException::Throw(-103,temporary,nowLine);
269 }
270
271 Parameter *pParam = new Parameter( name, type, isRef );
272 if( isArray ){
273 pParam->SetArray( subScripts );
274 }
275
276 //パラメータを追加
277 this->params.push_back( pParam );
278
279 if(sourceOfParams[i]==','){
280 i++;
281 continue;
282 }
283 else if(sourceOfParams[i]==')') continue;
284 else{
285 SmoothieException::Throw(1,NULL,nowLine);
286 break;
287 }
288 }
289 i++;
290 }
291
292 if(sourceOfParams[i]){
293 ///////////////////
294 // 戻り値を取得
295 ///////////////////
296
297 if( !this->IsFunction() ){
298 // Sub/Macroの場合
299 SmoothieException::Throw(38,this->GetName(),nowLine);
300 }
301
302 if( this->pParentClass ){
303 if( this->GetName() == this->pParentClass->GetName() ||
304 this->GetName()[0]=='~'){
305 //クラスのコンストラクタ、デストラクタがFunction定義の場合はエラーをだす
306 SmoothieException::Throw(115,NULL,nowLine);
307 }
308 }
309
310
311 i2=lstrlen(sourceOfParams)-2;
312
313 int sw_as=0;
314 for(;i2>0;i2--){
315 if(sourceOfParams[i2]==')') break;
316
317 if(sourceOfParams[i2]==1&&sourceOfParams[i2+1]==ESC_AS){
318 i2+=2;
319 i3=0;
320 while(sourceOfParams[i2]=='*') temporary[i3++]=sourceOfParams[i2++];
321 for(;;i2++,i3++){
322 if(!IsVariableChar(sourceOfParams[i2])){
323 temporary[i3]=0;
324 break;
325 }
326 temporary[i3]=sourceOfParams[i2];
327 }
328 Type::StringToType( temporary, this->returnType );
329 if( this->returnType.IsNull() ) SmoothieException::Throw(3,temporary,nowLine);
330
331 sw_as=1;
332 break;
333 }
334 }
335
336 if(!sw_as){
337 SmoothieException::Throw(-104,this->GetName().c_str(),nowLine);
338
339 this->returnType.SetBasicType( DEF_DOUBLE );
340 }
341 }
342 else{
343 //戻り値なしのSub定義
344 this->returnType.SetNull();
345 }
346
347 //リアルパラメータ領域を取得(_System_LocalThisを考慮して2つだけ多く確保する場合がある)
348
349 if( this->pParentClass && isStatic == false ){
350 //オブジェクトメンバの場合は、第一パラメータを_System_LocalThis引き渡し用として利用
351 string name = "_System_LocalThis";
352 Type type( DEF_PTR_VOID );
353 this->realParams.push_back( new Parameter( name, type ) );
354 }
355
356 if( this->returnType.IsStruct() ){
357 //構造体を戻り値として持つ場合
358 //※第一パラメータ(Thisポインタありの場合は第二パラメータ)を戻り値用の参照宣言にする
359
360 string name = this->GetName();
361 if(name[0]==1&&name[1]==ESC_OPERATOR){
362 name="_System_ReturnValue";
363 }
364 Type type( DEF_STRUCT, this->returnType.GetIndex() );
365 this->realParams.push_back( new Parameter( name, type, true ) );
366 }
367
368 //パラメータをコピー
369 BOOST_FOREACH( Parameter *pParam, params ){
370 this->realParams.push_back( new Parameter( *pParam ) );
371 }
372
373 return true;
374}
375
376const NamespaceScopes &GlobalProc::GetNamespaceScopes() const
377{
378 if( HasParentClass() ){
379 return GetParentClassPtr()->GetNamespaceScopes();
380 }
381 return namespaceScopes;
382}
383bool GlobalProc::IsEqualSymbol( const NamespaceScopes &namespaceScopes, const string &name ) const
384{
385 if( GetName() != name ){
386 return false;
387 }
388
389 return NamespaceScopes::IsSameArea( GetNamespaceScopes(), namespaceScopes );
390}
391bool GlobalProc::IsEqualSymbol( const GlobalProc &globalProc ) const
392{
393 return IsEqualSymbol( globalProc.GetNamespaceScopes(), globalProc.GetName() );
394}
395bool GlobalProc::IsEqualSymbol( const string &fullName ) const
396{
397 char AreaName[VN_SIZE] = ""; //オブジェクト変数
398 char NestName[VN_SIZE] = ""; //入れ子メンバ
399 bool isNest = CClass::SplitName( fullName.c_str(), AreaName, NestName );
400
401 return IsEqualSymbol( NamespaceScopes( AreaName ), NestName );
402}
403
404bool DllProcImpl::SetParamsAndReturnType( const char *sourceOfParams, int nowLine ){
405 int i = 0;
406 int i2,i3,sw;
407 char temporary[8192],temp2[VN_SIZE];
408
409 //ソースコードの位置
410 this->codePos = nowLine;
411
412 //パラメータ
413 if(sourceOfParams[i]!='('){
414 SmoothieException::Throw(1,NULL,nowLine);
415 return 0;
416 }
417 i++;
418
419 while(1){
420 if(sourceOfParams[i]==')') break;
421
422 //ByRef
423 bool isRef;
424 if(sourceOfParams[i]==1&&sourceOfParams[i+1]==ESC_BYVAL){
425 isRef = false;
426 i+=2;
427 }
428 else if(sourceOfParams[i]==1&&sourceOfParams[i+1]==ESC_BYREF){
429 isRef = true;
430 i+=2;
431 }
432 else isRef = false;
433
434 //パラメータ名
435 bool isArray = false;
436 int subScripts[MAX_ARRAYDIM];
437 char name[VN_SIZE];
438 sw=0;
439 for(i2=0;;i++,i2++){
440 if(sourceOfParams[i]=='('){
441 if(!sw) sw=1;
442
443 i3=GetStringInPare(name+i2,sourceOfParams+i);
444 i2+=i3-1;
445 i+=i3-1;
446 continue;
447 }
448 if(sourceOfParams[i]=='['){
449 if(!sw) sw=1;
450
451 i3=GetStringInBracket(name+i2,sourceOfParams+i);
452 i2+=i3-1;
453 i+=i3-1;
454 continue;
455 }
456 if(!IsVariableChar(sourceOfParams[i])){
457 name[i2]=0;
458 break;
459 }
460 name[i2]=sourceOfParams[i];
461 }
462 if(sw){
463 //配列パラメータ
464 if( isRef == false ) SmoothieException::Throw(29,NULL,nowLine);
465 isArray = true;
466
467 if((name[i2-2]=='('&&name[i2-1]==')')||
468 (name[i2-2]=='['&&name[i2-1]==']')){
469 subScripts[0]=LONG_MAX;
470 subScripts[1]=-1;
471
472 name[i2-2]=0;
473 }
474 else{
475 GetArrange(name,temp2,subScripts);
476 lstrcpy(name,temp2);
477 }
478
479 i2=lstrlen(name);
480 }
481
482 //型
483 Type type( DEF_NON );
484 if(lstrcmp(name,"...")==0) type.SetBasicType( DEF_ELLIPSE );
485 else if(sourceOfParams[i]==1&&sourceOfParams[i+1]==ESC_AS){
486 i+=2;
487
488 i2=0;
489 while(sourceOfParams[i]=='*'){
490 temporary[i2]=sourceOfParams[i];
491 i++;
492 i2++;
493 }
494 for(;;i++,i2++){
495 if(!IsVariableChar(sourceOfParams[i])){
496 if(sourceOfParams[i]==1&&(sourceOfParams[i+1]==ESC_FUNCTION||sourceOfParams[i+1]==ESC_SUB)){
497 temporary[i2++]=sourceOfParams[i++];
498 temporary[i2]=sourceOfParams[i];
499 continue;
500 }
501 temporary[i2]=0;
502 break;
503 }
504 temporary[i2]=sourceOfParams[i];
505 }
506
507 Type::StringToType( temporary, type );
508
509 if( type.IsNull() ){
510 SmoothieException::Throw(3,temporary,nowLine);
511 type.SetBasicType( DEF_PTR_VOID );
512 }
513 }
514 else{
515 type.SetBasicType( Type::GetBasicTypeFromSimpleName(temporary) );
516 SmoothieException::Throw(-103,temporary,nowLine);
517 }
518
519 Parameter *pParam = new Parameter( name, type, isRef );
520 if( isArray ){
521 pParam->SetArray( subScripts );
522 }
523
524 //パラメータを追加
525 this->params.push_back( pParam );
526
527 if(sourceOfParams[i]==','){
528 i++;
529 continue;
530 }
531 else if(sourceOfParams[i]==')') continue;
532 else{
533 SmoothieException::Throw(1,NULL,nowLine);
534 break;
535 }
536 }
537 i++;
538
539 if(sourceOfParams[i]){
540 ///////////////////
541 // 戻り値を取得
542 ///////////////////
543
544 i2=lstrlen(sourceOfParams)-2;
545
546 int sw_as=0;
547 for(;i2>0;i2--){
548 if(sourceOfParams[i2]==')') break;
549
550 if(sourceOfParams[i2]==1&&sourceOfParams[i2+1]==ESC_AS){
551 i2+=2;
552 i3=0;
553 while(sourceOfParams[i2]=='*') temporary[i3++]=sourceOfParams[i2++];
554 for(;;i2++,i3++){
555 if(!IsVariableChar(sourceOfParams[i2])){
556 temporary[i3]=0;
557 break;
558 }
559 temporary[i3]=sourceOfParams[i2];
560 }
561 Type::StringToType( temporary, this->returnType );
562 if( this->returnType.IsNull() ) SmoothieException::Throw(3,temporary,nowLine);
563
564 sw_as=1;
565 break;
566 }
567 }
568 }
569 else{
570 //戻り値なしのSub定義
571 this->returnType.SetNull();
572 }
573
574 return true;
575}
576
577bool ProcPointerImpl::SetParamsAndReturnType( const char *sourceOfParams, int nowLine ){
578 int i = 0;
579 int i2,i3,sw;
580 char temporary[8192],temp2[VN_SIZE];
581
582 //ソースコードの位置
583 this->codePos = nowLine;
584
585 //パラメータ
586 if(sourceOfParams[i]!='('){
587 SmoothieException::Throw(1,NULL,nowLine);
588 return 0;
589 }
590 i++;
591 while(1){
592 if(sourceOfParams[i]==')') break;
593
594 //ByRef
595 bool isRef;
596 if(sourceOfParams[i]==1&&sourceOfParams[i+1]==ESC_BYVAL){
597 isRef = false;
598 i+=2;
599 }
600 else if(sourceOfParams[i]==1&&sourceOfParams[i+1]==ESC_BYREF){
601 isRef = true;
602 i+=2;
603 }
604 else isRef = false;
605
606 //パラメータ名
607 bool isArray = false;
608 int subScripts[MAX_ARRAYDIM];
609 char name[VN_SIZE];
610 sw=0;
611 for(i2=0;;i++,i2++){
612 if(sourceOfParams[i]=='('){
613 if(!sw) sw=1;
614
615 i3=GetStringInPare(name+i2,sourceOfParams+i);
616 i2+=i3-1;
617 i+=i3-1;
618 continue;
619 }
620 if(sourceOfParams[i]=='['){
621 if(!sw) sw=1;
622
623 i3=GetStringInBracket(name+i2,sourceOfParams+i);
624 i2+=i3-1;
625 i+=i3-1;
626 continue;
627 }
628 if(!IsVariableChar(sourceOfParams[i])){
629 name[i2]=0;
630 break;
631 }
632 name[i2]=sourceOfParams[i];
633 }
634 if(sw){
635 //配列パラメータ
636 if( isRef == false ) SmoothieException::Throw(29,NULL,nowLine);
637 isArray = true;
638
639 if((name[i2-2]=='('&&name[i2-1]==')')||
640 (name[i2-2]=='['&&name[i2-1]==']')){
641 subScripts[0]=LONG_MAX;
642 subScripts[1]=-1;
643
644 name[i2-2]=0;
645 }
646 else{
647 GetArrange(name,temp2,subScripts);
648 lstrcpy(name,temp2);
649 }
650
651 i2=lstrlen(name);
652 }
653
654 //型
655 Type type( DEF_NON );
656 if(sourceOfParams[i]==1&&sourceOfParams[i+1]==ESC_AS){
657 i+=2;
658
659 i2=0;
660 while(sourceOfParams[i]=='*'){
661 temporary[i2]=sourceOfParams[i];
662 i++;
663 i2++;
664 }
665 for(;;i++,i2++){
666 if(!IsVariableChar(sourceOfParams[i])){
667 if(sourceOfParams[i]==1&&(sourceOfParams[i+1]==ESC_FUNCTION||sourceOfParams[i+1]==ESC_SUB)){
668 temporary[i2++]=sourceOfParams[i++];
669 temporary[i2]=sourceOfParams[i];
670 continue;
671 }
672 temporary[i2]=0;
673 break;
674 }
675 temporary[i2]=sourceOfParams[i];
676 }
677
678 Type::StringToType( temporary, type );
679
680 if( type.IsNull() ){
681 SmoothieException::Throw(3,temporary,nowLine);
682 type.SetBasicType( DEF_PTR_VOID );
683 }
684 }
685 else{
686 type.SetBasicType( Type::GetBasicTypeFromSimpleName(temporary) );
687 SmoothieException::Throw(-103,temporary,nowLine);
688 }
689
690 Parameter *pParam = new Parameter( name, type, isRef );
691 if( isArray ){
692 pParam->SetArray( subScripts );
693 }
694
695 //パラメータを追加
696 this->params.push_back( pParam );
697
698 if(sourceOfParams[i]==','){
699 i++;
700 continue;
701 }
702 else if(sourceOfParams[i]==')') continue;
703 else{
704 SmoothieException::Throw(1,NULL,nowLine);
705 break;
706 }
707 }
708 i++;
709
710 if(sourceOfParams[i]){
711 ///////////////////
712 // 戻り値を取得
713 ///////////////////
714
715 i2=lstrlen(sourceOfParams)-2;
716
717 int sw_as=0;
718 for(;i2>0;i2--){
719 if(sourceOfParams[i2]==')') break;
720
721 if(sourceOfParams[i2]==1&&sourceOfParams[i2+1]==ESC_AS){
722 i2+=2;
723 i3=0;
724 while(sourceOfParams[i2]=='*') temporary[i3++]=sourceOfParams[i2++];
725 for(;;i2++,i3++){
726 if(!IsVariableChar(sourceOfParams[i2])){
727 temporary[i3]=0;
728 break;
729 }
730 temporary[i3]=sourceOfParams[i2];
731 }
732 Type::StringToType( temporary, this->returnType );
733 if( this->returnType.IsNull() ) SmoothieException::Throw(3,temporary,nowLine);
734
735 sw_as=1;
736 break;
737 }
738 }
739 }
740 else{
741 //戻り値なしのSub定義
742 this->returnType.SetNull();
743 }
744
745 //戻り値のエラーチェック
746 if( IsFunction() ){
747 // Function定義
748
749 if( this->ReturnType().IsNull() ){
750 // 戻り値がない
751 SmoothieException::Throw(26,this->GetName(),nowLine);
752 }
753 }
754 else{
755 if( !this->ReturnType().IsNull() ){
756 // Sub定義なのに、戻り値がある
757 SmoothieException::Throw(38,this->GetName(),nowLine);
758 }
759 }
760
761 return true;
762}
763
764int ProcPointersImpl::Add( const string &typeExpression )
765{
766 DWORD dwProcType = (DWORD)typeExpression[2];
767 const string &paramStr = typeExpression.substr( 3 );
768
769 Procedure::Kind kind = Procedure::Sub;
770 if( dwProcType == ESC_FUNCTION ){
771 kind = Procedure::Function;
772 }
773
774 ProcPointer *pProcPointer = new ProcPointerImpl( kind );
775
776 //buffer[0]は'('となっている
777 extern int cp;
778 pProcPointer->SetParamsAndReturnType( paramStr.c_str(), cp );
779
780 this->push_back( pProcPointer );
781
782 return (int)this->size()-1;
783}
784
785void ProcPointersImpl::Clear()
786{
787 ProcPointersImpl &procPointers = *this;
788 BOOST_FOREACH( ProcPointer *pProcPointer, procPointers ){
789 delete pProcPointer;
790 }
791 this->clear();
792}
Note: See TracBrowser for help on using the repository browser.