source: dev/BasicCompiler_Common/Intermediate_Step2.cpp@ 43

Last change on this file since 43 was 43, checked in by dai_9181, 17 years ago
File size: 16.2 KB
Line 
1#include "../BasicCompiler_Common/common.h"
2
3CONSTINFO *GetNewConstHash(char *name){
4 extern int cp;
5 CONSTINFO *pci;
6
7 int key;
8 key=hash_default(name);
9
10 extern CONSTINFO **ppConstHash;
11 if(ppConstHash[key]){
12 pci=ppConstHash[key];
13 while(1){
14 if(lstrcmp(pci->name,name)==0){
15 //重複エラー
16 SetError(15,name,cp);
17 return 0;
18 }
19
20 if(pci->pNextData==0){
21 pci->pNextData=(CONSTINFO *)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,sizeof(CONSTINFO));
22 break;
23 }
24 pci=pci->pNextData;
25 }
26 pci=pci->pNextData;
27 }
28 else{
29 ppConstHash[key]=(CONSTINFO *)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,sizeof(CONSTINFO));
30 pci=ppConstHash[key];
31 }
32
33 return pci;
34}
35
36// マクロ定数を追加するための関数
37void AddConstData(char *Command){
38 extern HANDLE hHeap;
39 extern int cp;
40 int i,i2;
41 char temporary[VN_SIZE];
42
43 for(i=0;;i++){
44 if(Command[i]=='\0'){
45 SetError(10,"Const",cp);
46 return;
47 }
48 if(Command[i]=='=' || Command[i] == 1 && Command[i+1] == ESC_AS ){
49 //定数定義は新しいクラスモジュール(CDBConst)へ移行
50 // ※この関数はマクロ定数のみを扱う
51 return;
52 }
53 if(Command[i]=='('){
54 temporary[i]=0;
55 break;
56 }
57 temporary[i]=Command[i];
58 }
59
60
61 /////////////////////////////////
62 // 格納位置を計算してpciにセット
63 /////////////////////////////////
64
65 CONSTINFO *pci;
66 pci=GetNewConstHash(temporary);
67 if(!pci) return;
68
69
70
71 ////////////////////
72 // 定数情報を取得
73 ////////////////////
74
75 //定数名
76 pci->name=(char *)HeapAlloc(hHeap,0,lstrlen(temporary)+1);
77 lstrcpy(pci->name,temporary);
78
79 if(Command[i]=='('){
80 pci->ppParm=(char **)HeapAlloc(hHeap,0,1);
81 pci->ParmNum=0;
82 for(i++,i2=0;;i++,i2++){
83 if(Command[i]=='\0'){
84 SetError(1,NULL,cp);
85 return;
86 }
87 if(Command[i]==','||Command[i]==')'){
88 temporary[i2]=0;
89 pci->ppParm=(char **)HeapReAlloc(hHeap,0,pci->ppParm,(pci->ParmNum+1)*sizeof(char *));
90 pci->ppParm[pci->ParmNum]=(char *)HeapAlloc(hHeap,0,lstrlen(temporary)+1);
91 lstrcpy(pci->ppParm[pci->ParmNum],temporary);
92 pci->ParmNum++;
93 if(Command[i]==')'){
94 i++;
95 if(Command[i]!='='){
96 SetError(1,NULL,cp);
97 return;
98 }
99 break;
100 }
101
102 i2=-1;
103 continue;
104 }
105 temporary[i2]=Command[i];
106 }
107 }
108 else pci->ParmNum=0;
109
110 //データ
111 lstrcpy(temporary,Command+i+1);
112 if(pci->ParmNum){
113 pci->StrValue=(char *)HeapAlloc(hHeap,0,lstrlen(temporary)+1);
114 lstrcpy(pci->StrValue,temporary);
115 }
116 else if(temporary[0]=='\"'){
117 //文字列定数
118 RemoveStringQuotes(temporary);
119 i2=lstrlen(temporary);
120
121 pci->StrValue=(char *)HeapAlloc(hHeap,0,i2+1);
122 memcpy(pci->StrValue,temporary,i2);
123 pci->StrValue[i2]=0;
124
125 pci->DblValue=(double)i2;
126
127 pci->type=DEF_STRING;
128 pci->lpIndex=-1;
129 }
130 else if((temporary[0]=='e'||temporary[0]=='E')&&
131 (temporary[1]=='x'||temporary[1]=='X')&&
132 temporary[2]=='\"'){
133 //文字列定数
134 RemoveStringQuotes(temporary+2);
135 i2=FormatString_EscapeSequence(temporary+2);
136 pci->StrValue=(char *)HeapAlloc(hHeap,0,i2+1);
137 memcpy(pci->StrValue,temporary+2,i2);
138 pci->StrValue[i2]=0;
139
140 pci->DblValue=(double)i2;
141
142 pci->type=DEF_STRING;
143 pci->lpIndex=-1;
144 }
145 else{
146 //数値定数
147 _int64 i64data;
148 pci->type=StaticCalculation(true, temporary,0,&i64data,&pci->lpIndex);
149 if(IsRealNumberType(pci->type)){
150 //実数型
151 memcpy(&pci->DblValue,&i64data,sizeof(double));
152 }
153 else if(Is64Type(pci->type)){
154 //64ビット型
155 pci->i64Value=i64data;
156 }
157 else if(IsWholeNumberType(pci->type)){
158 //その他整数型
159 pci->DblValue=(double)i64data;
160 }
161
162 pci->StrValue=0;
163 }
164}
165void AddConstEnum(char *buffer){
166 extern int cp;
167 int i=0,i2;
168
169 if(!(buffer[i]==1&&buffer[i+1]==ESC_ENUM)) return;
170 i+=2;
171
172 //列挙体の名前を取得
173 char temporary[VN_SIZE];
174 for(i2=0;;i++,i2++){
175 if(IsCommandDelimitation(buffer[i])){
176 temporary[i2]=0;
177 break;
178 }
179 if(!IsVariableChar(buffer[i])){
180 SetError(1,NULL,i);
181 break;
182 }
183 temporary[i2]=buffer[i];
184 }
185
186 //新しい型情報を追加
187 pobj_DBTypeDef->add(temporary,"Long");
188
189 if(buffer[i]=='\0'){
190 SetError(22,"Enum",cp);
191 return;
192 }
193
194 int NextValue=0;
195 while(1){
196 i++;
197
198 if(buffer[i]==1&&buffer[i+1]==ESC_ENDENUM) break;
199
200 for(i2=0;;i2++,i++){
201 if(IsCommandDelimitation(buffer[i])){
202 temporary[i2]=0;
203 break;
204 }
205 if(buffer[i]=='='){
206 temporary[i2]=0;
207 break;
208 }
209 temporary[i2]=buffer[i];
210 }
211 if(temporary[0]=='\0'){
212 if(buffer[i]=='\0'){
213 SetError(22,"Enum",cp);
214 break;
215 }
216 continue;
217 }
218
219 if(buffer[i]!='='){
220 NextValue++;
221 }
222 else{
223 char temp2[VN_SIZE];
224 for(i++,i2=0;;i2++,i++){
225 if(IsCommandDelimitation(buffer[i])){
226 temp2[i2]=0;
227 break;
228 }
229 temp2[i2]=buffer[i];
230 }
231
232 _int64 i64data;
233 LONG_PTR lpIndex;
234 StaticCalculation(true, temp2,DEF_LONG,&i64data,&lpIndex);
235 NextValue=(int)i64data;
236 }
237
238 //定数を追加
239 CDBConst::obj.AddConst(temporary, NextValue);
240 }
241}
242bool GetConstInfo(void){
243 ////////////////////////////////////////////
244 // Const命令の情報を取得
245 ////////////////////////////////////////////
246
247 int i,i2;
248 char temporary[1024];
249
250 //定数に関する情報
251 extern CONSTINFO **ppConstHash;
252 ppConstHash=(CONSTINFO **)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,MAX_HASH*sizeof(CONSTINFO *));
253
254 extern char *basbuf;
255 for(i=0;;i++){
256 if( basbuf[i] == '\0' ) break;
257 if( basbuf[i] == 1 ){
258 if(basbuf[i]==1&&basbuf[i+1]==ESC_CONST){
259 i+=2;
260
261 extern int cp;
262 cp=i; //エラー用
263
264
265 if(basbuf[i]==1&&basbuf[i+1]==ESC_ENUM){
266 AddConstEnum(basbuf+i);
267 continue;
268 }
269
270 for(i2=0;;i++,i2++){
271 if(basbuf[i]=='\"'){
272 temporary[i2]=basbuf[i];
273 for(i++,i2++;;i++,i2++){
274 temporary[i2]=basbuf[i];
275 if(basbuf[i]=='\"') break;
276 }
277 continue;
278 }
279 if(IsCommandDelimitation(basbuf[i])){
280 temporary[i2]=0;
281 break;
282 }
283 temporary[i2]=basbuf[i];
284 }
285 CDBConst::obj.Add(temporary);
286 if(basbuf[i]=='\0') break;
287 }
288 else{
289 int result = JumpStatement( basbuf, i );
290 if( result == -1 ){
291 //エラー
292 return false;
293 }
294 else if( result == 1 ){
295 //ジャンプした場合
296 i--;
297 }
298 }
299 }
300 }
301 return true;
302}
303
304char ConstructorDestructorSchedule[MAX_PATH];
305void MakeConstructorAndDestructor(char *buffer,int NowLine,char *ClassName){
306 int i,i2;
307 char temporary[MAX_PATH],*pTemp;
308 BOOL bConstructor,bDestructor;
309
310 ConstructorDestructorSchedule[0]=0;
311 bConstructor=0;
312 bDestructor=0;
313
314 for(i=NowLine;;i++){
315 if(buffer[i]=='\0') break;
316 if(buffer[i]==1&&buffer[i+1]==ESC_ENDCLASS){
317 if((!bConstructor)||(!bDestructor)){
318 pTemp=ConstructorDestructorSchedule;
319
320 lstrcpy(pTemp,"Public:");
321
322 if(!bConstructor){
323 //コンストラクタが無いときは生成する
324 sprintf(pTemp+lstrlen(pTemp),"%c%c%s():%c%c:",
325 1,ESC_SUB,
326 ClassName,
327 1,ESC_ENDSUB);
328 }
329
330 if(!bDestructor){
331 //デストラクタが無いときは生成する
332 sprintf(pTemp+lstrlen(pTemp),"%c%c~%s():%c%c:",
333 1,ESC_SUB,
334 ClassName,
335 1,ESC_ENDSUB);
336 }
337 }
338 break;
339 }
340
341 if(buffer[i]==1&&(buffer[i+1]==ESC_SUB||buffer[i+1]==ESC_FUNCTION)){
342 i+=2;
343 while(IsBlank(buffer[i])) i++;
344 if(buffer[i]=='~'){
345 //デストラクタ
346 bDestructor=1;
347 }
348 else{
349 //コンストラクタかどうかをチェック
350 for(i2=0;;i++,i2++){
351 if(!IsVariableChar(buffer[i])){
352 temporary[i2]=0;
353 break;
354 }
355 temporary[i2]=buffer[i];
356 }
357 if(lstrcmp(temporary,ClassName)==0) bConstructor=1;
358 }
359 }
360 }
361}
362void ChangeCommand(char *buffer,int NowLine,char *Command){
363 int i,i2,IsStr;
364 unsigned _int16 ComNum;
365 char com[8192],pam[8192];
366
367 static int nCountOfNonGlobalScope = 0;
368
369 if(Command[0]==1){
370 switch(Command[1]){
371 case ESC_SELECTCASE:
372 case ESC_CASE:
373 KillStringSpaces(Command+2);
374 break;
375 case ESC_WITH:
376 KillStringSpaces(Command+2);
377 break;
378 case ESC_TYPEDEF:
379 KillStringSpaces(Command+2);
380 AddTypeDefData(Command+2);
381 break;
382 case ESC_DECLARE:
383 KillStringSpaces(Command+2);
384 break;
385 case ESC_IF:
386 KillStringSpaces(Command+2);
387 break;
388
389 case ESC_CLASS:
390 KillStringSpaces(Command+2);
391
392 //コンストラクタ、デストラクタを暗黙的に生成
393 MakeConstructorAndDestructor(buffer,NowLine,Command+2);
394 break;
395 case ESC_INTERFACE:
396 KillStringSpaces(Command+2);
397 break;
398 case ESC_ENDCLASS:
399 if(ConstructorDestructorSchedule[0]){
400 //生成されたコンストラクタ、デストラクタを挿入
401 sprintf(Command,"%s%c%c",ConstructorDestructorSchedule,1,ESC_ENDCLASS);
402 }
403 break;
404
405 case ESC_TYPE:
406 KillStringSpaces(Command+2);
407 break;
408
409 case ESC_CONST:
410 KillStringSpaces(Command+2);
411 if( Command[2] == 1 && Command[3] == ESC_ENUM ){
412 nCountOfNonGlobalScope++;
413 }
414 break;
415
416 case ESC_ENUM:
417 nCountOfNonGlobalScope++;
418 KillStringSpaces(Command+2);
419 break;
420
421 case ESC_ENDENUM:
422 nCountOfNonGlobalScope--;
423 break;
424
425 case ESC_INHERITS:
426 case ESC_VIRTUAL:
427 case ESC_OVERRIDE:
428 case ESC_ABSTRACT:
429 case ESC_SUB:
430 case ESC_FUNCTION:
431 case ESC_MACRO:
432 case ESC_STATIC:
433 KillStringSpaces(Command+2);
434 break;
435 }
436 return;
437 }
438
439 bool isPare = false;
440 for(i=0;;i++){
441 if(Command[i]==' '||Command[i]=='\t'||Command[i]=='('||Command[i]=='\"'||Command[i]=='@'||Command[i]=='-'){
442 com[i]=0;
443 while(Command[i]==' '||Command[i]=='\t') i++;
444 if( Command[i] == '(' ) isPare = true;
445 break;
446 }
447 if(Command[i]=='='){
448 KillStringSpaces(Command);
449 return;
450 }
451 com[i]=Command[i];
452 if(Command[i]=='\0') break;
453 }
454
455 //マクロによるコマンド
456 i2=1;
457 if( nCountOfNonGlobalScope == 0 ){
458 //グローバル
459 if(lstrcmpi(com,"Open")==0) ComOpen(Command+i,pam,NowLine);
460 else if(lstrcmpi(com,"Close")==0) ComClose(Command+i,pam);
461 else if(lstrcmpi(com,"Field")==0||
462 lstrcmpi(com,"Get")==0||
463 lstrcmpi(com,"Put")==0) ComField(Command+i,pam);
464 else if(lstrcmpi(com,"Line")==0) ComLine(Command+i,pam,NowLine);
465 else if(lstrcmpi(com,"Circle")==0) ComCircle(Command+i,pam,NowLine);
466 else if(lstrcmpi(com,"PSet")==0) ComPSet(Command+i,pam,NowLine);
467 else if(lstrcmpi(com,"Paint")==0) ComPaint(Command+i,pam,NowLine);
468
469 else if(
470 lstrcmpi(com,"EXEC")==0||
471 lstrcmpi(com,"INPUT")==0||
472 lstrcmpi(com,"PRINT")==0||
473 lstrcmpi(com,"RANDOMIZE")==0||
474 ( lstrcmpi(com,"WRITE")==0 && isPare == false )||
475 lstrcmpi(com,"MSGBOX")==0||
476 lstrcmpi(com,"WINDOW")==0||
477 lstrcmpi(com,"DELWND")==0||
478 lstrcmpi(com,"INSMENU")==0||
479 lstrcmpi(com,"CHDIR")==0||
480 lstrcmpi(com,"MKDIR")==0||
481 lstrcmpi(com,"KILL")==0||
482 lstrcmpi(com,"CLS")==0||
483 lstrcmpi(com,"COLOR")==0||
484 lstrcmpi(com,"LOCATE")==0
485 ){
486 KillSpaces(Command+i,pam);
487
488 //大文字に変換
489 CharUpper(com);
490
491 sprintf(Command,"%s(%s)",com,pam);
492 return;
493 }
494
495 else i2=0;
496 }
497 else i2=0;
498 if(i2){
499 //大文字に変換
500 CharUpper(com);
501
502 sprintf(Command,"%s(%s)",com,pam);
503 return;
504 }
505
506
507
508 //コンパイラに搭載されるコマンド
509 if(lstrcmpi(com,"Do")==0){
510 KillSpaces(Command+i,pam);
511 ComNum=COM_DO;
512 }
513 else if(lstrcmpi(com,"goto")==0){
514 KillSpaces(Command+i,pam);
515 ComNum=COM_GOTO;
516 }
517 else if(lstrcmpi(com,"gosub")==0){
518 KillSpaces(Command+i,pam);
519 ComNum=COM_GOSUB;
520 }
521 else if(lstrcmpi(com,"Loop")==0){
522 if((Command[i]=='w'||Command[i]=='W')&&(Command[i+1]=='h'||Command[i+1]=='H')&&(Command[i+2]=='i'||Command[i+2]=='I')&&(Command[i+3]=='l'||Command[i+3]=='L')&&(Command[i+4]=='e'||Command[i+4]=='E')){
523 lstrcpy(pam,"0,");
524 KillSpaces(Command+i+5,pam+2);
525 }
526 else if((Command[i]=='u'||Command[i]=='U')&&(Command[i+1]=='n'||Command[i+1]=='N')&&(Command[i+2]=='t'||Command[i+2]=='T')&&(Command[i+3]=='i'||Command[i+3]=='I')&&(Command[i+4]=='l'||Command[i+4]=='L')){
527 lstrcpy(pam,"1,");
528 KillSpaces(Command+i+5,pam+2);
529 }
530 else pam[0]=0;
531 ComNum=COM_LOOP;
532 }
533 else if(lstrcmpi(com,"For")==0){
534 for(i2=0,IsStr=0;;i++,i2++){
535 while(Command[i]==' '||Command[i]=='\t') i++;
536 if(Command[i]=='\"') IsStr^=1;
537 if((Command[i-1]==' '||Command[i-1]=='\t')&&(Command[i]=='t'||Command[i]=='T')&&(Command[i+1]=='o'||Command[i+1]=='O')&&(Command[i+2]==' '||Command[i+2]=='\t')&&IsStr==0){
538 pam[i2]=',';
539 break;
540 }
541 pam[i2]=Command[i];
542 if(Command[i]=='\0') break;
543 }
544 if(Command[i]){
545 for(i+=3,i2++,IsStr=0;;i++,i2++){
546 while(Command[i]==' '||Command[i]=='\t') i++;
547 if((Command[i-1]==' '||Command[i-1]=='\t')&&(Command[i]=='s'||Command[i]=='S')&&(Command[i+1]=='t'||Command[i+1]=='T')&&(Command[i+2]=='e'||Command[i+2]=='E')&&(Command[i+3]=='p'||Command[i+3]=='P')&&(Command[i+4]==' '||Command[i+4]=='\t')){
548 pam[i2]=',';
549 i+=4;
550 continue;
551 }
552 pam[i2]=Command[i];
553 if(Command[i]=='\0') break;
554 }
555 }
556 ComNum=COM_FOR;
557 }
558 else if(lstrcmpi(com,"Next")==0){
559 KillSpaces(Command+i,pam);
560 ComNum=COM_NEXT;
561 }
562 else if(lstrcmpi(com,"Return")==0){
563 KillSpaces(Command+i,pam);
564 ComNum=COM_RETURN;
565 }
566 else if(lstrcmpi(com,"While")==0){
567 KillSpaces(Command+i,pam);
568 ComNum=COM_WHILE;
569 }
570 else if(lstrcmpi(com,"Wend")==0){
571 pam[0]=0;
572 ComNum=COM_WEND;
573 }
574
575 //変数、データ操作
576 else if(lstrcmpi(com,"dim")==0){
577 KillSpaces(Command+i,pam);
578 ComNum=COM_DIM;
579 }
580 else if(lstrcmpi(com,"Delete")==0){
581 KillSpaces(Command+i,pam);
582 ComNum=COM_DELETE;
583 }
584
585 //その他
586 else if(lstrcmpi(com,"Debug")==0){
587 pam[0]=0;
588 ComNum=COM_DEBUG;
589 }
590 else if(lstrcmpi(com,"let")==0){
591 KillSpaces(Command+i,pam);
592 ComNum=COM_LET;
593 }
594 else if(lstrcmpi(com,"rem")==0){
595 Command[0]=0;
596 return;
597 }
598
599 //ポインタ
600 else if(lstrcmpi(com,"SetDouble")==0){
601 KillSpaces(Command+i,pam);
602 ComNum=COM_SETDOUBLE;
603 }
604 else if(lstrcmpi(com,"SetSingle")==0){
605 KillSpaces(Command+i,pam);
606 ComNum=COM_SETSINGLE;
607 }
608 else if(lstrcmpi(com,"SetQWord")==0){
609 KillSpaces(Command+i,pam);
610 ComNum=COM_SETQWORD;
611 }
612 else if(lstrcmpi(com,"SetDWord")==0){
613 KillSpaces(Command+i,pam);
614 ComNum=COM_SETDWORD;
615 }
616 else if(lstrcmpi(com,"SetWord")==0){
617 KillSpaces(Command+i,pam);
618 ComNum=COM_SETWORD;
619 }
620 else if(lstrcmpi(com,"SetByte")==0){
621 KillSpaces(Command+i,pam);
622 ComNum=COM_SETBYTE;
623 }
624
625 else{
626 //その他のコマンド(一般コード)
627 lstrcpy(com,Command);
628 KillSpaces(com,Command);
629 return;
630 }
631
632 i=lstrlen(pam);
633 while(pam[i-1]==' '||pam[i-1]=='\t') i--;
634 pam[i]=0;
635 if(pam[0]) sprintf(Command,"%c%c%s",HIBYTE(ComNum),LOBYTE(ComNum),pam);
636 else sprintf(Command,"%c%c",HIBYTE(ComNum),LOBYTE(ComNum));
637
638 return;
639}
640
641void ChangeCommandToCode(char *buffer){
642 extern HANDLE hHeap;
643 int i,i2,i3,IsStr,CommandBufferSize;
644 char *temporary,*tempBase,temp2[VN_SIZE],*lpCommand;
645
646 tempBase=(char *)HeapAlloc(hHeap,0,(lstrlen(buffer)+1)*2+8192);
647 temporary=tempBase+1;
648 temporary[0]=0;
649 i=0;
650 i3=0;
651
652 CommandBufferSize=512;
653 lpCommand=(char *)HeapAlloc(hHeap,0,CommandBufferSize);
654
655 while(1){
656 i2=0;
657 while(buffer[i]==' '||buffer[i]=='\t') i++;
658 while(buffer[i]>='0'&&buffer[i]<='9'){
659 temp2[i2]=buffer[i];
660 i++;
661 i2++;
662 }
663 temp2[i2]=0;
664 while(buffer[i]==' '||buffer[i]=='\t') i++;
665 for(i2=0,IsStr=0;;i++,i2++){
666 if(i2>=CommandBufferSize){ //バッファ領域が足りなくなった場合はバッファを増量する
667 CommandBufferSize+=512;
668 lpCommand=(char *)HeapReAlloc(hHeap,0,lpCommand,CommandBufferSize);
669 }
670 if(buffer[i]=='\"') IsStr^=1;
671 if(buffer[i]=='\n'||(buffer[i]==':'&&IsStr==0)||buffer[i]=='\0'){
672 lpCommand[i2]=0;
673
674 if(temp2[0]){
675 //行番号ラベル
676 sprintf(temporary+i3,"%c%c%s,",1,ESC_LINENUM,temp2);
677 i3+=lstrlen(temporary+i3);
678
679 temp2[0]=0;
680 }
681
682 //命令コードへ変換
683 if(i2){
684 //エラー用
685 extern int cp;
686 cp=i;
687
688 ChangeCommand(buffer,i,lpCommand);
689
690 lstrcpy(temporary+i3,lpCommand);
691 i3+=lstrlen(temporary+i3);
692 }
693
694 if(!(lpCommand[0]=='\0'&&buffer[i]==':')){
695 temporary[i3++]=buffer[i];
696 temporary[i3]=0;
697 }
698
699 if(buffer[i]=='\n'){
700 i++;
701 break;
702 }
703 else if(buffer[i]==':'){
704 while(buffer[i+1]==' '||buffer[i+1]=='\t') i++;
705 }
706 if(buffer[i]=='\0') break;
707 i2=-1;
708 continue;
709 }
710 lpCommand[i2]=buffer[i];
711 }
712 if(buffer[i]=='\0') break;
713 }
714 HeapDefaultFree(lpCommand);
715 lstrcpy(buffer,temporary);
716
717 HeapDefaultFree(tempBase);
718}
Note: See TracBrowser for help on using the repository browser.