(* MPL - MICRO PROGRAMMING LANGUAGE / COPYRIGHT (C) 2017-2018 DEREK JOHN EVANS *)
{$IFDEF FPC}{$MODE TP}{$S-}{$ENDIF}
{$IFDEF HPC}{$M 8,8,8,8}{$F+}{$S-}{$ENDIF}
{$IFNDEF HPC}{$A-}{$ENDIF}
{$D-}{$I-}{$R-}{$V-}
PROGRAM MPL;
{$I MPL.INC}
CONST LIBDIR = {$IFDEF HPC}'M:\MPL\'{$ELSE}''{$ENDIF};
TYPE
TERR = (ESYN, EDUP, ENOT);
TOPT = (ISNO, IS2P, ISDM, ISID, ISSH, ISCP, ISEQ, ISBT, ISLG);
VAR
M: TMEM;
G: PACKED RECORD
E: INT; F: ^TEXT; C: CHAR;
A: PACKED RECORD S: STR127; X: TOPX; T: TOPT; I: INT END;
S: PACKED RECORD S: TSYM; N: INT; T: PACKED ARRAY[BYT] OF INT END;
END;
PROCEDURE WERR(E: TERR); BEGIN G.E := G.E + 1; WRITE('!ERR!: ');
CASE E OF ESYN: WRITE('SYNTAX'); EDUP: WRITE('DUPLICATE');
ENOT: WRITE('NOT FOUND') END; WRITELN('? "', G.A.S, '"')
END;
FUNCTION FIND: PINT; VAR S: TSYM; I: INT; BEGIN FIND := NIL; I := 0;
IF G.A.X = XLDNEG THEN BEGIN
I := G.S.N; S := G.S.S;
WHILE LENGTH(S.P^) <> 0 DO BEGIN
IF S.P^[0] = G.A.S[0] THEN
IF S.P^ = G.A.S THEN BEGIN FIND := PTR(ADDR(G.S.T[I])); EXIT END;
I := I - 1; S.I := S.I + LENGTH(S.P^) + 1
END
END ELSE WERR(ESYN)
END;
FUNCTION INCS: PINT; VAR P: PINT; BEGIN P := FIND; IF P = NIL THEN BEGIN
G.S.S.I := G.S.S.I - LENGTH(G.A.S) - 1; G.S.S.P^ := G.A.S;
G.S.N := G.S.N + 1; INCS := PTR(ADDR(G.S.T[G.S.N]));
END ELSE INCS := P END;
PROCEDURE I1(I: INT); BEGIN M.B[M.N] := I; M.N := M.N + 1 END;
PROCEDURE I2(I: INT); BEGIN MOVE(I, M.B[M.N], 2); M.N := M.N + 2 END;
PROCEDURE OP(X: TOPX); BEGIN M.B[M.N] := ORD(X); M.N := M.N + 1 END;
PROCEDURE O1(X: TOPX; I: INT); BEGIN OP(X); I1(I) END;
PROCEDURE O2(X: TOPX; I: INT); BEGIN OP(X); I2(I) END;
PROCEDURE O12(I1, I2: TOPX; I: INT);
BEGIN IF I = SEX(I) THEN O1(I1, I) ELSE O2(I2, I) END;
PROCEDURE O2A(X: TOPX; I, A: INT); VAR N: INT;
BEGIN N := M.N; M.N := A; O2(X, I); M.N := N END;
PROCEDURE LDI(I: INT); VAR J: INT; BEGIN J := I + 128;
IF (J >= ORD(XZEND)) AND (J <= 255) THEN I1(J) ELSE O12(XIMCHR, XIMINT, I)
END;
PROCEDURE LEA(PC: INT);
BEGIN IF PC >= 0 THEN LDI(PC) ELSE O12(XLEAD1, XLEAD2, -PC) END;
PROCEDURE ARG; VAR S: TSYM; BEGIN S.P := PTR(ADDR(M.B[M.N]));
S.P^ := G.A.S; M.N := M.N + LENGTH(G.A.S) + 1 END;
PROCEDURE RCH; BEGIN IF G.C <> CHR(0) THEN G.C := FRDC(G.F^) END;
FUNCTION ESC: CHAR; BEGIN IF G.C = '*' THEN BEGIN RCH; CASE UPCASE(G.C) OF
'0': G.C := CHR( 0); 'A': G.C := CHR( 7); 'B': G.C := CHR( 8);
'T': G.C := CHR( 9); 'N': G.C := CHR(10); 'V': G.C := CHR(11);
'P': G.C := CHR(12); 'C': G.C := CHR(13); 'S': G.C := CHR(32)
END END; ESC := G.C END;
PROCEDURE T1(T: TOPT; X: TOPX); BEGIN G.A.T := T; G.A.X := X END;
PROCEDURE T2(T: TOPT; X: TOPX); BEGIN G.A.T := T; G.A.X := X; RCH END;
PROCEDURE NEXT; LABEL LOOP; BEGIN LOOP: WHILE G.C IN [CHR(1)..CHR(32)] DO RCH;
T1(ISNO, XABORT); G.C := UPCASE(G.C); G.A.S := G.C;
IF G.C IN ['.', 'A'..'Z'] THEN BEGIN RCH; G.C := UPCASE(G.C);
WHILE G.C IN ['.', 'A'..'Z', '0'..'9'] DO BEGIN
G.A.S := G.A.S + G.C; RCH; G.C := UPCASE(G.C)
END; G.A.X := XLDNEG END
ELSE IF G.C IN ['0'..'9'] THEN BEGIN G.A.I := ORD(G.C) - ORD('0'); RCH;
WHILE G.C IN ['0'..'9'] DO BEGIN G.A.I := G.A.I * 10 + ORD(G.C) - ORD('0');
RCH END; G.A.X := XIMINT END
ELSE BEGIN RCH; CASE G.A.S[1] OF
'''':BEGIN G.A.I := ORD(ESC); RCH; IF G.C = '''' THEN
BEGIN RCH; G.A.X := XIMINT END END;
'"':BEGIN G.A.S[0] := CHR(0);
WHILE NOT (G.C IN [CHR(0), '"']) DO BEGIN G.A.S := G.A.S + ESC; RCH
END; IF G.C = '"' THEN BEGIN G.A.X := XIMSTR; RCH END END;
'/':CASE G.C OF '=': T2(IS2P, X2PDIV);
'/':BEGIN WHILE NOT (G.C IN [CHR(0), CHR(10)]) DO RCH; GOTO LOOP END;
'*':BEGIN RCH; REPEAT WHILE NOT (G.C IN [CHR(0), '*']) DO RCH;
RCH UNTIL G.C IN [CHR(0), '/']; RCH; GOTO LOOP END
ELSE T1(ISDM, XDMDIV) END;
'?':G.A.X:=XIFEQU;'@':G.A.X:=XIFNEQ;';':G.A.X:=XIFNOW;',':G.A.X:=XFNJMP;
'(':G.A.X:=XFNBEG;')':G.A.X:=XFNEND;'[':G.A.X:=XPUSHA;']':G.A.X:=XPULLA;
'$':G.A.X:=XLEAD2;'%':G.A.X:=XLDBYT;
'!':IF G.C='='THEN T2(ISEQ,XEQNOT)ELSE G.A.X := XLDINT;
'^':IF G.C='='THEN T2(IS2P,X2PXOR)ELSE T1(ISBT,XBTXOR);
'*':IF G.C='='THEN T2(IS2P,X2PMUL)ELSE T1(ISDM,XDMMUL);
'#':IF G.C='='THEN T2(IS2P,X2PMOD)ELSE T1(ISDM,XDMMOD);
'=':IF G.C='='THEN T2(ISEQ,XEQYES)ELSE T1(IS2P,X2PSET);
'+':CASE G.C OF'+':T2(ISNO,XAPINC);'=':T2(IS2P,X2PADD);ELSE T1(ISID,XIDADD)END;
'-':CASE G.C OF'-':T2(ISNO,XAPDEC);'=':T2(IS2P,X2PSUB);ELSE T1(ISID,XIDSUB)END;
'<':CASE G.C OF'<':T2(ISSH,XSHSHL);'=':T2(ISCP,XCPLTE);ELSE T1(ISCP,XCPLTH)END;
'>':CASE G.C OF'>':T2(ISSH,XSHSHR);'=':T2(ISCP,XCPGTE);ELSE T1(ISCP,XCPGTH)END;
':':CASE G.C OF':':T2(ISLG,XLGIOR);'=':T2(IS2P,X2PIOR);ELSE T1(ISBT,XBTIOR)END;
'&':CASE G.C OF'&':T2(ISLG,XLGAND);'=':T2(IS2P,X2PAND) ELSE T1(ISBT,XBTAND)END
END
END;
END;
FUNCTION ISX(X: TOPX): BOOLEAN; BEGIN IF G.A.X = X THEN
BEGIN NEXT; ISX := TRUE END ELSE ISX := FALSE END;
FUNCTION IST(T: TOPT; VAR X: TOPX): BOOLEAN; BEGIN X := G.A.X;
IF G.A.T = T THEN BEGIN NEXT; IST := TRUE END ELSE IST := FALSE
END;
FUNCTION SIGN: INT; VAR I: INT; X: TOPX; BEGIN I := 1;
WHILE IST(ISID, X) DO IF X = XIDSUB THEN I := -I; SIGN := I
END;
FUNCTION NUMB: INT; VAR I, J: INT; P: PINT; BEGIN I := 0; J := SIGN;
IF G.A.X = XIMINT THEN BEGIN I := G.A.I; NEXT END
ELSE BEGIN P := FIND; IF P = NIL THEN WERR(ENOT) ELSE BEGIN
IF G.A.S[1] = '.' THEN BEGIN NEXT; I := P^ END
ELSE BEGIN NEXT; IF ISX(XLEAD2) THEN I := P^ ELSE I := M.I[P^ SHR 1]
END END
END; NUMB := I * J
END;
FUNCTION X2X1(X: TOPX): TOPX; BEGIN CASE X OF
X2PADD: X := X1PADD; X2PAND: X := X1PAND; X2PDIV: X := X1PDIV;
X2PIOR: X := X1PIOR; X2PMUL: X := X1PMUL; X2PSET: X := X1PSET;
X2PSHL: X := X1PSHL; X2PSHR: X := X1PSHR; X2PSUB: X := X1PSUB;
X2PXOR: X := X1PXOR; X2PMOD: X := X1PMOD
END; X2X1 := X END;
PROCEDURE EXPR; FORWARD;
PROCEDURE ELEM(OK: BOOLEAN);
VAR I, J: INT; X: TOPX; ISI, IID: BOOLEAN; P: PINT; LABEL LOOP;
BEGIN J := SIGN;
CASE G.A.X OF
X2PSET: BEGIN NEXT; EXPR; OP(XFNEND) END;
XFNBEG: BEGIN NEXT; EXPR; IF NOT ISX(XFNEND) THEN WERR(ESYN) END;
XIFNEQ: BEGIN NEXT; I := M.N; EXPR; O2(XIFNEQ, I) END;
XIMINT: BEGIN LDI(G.A.I * J); J := 1; NEXT END;
XIMSTR: BEGIN OP(XIMSTR); ARG; NEXT END;
XLDBYT, XLDINT: BEGIN ISI := G.A.X = XLDINT; NEXT; ELEM(FALSE);
IF OK AND (G.A.T = IS2P) THEN BEGIN
IF ISI THEN X := G.A.X ELSE X := X2X1(G.A.X);
OP(XPUSHA); NEXT; EXPR; OP(X) END
ELSE IF ISI THEN OP(XLDINT) ELSE OP(XLDBYT) END
ELSE BEGIN X := G.A.X; IID := X IN [XAPINC, XAPDEC]; IF IID THEN NEXT;
P := FIND; IF P = NIL THEN WERR(ENOT) ELSE BEGIN
IF G.A.S[1] = '.' THEN BEGIN IF IID THEN WERR(ESYN); LDI(P^); NEXT; END
ELSE BEGIN LEA(P^); NEXT;
IF IID THEN OP(X) ELSE IF OK AND (G.A.T = IS2P) THEN BEGIN
X := G.A.X; OP(XPUSHA); NEXT; EXPR; OP(X) END
ELSE CASE G.A.X OF
XAPINC: BEGIN OP(XAPINP); NEXT END;
XAPDEC: BEGIN OP(XAPDEP); NEXT END;
XLEAD2: BEGIN NEXT END ELSE OP(XAPGET)
END
END
END
END
END;
LOOP: CASE G.A.X OF
XLDBYT, XLDINT: BEGIN
ISI := G.A.X = XLDINT; NEXT; OP(XPUSHA); ELEM(FALSE);
IF OK AND (G.A.T = IS2P) THEN BEGIN
IF ISI THEN BEGIN X := G.A.X; NEXT; OP(XINCA2) END
ELSE BEGIN X := X2X1(G.A.X); NEXT; OP(XINCA1) END; EXPR; OP(X) END
ELSE IF ISI THEN OP(XPEINT) ELSE OP(XPEBYT);
GOTO LOOP END;
XFNBEG: BEGIN NEXT; OP(XPUSHA); I := 1; IF G.A.X <> XFNEND THEN
REPEAT EXPR; OP(XPUSHA); I := I + 1 UNTIL NOT ISX(XFNJMP);
LDI(I); OP(XFNJMP); IF NOT ISX(XFNEND) THEN WERR(ESYN); GOTO LOOP
END
END; IF J < 0 THEN OP(XLDNEG)
END;
PROCEDURE OPER(T: TOPT); VAR X: TOPX; BEGIN
IF T = IS2P THEN ELEM(TRUE) ELSE BEGIN OPER(PRED(T));
WHILE IST(T, X) DO BEGIN OP(XPUSHA); OPER(PRED(T)); OP(X) END;
END
END;
PROCEDURE EXPR; VAR PEXP, PNOW, PEQU: INT; BEGIN
PEXP := M.N; IF G.A.X = XPUSHA THEN BEGIN
NEXT; REPEAT EXPR UNTIL NOT ISX(XIFNOW); IF NOT ISX(XPULLA) THEN WERR(ESYN)
END ELSE OPER(ISLG);
CASE G.A.X OF
XIFNEQ: BEGIN NEXT; PEQU := M.N; O2(XIFEQU, 0); EXPR;
O2(XIFNOW, PEXP); O2A(XIFEQU, M.N, PEQU) END;
XIFEQU: BEGIN NEXT; PEQU := M.N; O2(XIFEQU, 0); EXPR;
IF G.A.X = XFNJMP THEN BEGIN NEXT; PNOW := M.N; O2(XIFNOW, 0);
O2A(XIFEQU, M.N, PEQU); EXPR; O2A(XIFNOW, M.N, PNOW) END
ELSE O2A(XIFEQU, M.N, PEQU) END
END
END;
PROCEDURE COMP; FORWARD;
PROCEDURE BODY; VAR I, N: INT; S: TSYM; P: PINT; BEGIN
WHILE (G.A.X <> XABORT) AND (G.E < 1) DO BEGIN
IF G.A.X = XIFNOW THEN BEGIN NEXT; COMP; NEXT END
ELSE BEGIN IF ODD(M.N) THEN M.N := M.N + 1;
P := INCS; WRITELN(M.N: 5, ': ', G.A.S); NEXT;
IF P^ <> 0 THEN IF ISX(XLEAD2) THEN M.I[P^ SHR 1] := M.N ELSE WERR(EDUP);
CASE G.A.X OF XBTIOR: BEGIN NEXT; P^ := NUMB END;
X2PSET: BEGIN NEXT; IF P^ = 0 THEN P^ := M.N;
REPEAT
IF G.A.X = XIMSTR THEN BEGIN ARG; NEXT; END
ELSE IF ISX(XLDBYT) THEN M.N := M.N + NUMB
ELSE IF ISX(XLDINT) THEN M.N := M.N + NUMB * 2
ELSE I2(NUMB)
UNTIL NOT ISX(XFNJMP) END;
XFNBEG: BEGIN NEXT; IF P^ = 0 THEN BEGIN P^ := M.N; I2(P^ + 2) END;
S := G.S.S; N := G.S.N; I := 1;
IF G.A.X <> XFNEND THEN REPEAT
REPEAT IF G.A.X <> XIFNOW THEN BEGIN
P := INCS;
IF P^ <> 0 THEN WERR(EDUP); IF G.A.S[1] = '.' THEN WERR(ESYN);
NEXT; P^ := -I; IF ISX(XLDINT) THEN I := I + NUMB
ELSE IF ISX(XLDBYT) THEN I := I + (NUMB + 1) DIV 2
ELSE I := I + 1
END
UNTIL NOT ISX(XFNJMP)
UNTIL NOT ISX(XIFNOW);
IF NOT ISX(XFNEND) THEN WERR(ESYN);
O1(XFNBEG, I); EXPR; OP(XFNEND);
FOR I := N + 1 TO G.S.N DO G.S.T[I] := 0;
G.S.S := S; G.S.N := N
END
END
END
END
END;
PROCEDURE COMP; VAR F: TEXT; TF: ^TEXT; TC: CHAR; P: PINT; BEGIN
G.A.X := XLDNEG; P := INCS;
IF P^ = 0 THEN BEGIN P^ := 1; WRITELN('+FILE: ', G.A.S);
G.A.S := LIBDIR + G.A.S + '.MPL';
IF FOLD(F, G.A.S) <> 0 THEN BEGIN DELETE(G.A.S, 1, LENGTH(LIBDIR));
IF FOLD(F, G.A.S) <> 0 THEN BEGIN WERR(ENOT); EXIT END
END;
TF := G.F; TC := G.C; G.F := PTR(ADDR(F)); G.C := CHR(32); NEXT; BODY;
G.F := TF; G.C := TC; CLOSE(F) (* BUG: MUST REMOVE CLOSE() FOR AMIGA HSPASCAL (?) *)
END
END;
PROCEDURE SAVE; VAR F: FILE; BEGIN
ARGV(G.A.S, 1); ASSIGN(F, G.A.S + '.MPX'); REWRITE(F);
IF IORESULT = 0 THEN BEGIN
BLOCKWRITE(F, M.B, {$IFDEF HPC}M.N{$ELSE}(M.N + 127) div 128{$ENDIF});
CLOSE(F);
END
END;
PROCEDURE MAIN; VAR P: PINT; BEGIN
IF ARGC < 1 THEN WRITELN('USAGE: MPL <FILE>')
ELSE BEGIN FILLCHAR(G, SIZEOF(G), 0);
ARGV(G.A.S, 1);
G.S.S.P := PTR(ADDR(M.B[MEMMAX])); M.N := 4; G.A.X := XLDNEG;
WRITELN; WRITELN('COMPILING'); WRITELN;
COMP; IF G.E = 0 THEN BEGIN G.A.S := 'MAIN'; G.A.X := XLDNEG;
P := FIND; IF P = NIL THEN WERR(ENOT) ELSE BEGIN M.P := M.N;
LEA(P^); OP(XAPGET); OP(XPUSHA); LDI(1); OP(XFNJMP); OP(XABORT)
END
END;
WRITELN; WRITE('COMPILE '); IF G.E <> 0 THEN BEGIN
WRITELN('FAIL'); WRITELN; WRITELN(G.E: 5, ' ERRORS') END
ELSE BEGIN
WRITELN('DONE'); WRITELN;
WRITELN(G.S.N: 5, ' SYMBOLS'); WRITELN(M.N: 5, ' BYTES');
SAVE;
END; WRITELN
END
END;
BEGIN
MAIN; {$IFDEF FPC}READLN;{$ENDIF}
END.