Context compiler design

Some books was written about compiler design and implementation. The following text, as I think, allow to get an elementary belief about the compilers. When I has decide to write a compiler, I wanted to realise immediately quite a few possibilities, noticeably more, than needed for writing the compiler itself. This manage, but my compiler is kept much details, obstruct its understanding. Part of details is connected with possibilities of language, other part - with the processor 8086 (80386 more suitable, but then I did not know this and 32-bit operating systems are not yet spread). And certainly, compiler may be written better. At some moment I has reduce a compiler, but quite small it did not become. Vastly reduce a compiler possible to the account of simplification of input language, that here and is made. The toy compiler is useless for practical programming, but it is build in the same way, either as full compiler...

Program in any high-level language consists of words (reserved words of language, identifiers, numbers, signs) and for the reading them from the text function Scan are used. This function are language-dependent. For some languages such as Pascal it must read only words, numbers and multi-character operators and single characters (in Context language there are three two-character operators - <= - less or equal, != - not equal and >= - greater or equal, all other operators are single-character) and skip a single-line and multi-line comments. Scanner Fortran'ΰ, for instance, must read the words .or. (logical or) and .and. (logical and), scanner of Context language read any of this words as three words. However, tere is not largest problem of Fortran scanner. Following example more complex:

901 FORMAT (1X,'N = ',I2/) N=0 10 DO 20 I=1.5 N=N+1 20 CONTINUE WRITE(6,901) N STOP END

Headline of do cycle in the line 10 consist an error, but Fortran does not find them and assumes that this is assignment of constant 1.5 to variable DO20I (Fortran does not requires explicit variables declaration). The result of this program was 1, not 5.

Not only scanner, a compiler as whole depends on input language, so better go over to private and consider concrete language - Context.

Context program list of followed definitions:

Their mutual location in the program is limited only by one condition - each object must be defined before its use, main function (begin) must be placed at the end of program. For some objects, for instance, functions sufficiently only declarations, but in the minimum version this will not be supported.

Definitions of constant, structures and main function are recognized on the first word (define, struct and begin accordingly), definitions of functions and global variable begins with the type name and friend from the friend are distinguish by presence and absence a parentheses after the name. I.e. after the type name is necessary to read symbols @ (reference signs, if they are present), name itself and following word. If this word is a parenthesis, must be execute analysis to functions, otherwise - an analysis global variable. In other languages a function is defined on the first word. In Pascal, for instance, function begins from function word and variable list begins from the var word.

Thereby, on the top-level a compiler can consist of the simple cycle:

while TRUE do Scan(@Buff); select case strcmp(@Buff,"begin") =0: // Main function parse exit case strcmp(@Buff,"define")=0: // Constant parse case strcmp(@Buff,"struct")=0: // New type parse default: // Global variable and function parse end end

Of course, each of select's branches must contain a certain code. Analysis of constants and structures are relatively simple (more over, in Context structures are nonrecursive - unlike Pascal in there are no structure definitions in other structure definition), result of the analysis are new record in the table of global names, no code will be create. Analysis of functions more complex. It consists of the analysis of heading with the parameter list (that much like like the analysis of structure) and analysis of operators. If we have function Ctrl, which can compile one operator, compilation of whole functions are reduced to the cycle from three lines:

while strcmp(@Scan(@Buff),"end")!=0 do Ctrl(@Buff); end

For further, however, needed to change this cycle and Ctrl function:

//Scan(@Buff); while strcmp(@Buff,"end")!=0 do Ctrl(@Buff); // Ctrl must read next word end

This change will allow execute an analysis of function in extended version of Context, allow function declarations (Pascal analog - forward):

word F(); word G() F(); end

After the close parentheses can will be meet semicolon (F) or beginning of the first operator (G). After reading close parentheses it is necessary to read a following word and, if this is not a semicolon, remember its word and go over to compiling the operators, second version of cycle is adapt for this. By the changing language definition possible to restore capacity to work of first version of cycle:

word F(); word G() is F(); end

Here after the close parentheses always present a word (semicolon or is).

All operators, which can will be meet in functions, will be recognize on the first word, so at the top-level Ctrl can be make so:

void Ctrl(char @Buff) select case strcmp(@Buff,"if")=0 | strcmp(@Buff,"select")=0: // Condition operator parse case strcmp(@Buff,"while")=0: // While loop parse case strcmp(@Buff,"loop")=0: // loop operator parse case strcmp(@Buff,"exit")=0: // exit operator parse case strcmp(@Buff,"inc")=0: // inc operator parse case strcmp(@Buff,"dec")=0: // dec operator parse default: // Local definition and assignment parse end Scan(Buff); end

Many operators contain expressions, their compiling more difficult tasc, but it can be a solved like calculation of expression. If already written function Expr that capable to compile any expression and return type and address of result (this can be an address of memory, register or reference to the element tables of constants).

By using of functions Expr analysis of cycle while can be executed so:

// Code generation @A: nop; begin of loop // Expr call, // loading result in AL // Code generation or AL,AL // jnz @B // jmp ? // @B: nop while strcmp(@Buff,"end")!=0 do Ctrl(@Buff); end // Code generation jmp @A @C: nop // jmp address correction jmp C (for jmp ?)

This is not all necessary actions, also needes prepare to compilation of loop/exit operators and to the local variables analysis. But this is detail, main idea in that cycle, either as a function, includes a sequence of operators and for their compilation can be used function Ctrl in the same way as it used for compilation of the whole function.

This is not a better way. Unnecessary commands are generated (or AL,AL...), but now simplicity more important then efficiency. From this fragment also unclear, how will be compiled complex condition, contain logical operators AND/OR, full or short condition evaluation will be made. This question must be solved in Expr functions. Probably, full evaluation with using a register for keeping of result is the most simple, in some languages (for instance in original Pascal) this method are used. This method also used in DOS version of Context.

Formula compilation ate similar to arithmetic expression calculation. Differences only in that, that in program expressions can contain not only numbers and operation signs, but also identifiers and function calls. And no calculations in compiling time, but computer code are created. All this can be made in one recursive function Expr. This function must calculate an result address or value (only if object - a constant).

void Expr(word Prty; char @Buff; OpInfo @Op1) select case strcmp(@Buff,"(")=0: // Expression in parenthesis case strcmp(@Buff,"'")=0: // Character constant case strcmp(@Buff,"#")=0: // Character constant case isdigit(Buff)=0: // Number constant default: // Variable, function call end while TRUE do word Sign; word P2; select case strcmp(@Buff,"|")=0: Sign=OR; P2 =1; case strcmp(@Buff,"&")=0: Sign=AND; P2 =1; case strcmp(@Buff,"<")=0: Sign=LT; P2 =2; ... case strcmp(@Buff,"%")=0: Sign=MOD; P2 =4; default: P2=0; end if P2<=P then exit end Expr(P2,@Scan(@Buff),@Op2); // Type comparison // Code generation for Sign operator end end

Note that Expr function always read next word. This can be a semicolon, colon, then, do.

All ideas are worded and here is as they are implemented in toy compiler. Simplification is reach to the account of simplification of language in the first place. Only one type given - word (but conditions are considered as boolean), only one-dimensional arrays and no new types, no functions, and no reference variables. No attempts to optimization. Compiler itself begins from Read functions:

define @emNOMEMORY "Not enough memory" define @emSIZE "Identifier too long" define @emEOF "EOF" define @emNUMBER "Eroor in number" define @emNAME "Name expected" define @emEMPTY "Empty array" define @emBRACKET "Bracket expected" define @emCOMMA "Comma expected" define @emSEMICOLON "Semicolon expected" define @emTHENEXP "then keyword expected" define @emDOEXP "do keyword expected" define @emDUP "Duplicate definition" define @emLVALUE "Variable expected" define @emTYPE "Types not match" define @emUNDEFINED "Undefined variable" define @emUNDEFOPR "Undefined operation" define @emASSIGN "Assignment expected" define tbSIZE 8192 define dbSIZE 8192 define dtSIZE 128 define idSIZE 16 define opOR 1 define opAND 2 define opLT 3 define opLE 4 define opEQ 5 define opNE 6 define opGE 7 define opGT 8 define opADD 9 define opSUB 10 define opMUL 11 define opDIV 12 define opNOT 13 define opNEG 14 define ptZERO 0 define ptBOOL 1 define ptCOMP 2 define ptADD 3 define ptMUL 4 define ptLVALUE 5 define ttWORD 0 define ttBOOL 1 struct DATA char Name [idSIZE]; word Ofs; word Index; end DATA Data [dtSIZE]; word nData; word Ofs; char Text [tbSIZE]; word hText; word nText; word pText; char Name [128]; word Line; word Label; char Dest [dbSIZE]; word hDest; word pDest; void @Ptr(word Seg, Ofs) void @P1=@Ofs; void @@P2=@P1; return @P2; end word isalpha(char Ch) if ('A'<=Ch & Ch<='Z') | ('a'<=Ch & Ch<='z') | (Ch='_') then return 0; end return 1; end word isdigit(char Ch) if ('0'<=Ch & Ch<='9') then return 0; end return 1; end word strlen(char @Buff) word P=0; while Buff[P]!=#0 do inc P; end return P; end word strcmp(char @St1, @St2) word P=0; while St1[P]=St2[P] do if St1[P]=#0 then return 0; end inc P; end return 1; end char @strcpy(char @Dst, @Src) word P=0; while Src[P]!=#0 do Dst[P]=Src[P]; inc P; end Dst[P]=#0; return @Dst; end char @strcat(char @Dst, @Src) word P=strlen(@Dst); word Q=0; while Src[Q]!=#0 do Dst[P]=Src[Q]; inc P; inc Q; end Dst[P]=#0; return @Dst; end word GetPSP() asm mov AH,62H asm int 21H asm mov AX,BX end word open(char @Name) asm push DS asm mov AH,3DH asm mov AL,00H asm mov DX,SS:[BP+6] asm mov DS,DX asm mov DX,SS:[BP+4] asm int 21H asm pop DS end word create(char @Name) asm push DS asm mov AH,3CH asm mov CX,00H asm mov DX,SS:[BP+6] asm mov DS,DX asm mov DX,SS:[BP+4] asm int 21H asm pop DS end word read(word F; void @Buff; word N) asm push DS asm mov AH,3FH asm mov BX,SS:[BP+10] asm mov CX,SS:[BP+4] asm mov DX,SS:[BP+8] asm mov DS,DX asm mov DX,SS:[BP+6] asm int 21H asm pop DS end word write(word F; void @Buff; word N) asm push DS asm mov AH,40H asm mov BX,SS:[BP+10] asm mov CX,SS:[BP+4] asm mov DX,SS:[BP+8] asm mov DS,DX asm mov DX,SS:[BP+6] asm int 21H asm pop DS end void close(word F) asm mov AH,3EH asm mov BX,SS:[BP+4] asm int 21H end void putc(char Ch) asm mov AH,2 asm mov DL,SS:[BP+4] asm int 21H end void puts(char @St) word P=0; while St[P]!=#0 do putc(St[P]); inc P; end end word str(word N; word S; char @Buff) if S>0 then dec S; end word P=0; if N>=10 | S>0 then P=str(N/10,S,@Buff); end char @D = "0123456789"; Buff [P]=D[N%10]; return P+1; end char @Str(word N; word S) char @P="00000"; P[str(N,S,@P)]=#0; return @P; end void Stop(char @EM) putc(#13); puts(@Name); putc('('); puts(@Str(Line,0)); putc(')'); if (strlen(@EM)>0) then puts(": "); puts(@EM); end close (hDest); close (hText); asm mov AX,4C00H asm int 21H end word Val(char @Buff) char @D = "0123456789"; word P = 0; word L = 0; word H = 0; while Buff[P]!=#0 do word S=0; while D[S]!=Buff[P] do inc S; if S>=10 then Stop(@emNUMBER); end end S=10*L+S; L=S%256; S=10*H+S/256; H=S%256; S=S/256; if S>0 then Stop(@emNUMBER); end inc P; end return 256*H+L; end char Read() if pText>=nText then nText=read(hText,@Text,tbSIZE); if nText<1 then Stop(@emEOF); end pText=0; end return Text[pText]; end void Next() inc pText; end char @Scan(char @Buff) while Read()=#09 | Read()=#10 | Read()=#13 | Read()=#32 do if Read()=#10 then inc Line; end Next(); end word P=0; while isalpha(Read())=0 | isdigit(Read())=0 do Buff[P]=Read(); inc P; if P>=idSIZE then Stop(@emSIZE); end Next(); end if P=0 then Buff[P]=Read(); inc P; Next(); select case Buff[0]='<': if Read()='=' then Next(); return @strcpy(@Buff,"<="); end case Buff[0]='!': if Read()='=' then Next(); return @strcpy(@Buff,"!="); end case Buff[0]='>': if Read()='=' then Next(); return @strcpy(@Buff,">="); end end end Buff[P]=#0; return @Buff; end void Save(char Ch) if pDest>=dbSIZE then Stop(@emNOMEMORY); end Dest[pDest]=Ch; inc pDest; end void Decl(char @Inst) word I=0; while Inst[I]!=#0 do Save(Inst[I]); inc I; end Save(#13); Save(#10); end void Code(word L; char @Inst) if L!=0 then Save('@'); char @P=@Str(L,5); word I=0; while P[I]!=#0 do Save(P[I]); inc I; end Save(':'); Save(' '); else word I=0; while I<8 do Save(' '); inc I; end end Decl(@Inst); end word Jump(word L) char Buff [28]; word P=pDest; Code(0,@strcat(@strcpy(@Buff,"jmp @"),@Str(L,5))); return P; end word Find(char @Name) word P=0; while P<nData do if strcmp(@Data[P].Name,@Name)=0 then exit end inc P; end return P; end word Comp(char @Jump) char Buff [128]; Code(0, "cmp BX,AX"); Code(0, "mov AL,1"); Code(0, @strcat(@strcat(@strcpy(@Buff,@Jump)," @"),@Str(Label,5))); Code(0, "xor AL,AL"); Code(Label,"nop"); inc Label; return ttBOOL; end word Expr(word Prty; char @Buff) word Type=ttWORD; word Flag=0; word N; select case strcmp(@Buff,"(")=0: Type=Expr(ptZERO,@Scan(@Buff)); if strcmp(@Buff,")")!=0 then Stop(@emBRACKET); end case isdigit(Buff)=0: word C=Val(@Buff); Code(0,@strcat(@strcpy(@Buff,"mov AX,"),@Str(C,0))); default: N=Find(@Buff); if N>=nData then Stop(@emUNDEFINED); end if Data[N].Index>0 then if strcmp(@Scan(@Buff),"[")!=0 then Stop(@emBRACKET); end if Expr(ptZERO,@Scan(@Buff))!=ttWORD then Stop(@emTYPE); end if strcmp(@Buff,"]")!=0 then Stop(@emBRACKET); end if Prty<ptLVALUE then Code(0,"mov BX,AX"); Code(0,"shl BX,1"); Code(0,@strcat(@strcat(@strcpy(@Buff,"mov AX,DS:[BX+"),@Str(Data[N].Ofs,0)),"]")); end else if Prty<ptLVALUE then Code(0,@strcat(@strcat(@strcpy(@Buff,"mov AX,DS:["),@Str(Data[N].Ofs,0)),"]")); end end Flag=1; end Scan(@Buff); if Prty>=ptLVALUE then if Flag=0 then Stop(@emLVALUE); end return N; end while TRUE do word Op; word P; select case strcmp(@Buff,"|")=0: Op=opOR; P =ptBOOL; case strcmp(@Buff,"&")=0: Op=opAND; P =ptBOOL; case strcmp(@Buff,"<")=0: Op=opLT; P =ptCOMP; case strcmp(@Buff,"<=")=0: Op=opLE; P =ptCOMP; case strcmp(@Buff,"=")=0: Op=opEQ; P =ptCOMP; case strcmp(@Buff,"!=")=0: Op=opNE; P =ptCOMP; case strcmp(@Buff,">=")=0: Op=opGE; P =ptCOMP; case strcmp(@Buff,">")=0: Op=opGT; P =ptCOMP; case strcmp(@Buff,"+")=0: Op=opADD; P =ptADD; case strcmp(@Buff,"-")=0: Op=opSUB; P =ptADD; case strcmp(@Buff,"*")=0: Op=opMUL; P =ptMUL; case strcmp(@Buff,"/")=0: Op=opDIV; P =ptMUL; default: P =ptZERO; end if P<=Prty then exit end Code(0,"push AX"); if Expr(P,@Scan(@Buff))!=Type then Stop(@emTYPE); end Code(0,"pop BX"); select case Type=ttBOOL: select case Op=opOR: Code(0,"or AL,BL"); case Op=opAND: Code(0,"and AL,BL"); default: Stop(@emUNDEFOPR); end case Type=ttWORD: select case Op=opLT: Type=Comp("jb "); case Op=opLE: Type=Comp("jbe"); case Op=opEQ: Type=Comp("je "); case Op=opNE: Type=Comp("jne"); case Op=opGE: Type=Comp("jae"); case Op=opGT: Type=Comp("ja "); case Op=opADD: Code(0,"add AX,BX"); case Op=opSUB: Code(0,"db 93H; xchg AX,BX"); Code(0,"sub AX,BX"); case Op=opMUL: Code(0,"mul BX"); case Op=opDIV: Code(0,"db 93H; xchg AX,BX"); Code(0,"xor DX,DX"); Code(0,"div BX"); default: Stop(@emUNDEFOPR); end end end return Type; end void Ctrl(char @Buff) select case strcmp(@Buff,"if")=0: if Expr(ptZERO,@Scan(@Buff))!=ttBOOL then Stop(@emTYPE); end if strcmp(@Buff,"then")!=0 then Stop(@emTHENEXP); end Code(0, "or AL,AL"); Code(0, @strcat(@strcpy(@Buff,"jne @"),@Str(Label,5))); word P=Jump(0); Code(Label,"nop"); inc Label; while strcmp(@Scan(@Buff),"end")!=0 do Ctrl(@Buff); end Code(Label,"nop"); word pDest1=pDest; pDest= P; Jump (Label); pDest=pDest1; inc Label; case strcmp(@Buff,"while")=0: word While=Label; Code(Label,"nop"); inc Label; if Expr(ptZERO,@Scan(@Buff))!=ttBOOL then Stop(@emTYPE); end if strcmp(@Buff,"do")!=0 then Stop(@emDOEXP); end Code(0, "or AL,AL"); Code(0, @strcat(@strcpy(@Buff,"jne @"),@Str(Label,5))); word P=Jump(0); Code(Label,"nop"); inc Label; while strcmp(@Scan(@Buff),"end")!=0 do Ctrl(@Buff); end Code(0, @strcat(@strcpy(@Buff,"jmp @"),@Str(While,5))); Code(Label,"nop"); word pDest1=pDest; pDest= P; Jump (Label); pDest=pDest1; inc Label; case strcmp(@Buff,"write")=0: while TRUE do if Expr(ptZERO,@Scan(@Buff))!=ttWORD then Stop(@emTYPE); end Code(0,"mov CX, 8"); Code(0,"call @WRITE"); if strcmp(@Buff,",")!=0 then exit end end if strcmp(@Buff,";")!=0 then Stop(@emSEMICOLON); end Code(0,"mov AH, 2"); Code(0,"mov DL,13"); Code(0,"int 21H"); Code(0,"mov AH, 2"); Code(0,"mov DL,10"); Code(0,"int 21H"); case strcmp(@Buff,"blank")=0: Code(0,"mov AH, 2"); Code(0,"mov DL,13"); Code(0,"int 21H"); Code(0,"mov AH, 2"); Code(0,"mov DL,10"); Code(0,"int 21H"); default: word N=Expr(ptLVALUE,@Buff); if N>=nData then Stop(@emUNDEFINED); end if strcmp(@Buff,"=")!=0 then Stop(@emASSIGN); end if Data[N].Index>0 then Code(0,"push AX"); end if Expr(ptZERO,@Scan(@Buff))!=ttWORD then Stop(@emTYPE); end if Data[N].Index>0 then Code(0,"pop BX"); Code(0,"shl BX,1"); Code(0,@strcat(@strcat(@strcpy(@Buff,"mov DS:[BX+"),@Str(Data[N].Ofs,0)),"],AX")); else Code(0,@strcat(@strcat(@strcpy(@Buff,"mov DS:["),@Str(Data[N].Ofs,0)),"],AX")); end end end begin byte @Size=@Ptr(GetPSP(),128); char @Parm=@Ptr(GetPSP(),129); char name [128]; word I=0; while I<Size & Parm[I] =' ' do inc I; end word Flag=0; word K =0; word E; while I<Size & Parm[I]!=' ' do name[K]=Parm[I]; if name[K]='.' then E =K; Flag=1; end if name[K]='\' then Flag=0; end inc K; inc I; end name[K]=#00; if K=0 then return end if Flag=0 then strcat(@name,".ctx"); E=K; end I=0; K=0; while name[I]!=#0 do if name[I]=#92 then K=I+1; end inc I; end strcpy(@Name,@name[K]); hText=open(@name); strcpy(@name[E],".asm"); hDest=create(@name); pText=0; nText=0; pDest=0; nData=0; Ofs =0; Label=1; Line =1; Code(0,"mov AX,DS"); Code(0,"add AX,4096"); Code(0,"mov DS,AX"); char Buff [128]; while TRUE do Scan(@Buff); select case strcmp(@Buff,"word")=0: while TRUE do if isalpha(Scan(@Buff))!=0 then Stop(@emNAME); end if Find(@Buff)<nData then Stop(@emDUP); end if nData>=dtSIZE then Stop(@emNOMEMORY); end Data[nData].Ofs =2*Ofs; strcpy(@Data[nData].Name,@Buff); Data[nData].Index=0; word N=1; if strcmp(@Scan(@Buff),"[")=0 then N=Val(@Scan(@Buff)); if N<1 then Stop(@emEMPTY); end Data[nData].Index=N; if strcmp(@Scan(@Buff),"]")!=0 then Stop(@emBRACKET); end Scan(@Buff); end Ofs=Ofs+N; inc nData; if strcmp(@Buff,";")=0 then exit end if strcmp(@Buff,",")!=0 then Stop(@emCOMMA); end end case strcmp(@Buff,"begin")=0: while strcmp(@Scan(@Buff),"end")!=0 do Ctrl(@Buff); end exit default: Stop(@emUNDEFINED); end end Code(0,"mov AX,4C00H"); Code(0,"int 21H"); Decl("@WRITE: dec CX"); Decl(" xor DX,DX"); Decl(" mov BX,10"); Decl(" div BX"); Decl(" push DX"); Decl(" or AX,AX"); Decl(" jz @SPACE"); Decl(" call @WRITE"); Decl(" jmp @DIGIT"); Decl("@SPACE: mov AH, 2"); Decl(" mov DL,32"); Decl(" int 21H"); Decl(" dec CX"); Decl(" jnz @SPACE"); Decl("@DIGIT: mov AH, 2"); Decl(" pop DX"); Decl(" add DL,48"); Decl(" int 21H"); Decl(" retn"); write(hDest,@Dest,pDest); Stop (""); end

I/O facilities include only write operator, that can output numbers on console and blank operator that output blank line. For example you may compile and execute next simple array sort program:

word Buff[16]; word Temp; word I; word J; word N; begin N=10; I=0; while I<N do Buff[I]=N-I; I=I+1; end I=0; while I<N do write I, Buff[I]; I=I+1; end I=N; while I>0 do I=I-1; J=0; while J<I do if Buff[J]>Buff[J+1] then Temp =Buff[J+1]; Buff[J+1]=Buff[J]; Buff[J] =Temp; end J=J+1; end end blank I=0; while I<N do write I, Buff[I]; I=I+1; end end

In Full version of compiler some simplifications are made:

Compiler structure:

void Stop(char @EM) // Terminate (with error msg. or no) ... end void Code(word L; char @S) // Line of asm code ... end char Read() // Read character ... end void Next() // Next character ... end char @Scan(char @Buff) // Read token ... end void Init() // Init expr compiler ... end void Push(word R) // Store register in stack ... end void Pop (word R) // Load register from stack ... end void LDAX(OpInfo @Op1) // Load operand into (DX:)AX ... end void LDBX(OpInfo @Op1) // Load operand into (CX:)BX ... end void LPTR(OpInfo @Op1) // Load pointer into ES:DI ... end void STAX(OpInfo @Op1) // Store result in memory ... end void MOVS(OpInfo @Op1, @Op2) // Large data assignment ... end word Comp(word Size; char @Jump) // Code for comp. operators ... end void Test(word pType1; word nPtr1; OpInfo @Op2) // Tect type ... // compatibility end void Expr(word P; char @Buff; OpInfo @Op1) // Expression ... // compiler end void Ctrl(char @Buff) // Block compiler ... end begin ... while (strcmp(@Scan(@Buff),"begin")!=0) do if (strcmp(@Buff,"define")=0) then // parse const ... loop end if (strcmp(@Buff,"struct")=0) then // parse struct ... loop end P=FindType(@Buff); if (P<nType) then // parse global var ... // or function loop end Stop(@emUNDEFINED); end ... while (strcmp(@Scan(@Buff),"end")!=0) do // Main block Ctrl(@Buff); // compilation end ... end

Some words about compiler modification. New errors may not be shown after first recompilation. I.e. after compiling compiler A' (changed or extended A) by initial compiler A, compiler A' will probably be able to compile itself, but genegated compiler A'' can be wrong.

And last remark. Compiler creates a listings, little different from required for Turbo Assembler. If you want to use TASM/TLINK, you need to add prolog

CODE segment assume CS:CODE org 100H @00001: nop

and epilog

CODE ends end @00001



Π‘Π°ΠΉΡ‚ создан Π² систСмС uCoz