(* MPL - MICRO PROGRAMMING LANGUAGE / COPYRIGHT (C) 2017-2018 DEREK JOHN EVANS *)
PROGRAM MPL;
%C MPLCOM:1
TYPE
TERR = (ESYN, EDUP, ENOT);
TKOP = (K00, KX2, KMD, KAS, KSH, KCP, KEQ, KBT, KLG);
TSYM = PACKED RECORD CASE INTEGER OF 1: (P: PSTR127); 2: (I: PTRINT) END;
VAR
GV: TVEC; GS: TSYM; GN, GI, GE, GP: INTEGER;
GT: PACKED ARRAY[0..511] OF INTEGER;
GF: PTEXT; GC: CHAR; GA: STR127; GX: TXOP; GK: TKOP;
FUNCTION SEXT(A: INTEGER): INTEGER; BEGIN
if (A AND $80) <> 0 then SEXT := A or $FF00 else SEXT := A and $00FF
END;
FUNCTION UPCASE(C: CHAR): CHAR; BEGIN
IF C IN [CHR(97)..CHR(122)] THEN UPCASE := CHR(ORD(C) - 32) ELSE UPCASE := C;
END;
FUNCTION LOWERCASE(C: CHAR): CHAR; BEGIN
IF C IN [CHR(65)..CHR(90)] THEN LOWERCASE := CHR(ORD(C) + 32) ELSE LOWERCASE := C;
END;
FUNCTION FOLD(VAR F: TEXT; VAR FN: STR127): INTEGER;
BEGIN RESET(F, FN); FOLD := FILEERROR(F) END;
FUNCTION FNEW(VAR F: TEXT; VAR FN: STR127): INTEGER; BEGIN
REWRITE(F, FN); FNEW := FILEERROR(F)
END;
FUNCTION FEND(VAR F: TEXT): INTEGER;
BEGIN CLOSE(F); FEND := FILEERROR(F) END;
FUNCTION FRDC(VAR F: TEXT): CHAR; VAR C: CHAR; BEGIN
IF EOF(F) THEN C := CHR(0)
ELSE BEGIN IF EOLN(F) THEN BEGIN READLN(F); C := CHR(10) END ELSE READ(F, C);
IF FILEERROR(F) <> 0 THEN C := CHR(0)
END; FRDC := C
END;
FUNCTION FWRC(VAR F: TEXT; C: CHAR): CHAR; BEGIN
IF C = CHR(10) THEN WRITELN(F) ELSE WRITE(F, C);
IF FILEERROR(F) <> 0 THEN C := CHR(0); FWRC := C
END;
FUNCTION FRDS(VAR F: TEXT; VAR S: STR127): INTEGER;
BEGIN READLN(F, S); FRDS := FILEERROR(F) END;
FUNCTION FWRS(VAR F: TEXT; VAR S: STR127): INTEGER;
BEGIN WRITE(F, S); FWRS := FILEERROR(F) END;
PROCEDURE RCH; BEGIN IF GC <> CHR(0) THEN GC := FRDC(GF^) END;
FUNCTION ESC: CHAR; BEGIN IF GC = '*' THEN BEGIN RCH; CASE UPCASE(GC) OF
'0':GC:=CHR( 0);'A':GC:=CHR( 7);'B':GC:=CHR( 8);'T':GC:=CHR( 9);
'N':GC:=CHR(10);'V':GC:=CHR(11);'P':GC:=CHR(12);'F':GC:=CHR(12);
'C':GC:=CHR(13);'S':GC:=CHR(32) END END; ESC := GC
END;
PROCEDURE K1(K: TKOP; X: TXOP); BEGIN GK := K; GX := X END;
PROCEDURE K2(K: TKOP; X: TXOP); BEGIN GK := K; GX := X; RCH END;
PROCEDURE NEXT; VAR I: INTEGER; LABEL 0; BEGIN
0:K1(K00, XABORT);WHILE GC IN [CHR(1)..CHR(32)] DO RCH;
GC := UPCASE(GC); GA := GC; IF GC IN ['.', 'A'..'Z'] THEN BEGIN
RCH; GC := UPCASE(GC); WHILE GC IN ['.', 'A'..'Z', '0'..'9'] DO
BEGIN GA := GA + GC; RCH; GC := UPCASE(GC) END; GX := XLDNEG END
ELSE IF GC IN ['0'..'9'] THEN BEGIN RCH; WHILE GC IN ['0'..'9'] DO
BEGIN GA := GA + GC; RCH END; GI:=ENCODE(GA); GX := XIMINT END
ELSE BEGIN RCH; CASE GA[1] OF
'''':BEGIN GI := ORD(ESC); RCH; IF GC = '''' THEN BEGIN RCH; GX := XIMINT END END;
'"':BEGIN GA[0] := CHR(0); WHILE NOT (GC IN [CHR(0), '"']) DO BEGIN
GA := GA + ESC; RCH END; IF GC = '"' THEN BEGIN GX := XIMSTR; RCH END END;
'/':CASE GC OF '/':BEGIN REPEAT RCH UNTIL GC IN [CHR(0),CHR(10)]; GOTO 0 END;
'=':K2(KX2, X2PDIV);
'*':BEGIN RCH; REPEAT WHILE NOT (GC IN [CHR(0), '*']) DO RCH; RCH
UNTIL GC IN [CHR(0), '/']; RCH; GOTO 0 END ELSE K1(KMD, XMDDIV) END;
'?':GX:=XJMPEQ;'@':GX:=XJMPNE;';':GX:=XJMPTO;',':GX:=XFNJMP;'(':GX:=XFNBEG;
')':GX:=XFNEND;'[':GX:=XPUSHA;']':GX:=XPULLA;'$':GX:=XLEAD1;'%':GX:=XLDBYT;
'!':GX:=XLDINT;'=':IF GC='='THEN K2(KEQ,XEQEQU)ELSE K1(KX2,X2PSET);
'#':CASE GC OF'#':K2(K00,XAPAI1);'=':K2(KX2,X2PMOD)ELSE K1(KMD,XMDMOD)END;
'&':CASE GC OF'&':K2(KLG,XLGAND);'=':K2(KX2,X2PAND)ELSE K1(KBT,XBTAND)END;
'*':CASE GC OF'*':K2(K00,XAPAD1);'=':K2(KX2,X2PMUL)ELSE K1(KMD,XMDMUL)END;
'+':CASE GC OF'+':K2(K00,XAPI1A);'=':K2(KX2,X2PADD)ELSE K1(KAS,XASADD)END;
'-':CASE GC OF'-':K2(K00,XAPD1A);'=':K2(KX2,X2PSUB)ELSE K1(KAS,XASSUB)END;
':':CASE GC OF':':K2(KLG,XLGIOR);'=':K2(KX2,X2PIOR)ELSE K1(KBT,XBTIOR)END;
'>':CASE GC OF'>':K2(KSH,XSHASR);'=':K2(KCP,XCPGTE)ELSE K1(KCP,XCPGTH)END;
'^':CASE GC OF'^':K2(K00,XLDINV);'=':K2(KX2,X2PXOR)ELSE K1(KBT,XBTXOR)END;
'<':CASE GC OF'<':K2(KSH,XSHASL);'=':K2(KCP,XCPLTE);'>':K2(KEQ,XEQNEQ)
ELSE K1(KCP,XCPLTH) END END
END END;
PROCEDURE ERR(E: TERR); BEGIN GE := GE + 1; WRITELN; WRITE(' --> ');
CASE E OF ESYN: WRITE('SYNTAX'); EDUP: WRITE('DUPLICATE');
ENOT: WRITE('NOT FOUND') END; WRITE('? "', GA, '"')
END;
FUNCTION LOC(VAR IP: PINTEGER): BOOLEAN; VAR I: INTEGER; S: TSYM; BEGIN
LOC := FALSE; IF GX = XLDNEG THEN BEGIN I := GN; S := GS;
WHILE I > 0 DO BEGIN I := I - 1;
IF S.P^ = GA THEN BEGIN IP := PTR(@GT[I]); LOC := TRUE; EXIT END;
S.I := S.I + ORD(S.P^[0]) + 1 END
END ELSE ERR(ESYN) END;
PROCEDURE ADD(VAR IP: PINTEGER); BEGIN GS.I := GS.I - ORD(GA[0]) - 1;
GS.P^ := GA; GT[GN] := 0; IP := PTR(@GT[GN]); GN := GN + 1
END;
FUNCTION ISX(X: TXOP): BOOLEAN;
BEGIN IF GX = X THEN BEGIN NEXT; ISX := TRUE END ELSE ISX := FALSE END;
FUNCTION ISK(K: TKOP; VAR X: TXOP): BOOLEAN;
BEGIN X := GX; IF GK = K THEN BEGIN NEXT; ISK := TRUE END ELSE ISK := FALSE END;
FUNCTION SGN: INTEGER; VAR I: INTEGER; X: TXOP;
BEGIN I := 1; WHILE ISK(KAS, X) DO IF X = XASSUB THEN I := -I; SGN := I END;
FUNCTION NUM: INTEGER; VAR I, J: INTEGER; IP: PINTEGER; BEGIN
I := 0; J := SGN; IF GX = XIMINT THEN BEGIN I := GI; NEXT END
ELSE IF LOC(IP) THEN BEGIN IF GA[1] = '.' THEN BEGIN I := IP^; NEXT END
ELSE BEGIN NEXT; IF ISX(XLEAD1) THEN I := IP^ ELSE I := GV.I[IP^ LSR 1] END
END ELSE ERR(ENOT); NUM := I * J END;
PROCEDURE I2(I: INTEGER); VAR IP: PINTEGER; BEGIN
IP := PTR(@GV.C[GP]); IP^ := I;
GP := GP + 2
END;
PROCEDURE X0(X: TXOP); BEGIN GV.C[GP] := CHR(ORD(X)); GP := GP + 1 END;
PROCEDURE IFX0(A: BOOLEAN; T, F: TXOP); BEGIN
IF A THEN GV.C[GP] := CHR(ORD(T)) ELSE GV.C[GP] := CHR(ORD(F)); GP := GP + 1
END;
PROCEDURE X1(X: TXOP; I: INTEGER);
BEGIN GV.C[GP] := CHR(ORD(X)); GV.C[GP + 1] := CHR(I); GP := GP + 2 END;
PROCEDURE X2(X: TXOP; I: INTEGER); BEGIN X0(X); I2(I) END;
PROCEDURE X12(A, B: TXOP; I: INTEGER);
BEGIN IF I = SEXT(I) THEN X1(A, I) ELSE X2(B, I) END;
PROCEDURE X2A(A: INTEGER; X: TXOP; I: INTEGER); VAR LP: INTEGER;
BEGIN LP := GP; GP := A; X2(X, I); GP := LP END;
PROCEDURE LDI(I: INTEGER); BEGIN
IF (I < (ORD(XZZEND) - 128)) OR (I >= 128) THEN X12(XIMCHR, XIMINT, I)
ELSE BEGIN GV.C[GP] := CHR(I + 128); GP := GP + 1 END
END;
PROCEDURE STS; VAR S: TSYM;
BEGIN S.P := PTR(@GV.C[GP]); S.P^ := GA; GP := GP + ORD(GA[0]) + 1 END;
FUNCTION X2X1(X: TXOP): TXOP; BEGIN CASE X OF
X2PADD:X:=X1PADD;X2PAND:X:=X1PAND;X2PDIV:X:=X1PDIV;X2PIOR:X:=X1PIOR;
X2PMUL:X:=X1PMUL;X2PSET:X:=X1PSET;X2PASL:X:=X1PASL;X2PASR:X:=X1PASR;
X2PSUB:X:=X1PSUB;X2PXOR:X:=X1PXOR;X2PMOD:X:=X1PMOD END; X2X1 := X
END;
PROCEDURE EXPR; FORWARD;
PROCEDURE ELEM(K: TKOP; ASINT: BOOLEAN);
VAR I, J: INTEGER; X: TXOP; ISID: BOOLEAN; IP: PINTEGER; LABEL 0;
BEGIN
IF K > KX2 THEN BEGIN ELEM(PRED(K), FALSE);
WHILE ISK(K, X) DO BEGIN X0(XPUSHA); ELEM(PRED(K), FALSE); X0(X) END END
ELSE BEGIN J := SGN; CASE GX OF
X2PSET: BEGIN NEXT; EXPR; X0(XFNEND) END;
XFNBEG: BEGIN NEXT; EXPR; IF NOT ISX(XFNEND) THEN ERR(ESYN) END;
XJMPNE: BEGIN I := GP; NEXT; EXPR; X2(XJMPNE, I) END;
XIMINT: BEGIN LDI(GI * J); J := 1; NEXT END;
XIMSTR: BEGIN X0(XIMSTR); STS; NEXT END;
XLDBYT, XLDINT: BEGIN ASINT := GX = XLDINT; NEXT; ELEM(K00, ASINT);
IF (K = KX2) AND (GK = KX2) THEN BEGIN X0(XPUSHA);
X := GX; NEXT; EXPR; IF ASINT THEN X0(X) ELSE X0(X2X1(X)) END
ELSE IFX0(ASINT, XLDINT, XLDBYT) END
ELSE BEGIN X := GX; IF (GK = KEQ) OR (GK = KCP) OR (X = XLDINV) THEN
BEGIN NEXT; ELEM(K00, FALSE); CASE X OF
XEQEQU:X:=XLDEQU;XEQNEQ:X:=XLDNEQ;XCPGTH:X:=XLDGTH;XCPGTE:X:=XLDGTE;
XCPLTH:X:=XLDLTH;XCPLTE:X:=XLDLTE END; X0(X) END
ELSE BEGIN ISID := X IN [XAPI1A, XAPD1A]; IF ISID THEN NEXT;
IF NOT LOC(IP) THEN ERR(ENOT) ELSE BEGIN IF GA[1] = '.' THEN BEGIN
IF ISID THEN ERR(ESYN); LDI(IP^); NEXT END
ELSE BEGIN
IF IP^ < 0 THEN X12(XLEAD1, XLEAD2, IP^) ELSE LDI(IP^); NEXT;
IF ISID THEN CASE X OF
XAPI1A: IFX0(ASINT, XAPI2A, XAPI1A);
XAPD1A: IFX0(ASINT, XAPD2A, XAPD1A) END
ELSE IF (K = KX2) AND (GK = KX2) THEN BEGIN
X0(XPUSHA); X := GX; NEXT; EXPR; X0(X) END
ELSE CASE GX OF XLEAD1: NEXT;
XAPI1A: BEGIN IFX0(ASINT, XAPAI2, XAPAI1); NEXT END;
XAPD1A: BEGIN IFX0(ASINT, XAPAD2, XAPAD1); NEXT END
ELSE X0(XAPLDA) END
END END END END END;
0: CASE GX OF
XLDBYT, XLDINT: BEGIN
X0(XPUSHA); ASINT := GX = XLDINT; NEXT; ELEM(K00, FALSE);
IF (K = KX2) AND (GK = KX2) THEN BEGIN IFX0(ASINT, XINCA2, XINCA1);
X := GX; NEXT; EXPR; IF ASINT THEN X0(X) ELSE X0(X2X1(X)) END
ELSE IFX0(ASINT, XPEINT, XPEBYT); GOTO 0 END;
XFNBEG: BEGIN X0(XPUSHA); NEXT; I := 1; IF GX <> XFNEND THEN
REPEAT EXPR; X0(XPUSHA); I := I + 1 UNTIL NOT ISX(XFNJMP);
LDI(I); X0(XFNJMP); IF ISX(XFNEND) THEN GOTO 0 ELSE ERR(ESYN) END
END;
IF J < 0 THEN X0(XLDNEG)
END END;
PROCEDURE EXPR; VAR EX, JP, EQ: INTEGER; X: TXOP; BEGIN EX := GP;
IF GX = XPUSHA THEN BEGIN NEXT; REPEAT EXPR UNTIL NOT ISX(XJMPTO);
IF NOT ISX(XPULLA) THEN ERR(ESYN) END ELSE ELEM(KLG, FALSE);
IF GX IN [XJMPNE, XJMPEQ] THEN BEGIN X := GX; EQ := GP; X2(XJMPEQ, 0);
NEXT; EXPR; CASE X OF
XJMPNE: BEGIN X2(XJMPTO, EX); X2A(EQ, XJMPEQ, GP) END;
XJMPEQ: IF GX = XFNJMP THEN BEGIN JP := GP; X2(XJMPTO, 0);
X2A(EQ, XJMPEQ, GP); NEXT; EXPR; X2A(JP, XJMPTO, GP) END
ELSE X2A(EQ, XJMPEQ, GP) END
END END;
PROCEDURE INCL; FORWARD;
PROCEDURE BODY;
VAR ISAD1, ISNEW, ISVAR: BOOLEAN; IP: PINTEGER; I, LN: INTEGER; LS: TSYM;
BEGIN WHILE NOT(GX IN [XABORT, XPULLA]) AND (GE < 1) DO CASE GX OF
XAPAI1: BEGIN NEXT; INCL; NEXT END;
XPUSHA: BEGIN LS := GS; LN := GN; NEXT; BODY; GS := LS; GN := LN;
IF NOT ISX(XPULLA) THEN ERR(ESYN) END
ELSE BEGIN GP := (GP + 1) AND $FFFE; ISAD1 := ISX(XAPAD1);
IF ISAD1 THEN BEGIN ISNEW := FALSE; ADD(IP); IP^ := GP END
ELSE ISNEW := NOT LOC(IP); ISVAR := GA[1] <> '.';
WRITELN(GP: 5, ': ', GA);
IF ISNEW THEN BEGIN ADD(IP); IP^ := GP; NEXT END ELSE BEGIN NEXT;
IF ISVAR THEN IF ISX(XLEAD1) THEN BEGIN
IF GV.I[IP^ LSR 1] = 0 THEN GV.I[IP^ LSR 1] := GP ELSE ERR(EDUP) END
ELSE IF NOT ISAD1 THEN ERR(EDUP)
END; IF GE = 0 THEN CASE GX OF
XBTIOR: BEGIN NEXT; IP^ := NUM END;
X2PSET: BEGIN NEXT; REPEAT IF GX = XIMSTR THEN BEGIN STS; NEXT END
ELSE IF ISX(XLDBYT) THEN GP := GP + NUM
ELSE IF ISX(XLDINT) THEN GP := GP + NUM * 2
ELSE I2(NUM) UNTIL NOT ISX(XFNJMP) END;
XFNBEG: BEGIN IF (ISNEW OR ISAD1) AND ISVAR THEN I2(GP + 2);
NEXT; LS := GS; LN := GN; I := 1; IF GX <> XFNEND THEN
REPEAT REPEAT IF GX <> XJMPTO THEN IF GA[1] = '.' THEN ERR(ESYN)
ELSE BEGIN ADD(IP); NEXT; IP^ := -I;
IF ISX(XLDBYT) THEN I := I + (NUM + 1) DIV 2
ELSE IF ISX(XLDINT) THEN I := I + NUM ELSE I := I + 1
END UNTIL NOT ISX(XFNJMP) UNTIL NOT ISX(XJMPTO);
IF NOT ISX(XFNEND) THEN ERR(ESYN);
X1(XFNBEG, I); EXPR; X0(XFNEND); GS := LS; GN := LN END
ELSE IF ISVAR THEN I2(0)
END END END END;
FUNCTION OPEN(VAR F: TEXT): BOOLEAN; VAR FN: STR127; BEGIN
FN := GA + MPLFEXT; OPEN := FOLD(F, FN) = 0
END;
PROCEDURE INCL; VAR F: TEXT; LF: PTEXT; LC: CHAR; IP: PINTEGER;
BEGIN GX := XLDNEG; IF NOT LOC(IP) THEN BEGIN ADD(IP);
IF NOT OPEN(F) THEN ERR(ENOT) ELSE BEGIN
LF := GF; LC := GC; GF := PTR(@F); GC := CHR(32);
NEXT; BODY; GF := LF; GC := LC; CLOSE(F) END
END END;
PROCEDURE SAVE(FN: STR127);
VAR
F: FILE OF TBLK;
I: INTEGER;
BEGIN
FN := FN + MPXFEXT;
REWRITE(F, FN);
IF FILEERROR(F) = 0 THEN BEGIN
FOR I := 0 TO (GV.I[0] + SIZEOF(TBLK) - 1) DIV SIZEOF(TBLK) DO
BEGIN WRITE(F, GV.B[I]) END;
CLOSE(F) END
END;
PROCEDURE MAIN; VAR IP: PINTEGER; FN: STRING(10); BEGIN
WRITE('MPL FILE: '); READLN(FN);
IF ORD(FN[0]) <> 0 THEN BEGIN
GA := FN;
GS.P := PTR(@GV.I[MPLIMAX]);
GE := 0; GN := 0; GP := MPLCODE * 2; GX := XLDNEG;
WRITELN; WRITELN('COMPILING'); WRITELN;
INCL; IF GE = 0 THEN BEGIN GA := '.'; GX := XLDNEG;
IF LOC(IP) THEN BEGIN
GV.I[MPLBOOT] := GP; LDI(IP^); X0(XPUSHA); LDI(1); X0(XFNJMP);
X0(XABORT); GV.I[MPLSIZE] := GP END ELSE ERR(ENOT)
END;
WRITELN; WRITELN; IF GE <> 0 THEN BEGIN
WRITELN('FAILED'); WRITELN; WRITELN(GE: 5, ' ERRORS') END
ELSE BEGIN WRITELN('COMPLETE'); WRITELN;
WRITELN(GN: 5, ' SYMBOLS'); WRITELN(GP: 5, ' BYTES');
SAVE(FN) END; WRITELN END
END;
BEGIN
MAIN;
END.