(* MPL - MICRO PROGRAMMING LANGUAGE / COPYRIGHT (C) 2017-2019 DEREK JOHN EVANS *)
(* NOTE: CP/M END ADDRESS = E000 *)
{$D-}{$I-}{$R-}{$V-}
{$IFDEF TPC}{$A-}{$ENDIF}
{$IFDEF FPC}{$MODE TP}{$S-}{$ENDIF}
{$IFDEF HPC}{$M 8,4,4,4}{$F-}{$S-}{$ENDIF}
PROGRAM MPL;
{$IFDEF TPC}TYPE TADR=INTEGER;{$ELSE}USES DOS; TYPE TADR=LONGINT;
FUNCTION ADDR(VAR V):TADR; BEGIN ADDR:=TADR(@V) END;{$ENDIF}
CONST H80=128; TOM=12287; (* 12287=24K 16382=~32K *)
TYPE
TCHR=PACKED RECORD VAL:CHAR END;
TFLG=BOOLEAN; TINT=INTEGER; TSTR=STRING[80];
PINT=RECORD CASE TINT OF 0:(ADR:TADR);1:(PTR:^TINT) END;
PSTR=RECORD CASE TINT OF 0:(ADR:TADR);1:(PTR:^TSTR) END;
PEXT=RECORD CASE TINT OF 0:(ADR:TADR);1:(PTR:^TEXT) END;
TERR=(ESYN,EDUP,EMIS); TLEV=(L0,L1,L2,L3,L4,L5,L6,L7,L8);
TOPR=(
OABRT,OCASE,OENTR,OEXIT,OIDX1,OIDX2,OJPEQ,OJPNE,OJSUB,OJUMP,OLEAD,OLOAD,
OAD1A,OAD2A,OADA1,OADA2,OADD1,OADD2,OADD3,OADD4,OBAN1,OBAN2,OBAN3,OBAN4,
OBOR1,OBOR2,OBOR3,OBOR4,OBOX1,OBOX2,OBOX3,OBOX4,ODIV1,ODIV2,ODIV3,ODIV4,
OEQV1,OEQV2,OEQV3,OEQV4,OLD11,OLD12,OLD21,OLD22,OLIT1,OLIT2,OLIT3,OLIT4,
OMOD1,OMOD2,OMOD3,OMOD4,OMUL1,OMUL2,OMUL3,OMUL4,OSET1,OSET2,OSET3,OSET4,
OSHL1,OSHL2,OSHL3,OSHL4,OSHR1,OSHR2,OSHR3,OSHR4,OSU1A,OSU2A,OSUA1,OSUA2,
OSUB1,OSUB2,OSUB3,OSUB4,OTAN1,OTAN2,OTEQ1,OTEQ2,OTGE1,OTGE2,OTGT1,OTGT2,
OTLE1,OTLE2,OTLT1,OTLT2,OTNE1,OTNE2,OTOR1,OTOR2,OTOX1,OTOX2,OFRE1,OFRE2,
OOO96);
VAR
GM:PACKED RECORD CASE TINT OF
0:(V:PACKED ARRAY[(VNIL,VTOM,VAUX,VTMP,VPRG)] OF TINT);
1:(I:PACKED ARRAY[0..TOM] OF TINT);
2:(C:PACKED ARRAY[0..0] OF TCHR) END;
GI,GE,GMZ,GSZ:TINT; GC:CHAR; GS:TSTR; GL:TLEV; GO:TOPR;
GPF:PEXT; GPS:PSTR; GPI: PINT; GV:PACKED ARRAY[0..511] OF TINT;
PROCEDURE WC(C:CHAR); BEGIN GM.C[GMZ].VAL:=C; GMZ:=SUCC(GMZ) END;
PROCEDURE WI(I:TINT); BEGIN MOVE(I,GM.C[GMZ],2); GMZ:=GMZ+2 END;
PROCEDURE WS; VAR PS:PSTR; BEGIN
PS.ADR:=ADDR(GM.C[GMZ]); PS.PTR^:=GS; GMZ:=SUCC(GMZ+LENGTH(GS)) END;
PROCEDURE WO(O:TOPR); BEGIN GM.C[GMZ].VAL:=CHR(ORD(O)); GMZ:=SUCC(GMZ) END;
PROCEDURE OB(O:TOPR;B:TFLG); BEGIN IF B THEN WO(PRED(O)) ELSE WO(O) END;
PROCEDURE OC(O:TOPR;C:CHAR); BEGIN WO(O); WC(C) END;
PROCEDURE OI(O:TOPR;I:TINT); BEGIN WO(O); WI(I) END;
PROCEDURE OCI(O:TOPR;CI:TINT); BEGIN
IF(CI>=-H80)AND(CI<H80)THEN OC(PRED(O),CHR(CI)) ELSE OI(O,CI) END;
PROCEDURE OJP(O:TOPR;I:TINT); VAR MZ:TINT; BEGIN
MZ:=GMZ; GMZ:=I; OI(O,MZ); GMZ:=MZ END;
PROCEDURE OLI(I:TINT); BEGIN
IF(I<(ORD(OOO96)-H80))OR(I>=H80)THEN OCI(OLIT2,I) ELSE WC(CHR(I+H80)) END;
PROCEDURE FAIL(E:TERR); BEGIN GE:=SUCC(GE); WRITELN; WRITE(' --> '); CASE E OF
ESYN:WRITE('SYNTAX'); EDUP:WRITE('DUPLICATE'); EMIS:WRITE('MISSING') END;
WRITE('? @ "',GS,'"') END;
FUNCTION OPEN:TFLG; BEGIN OPEN:=TRUE; ASSIGN(GPF.PTR^,{$IFDEF HPC}'M:\MPL\'+{$ENDIF}GS);
RESET(GPF.PTR^); IF IORESULT<>0 THEN BEGIN ASSIGN(GPF.PTR^,GS); RESET(GPF.PTR^);
OPEN:=IORESULT=0 END END;
PROCEDURE SHUT; BEGIN CLOSE(GPF.PTR^); IF GO<>OABRT THEN FAIL(ESYN) END;
PROCEDURE RDCH; BEGIN GC:=CHR(0); IF NOT EOF(GPF.PTR^) THEN READ(GPF.PTR^,GC) END;
PROCEDURE ARGV(VAR S:TSTR; I:TINT); BEGIN S:=PARAMSTR(I);
FOR I:=1 TO LENGTH(S) DO S[I]:=UPCASE(S[I]) END;
FUNCTION META:CHAR; BEGIN IF GC='*' THEN BEGIN RDCH; CASE UPCASE(GC) OF
'0':GC:=CHR(00);'A':GC:=CHR(07);'B':GC:=CHR(08);'T':GC:=CHR(09);'N':GC:=CHR(10);
'V':GC:=CHR(11);'P':GC:=CHR(12);'F':GC:=CHR(12);'R':GC:=CHR(13);'C':GC:=CHR(13);
'E':GC:=CHR(27);'S':GC:=CHR(32) END END; META:=GC END;
PROCEDURE LO1(L:TLEV;O:TOPR); BEGIN GL:=L; GO:=O END;
PROCEDURE LO2(L:TLEV;O:TOPR); BEGIN GL:=L; GO:=O; RDCH END;
PROCEDURE NEXT; LABEL 0; BEGIN 0:LO1(L1,OABRT);
WHILE GC IN [CHR(1)..CHR(32)] DO RDCH; GC:=UPCASE(GC); GS:=GC;
IF GC IN ['.','A'..'Z'] THEN BEGIN
RDCH; GC:=UPCASE(GC); WHILE GC IN ['.','A'..'Z','0'..'9'] DO
BEGIN GS:=GS+GC; RDCH; GC:=UPCASE(GC) END; GO:=OLIT1 END
ELSE IF GC IN ['0'..'9'] THEN BEGIN GI:=ORD(GC)-ORD('0'); RDCH;
WHILE GC IN ['0'..'9'] DO BEGIN GI:=GI*10+ORD(GC)-ORD('0');
GS:=GS+GC; RDCH END; GO:=OLIT2 END ELSE BEGIN RDCH; CASE GS[1] OF
'''':BEGIN GI:=ORD(META); RDCH; IF GC='''' THEN LO2(L1,OLIT2) END;
'"':BEGIN GS:=''; WHILE NOT(GC IN [CHR(0),'"']) DO BEGIN
GS:=GS+META; RDCH END; IF GC='"' THEN LO2(L1,OLIT3) END;
'/':CASE GC OF'=':LO2(L2,ODIV4);
'*':BEGIN RDCH; REPEAT WHILE NOT(GC IN [CHR(0),'*'])DO RDCH;
RDCH UNTIL GC IN [CHR(0),'/']; RDCH; GOTO 0 END;
'/':BEGIN READLN(GPF.PTR^,GS); RDCH; GOTO 0 END ELSE LO1(L3,ODIV2)END;
'(':GO:=OSET1;')':GO:=OSET2;'[':GO:=OENTR;']':GO:=OEXIT;',':GO:=OIDX1;
'%':GO:=OLD11;'!':GO:=OLD12;'@':GO:=OJUMP;'?':GO:=OCASE;';':GO:=OIDX2;
':':IF GC=':'THEN LO2(L1,OJSUB)ELSE GO:=OJPNE;'$':LO1(L2,OLEAD);
'*':IF GC='='THEN LO2(L2,OMUL4)ELSE LO1(L3,OMUL2);
'=':IF GC='='THEN LO2(L6,OTEQ2)ELSE LO1(L2,OSET4);
'\':IF GC='='THEN LO2(L2,OMOD4)ELSE LO1(L3,OMOD2);
'#':CASE GC OF'#':LO2(L1,OLOAD);'=':LO2(L2,OEQV4)ELSE LO1(L7,OEQV2)END;
'&':CASE GC OF'&':LO2(L8,OTAN2);'=':LO2(L2,OBAN4)ELSE LO1(L7,OBAN2)END;
'+':CASE GC OF'+':LO2(L1,OAD2A);'=':LO2(L2,OADD4)ELSE LO1(L4,OADD2)END;
'-':CASE GC OF'-':LO2(L1,OSU2A);'=':LO2(L2,OSUB4)ELSE LO1(L4,OSUB2)END;
'^':CASE GC OF'^':LO2(L8,OTOX2);'=':LO2(L2,OBOX4)ELSE LO1(L7,OBOX2)END;
'|':CASE GC OF'|':LO2(L8,OTOR2);'=':LO2(L2,OBOR4)ELSE LO1(L7,OBOR2)END;
'>':CASE GC OF'>':BEGIN RDCH; IF GC='='THEN LO2(L2,OSHR4)ELSE LO1(L5,OSHR2)END;
'=':LO2(L6,OTGE2)ELSE LO1(L6,OTGT2)END;
'<':CASE GC OF'<':BEGIN RDCH; IF GC='='THEN LO2(L2,OSHL4)ELSE LO1(L5,OSHL2)END;
'=':LO2(L6,OTLE2);'>':LO2(L6,OTNE2)ELSE LO1(L6,OTLT2)END END END END;
FUNCTION FIND:TFLG; VAR SZ:TINT; PS:PSTR; BEGIN FIND:=FALSE;
IF GO=OLIT1 THEN BEGIN SZ:=GSZ; PS:=GPS; WHILE SZ>0 DO BEGIN SZ:=PRED(SZ);
IF PS.PTR^=GS THEN BEGIN GPI.ADR:=ADDR(GV[SZ]); FIND:=TRUE; EXIT END;
PS.ADR:=SUCC(PS.ADR+LENGTH(PS.PTR^)) END END ELSE FAIL(ESYN) END;
PROCEDURE ADDS(I:TINT); BEGIN GPS.ADR:=PRED(GPS.ADR-LENGTH(GS)); GPS.PTR^:=GS;
GPI.ADR:=ADDR(GV[GSZ]); GPI.PTR^:=I; GSZ:=SUCC(GSZ) END;
FUNCTION NUMB:TINT; VAR I:TINT; PI:PINT; NEG:TFLG; BEGIN PI:=GPI; I:=0;
NEG:=GO=OSUB2; IF GL=L4 THEN NEXT; IF GO=OLIT2 THEN BEGIN I:=GI; NEXT END
ELSE IF FIND THEN BEGIN IF GS[1]='.' THEN BEGIN I:=GPI.PTR^; NEXT END
ELSE BEGIN NEXT; IF GO=OLEAD THEN BEGIN I:=GPI.PTR^; NEXT END
ELSE I:=GM.I[GPI.PTR^SHR 1] END END ELSE FAIL(EMIS);
IF NEG THEN NUMB:=-I ELSE NUMB:=I; GPI:=PI END;
FUNCTION ONOT(O:TOPR):TFLG; BEGIN IF GO=O THEN BEGIN ONOT:=FALSE; NEXT END
ELSE ONOT:=TRUE END;
PROCEDURE EXPR; FORWARD; PROCEDURE LOAD; FORWARD;
PROCEDURE ELEM(L:TLEV;IS1:TFLG); LABEL 0; VAR I:TINT; O:TOPR; ID:TFLG;
BEGIN IF L>L2 THEN BEGIN ELEM(PRED(L),TRUE); WHILE L=GL DO BEGIN
O:=GO; NEXT; WO(OSET1); ELEM(PRED(L),TRUE); WO(O) END END
ELSE BEGIN CASE GO OF OCASE:BEGIN NEXT; ELEM(L0,IS1); WO(OCASE) END;
OSET1:BEGIN NEXT; EXPR; IF GO=OSET2 THEN NEXT ELSE FAIL(EMIS) END;
OSET4:BEGIN NEXT; EXPR; WO(OEXIT) END;
OJUMP:BEGIN I:=GMZ; NEXT; EXPR; OI(OJPNE,I) END;
OLIT2:BEGIN OLI(GI); NEXT END; OLIT3:BEGIN WO(OLIT3); WS; NEXT END;
OLD11,OLD12:BEGIN IS1:=GO=OLD11; NEXT; ELEM(L0,IS1); IF(L=GL)AND(L=L2)THEN
BEGIN IF GO=OLEAD THEN FAIL(ESYN); WO(OSET1); O:=GO; NEXT; EXPR;
IF IS1 THEN WO(PRED(O)) ELSE WO(O) END ELSE OB(OLD12,IS1) END
ELSE BEGIN O:=GO; IF GL IN [L3,L4,L5,L6] THEN BEGIN
NEXT; ELEM(L1,TRUE); WO(PRED(O)) END
ELSE BEGIN ID:=O IN [OAD2A,OSU2A]; IF ID THEN NEXT; IF FIND THEN
IF GS[1]='.' THEN BEGIN IF ID THEN FAIL(ESYN); OLI(GPI.PTR^); NEXT END
ELSE BEGIN IF GPI.PTR^<0 THEN BEGIN OLI(-GPI.PTR^); WO(OLEAD) END ELSE OLI(GPI.PTR^);
NEXT; IF ID THEN OB(O,IS1) ELSE IF(L=GL)AND(L=L2)THEN BEGIN
IF GO=OLEAD THEN NEXT ELSE BEGIN WO(OSET1); O:=GO; NEXT; EXPR; WO(O)
END END ELSE IF GO IN [OAD2A,OSU2A] THEN BEGIN OB(SUCC(SUCC(GO)),IS1);
NEXT END ELSE WO(OLOAD) END ELSE FAIL(EMIS) END END END;
IF L>L0 THEN BEGIN 0:CASE GO OF
OLD11,OLD12:BEGIN WO(OSET1); IS1:=GO=OLD11; NEXT; ELEM(L0,TRUE);
IF(L=GL)AND(L=L2)THEN BEGIN OB(OIDX2,IS1); O:=GO; NEXT; IF O=OLEAD THEN
WO(OSET2) ELSE BEGIN EXPR; IF IS1 THEN WO(PRED(O)) ELSE WO(O) END END
ELSE OB(OLD22,IS1); GOTO 0 END;
OSET1:BEGIN WO(OSET1); I:=1; NEXT; IF GO<>OSET2 THEN
REPEAT EXPR; WO(OSET1); I:=SUCC(I) UNTIL ONOT(OIDX1);
OLI(I); WO(OJSUB); IF GO=OSET2 THEN NEXT ELSE FAIL(EMIS); GOTO 0
END END END END END;
PROCEDURE EXPR; VAR JPEX,JPEQ,JPNE:TINT; BEGIN JPEX:=GMZ;
IF GO=OENTR THEN BEGIN REPEAT NEXT; EXPR UNTIL GO<>OIDX2;
IF GO=OEXIT THEN NEXT ELSE FAIL(EMIS) END ELSE ELEM(L8,TRUE);
CASE GO OF
OJUMP:BEGIN NEXT; JPEQ:=GMZ; OI(OJPEQ,0); EXPR; OI(OJUMP,JPEX); OJP(OJPEQ,JPEQ) END;
OCASE:BEGIN NEXT; IF GO=OJPNE THEN BEGIN NEXT; JPNE:=GMZ; OI(OJPNE,0); EXPR;
OJP(OJPNE,JPNE) END ELSE BEGIN JPEQ:=GMZ; OI(OJPEQ,0); EXPR;
IF GO<>OJPNE THEN OJP(OJPEQ,JPEQ) ELSE BEGIN JPNE:=GMZ; OI(OJUMP,0);
OJP(OJPEQ,JPEQ); NEXT; EXPR; OJP(OJUMP,JPNE) END END END END END;
PROCEDURE ENTR; VAR AIMP,AVAR:TFLG; I,SZ:TINT; PS:PSTR; BEGIN
WHILE GE=0 DO CASE GO OF OLOAD:REPEAT NEXT; LOAD; NEXT UNTIL GO<>OIDX1;
OABRT,OEXIT:EXIT; OENTR:BEGIN SZ:=GSZ; PS:=GPS; NEXT; ENTR; GSZ:=SZ; GPS:=PS;
IF GO=OEXIT THEN NEXT ELSE FAIL(EMIS) END
ELSE BEGIN GMZ:=SUCC(GMZ)AND-2; AIMP:=GO=OJSUB; IF AIMP THEN NEXT;
WRITELN(GMZ:5,': ',GS);
IF GO<>OLIT1 THEN FAIL(ESYN) ELSE BEGIN AVAR:=GS[1]<>'.';
IF AIMP THEN IF FIND THEN IF AVAR THEN GM.I[GPI.PTR^SHR 1]:=GMZ
ELSE FAIL(ESYN) ELSE FAIL(EMIS) ELSE ADDS(GMZ);
IF GE=0 THEN BEGIN NEXT; CASE GO OF OJPNE:BEGIN NEXT; GPI.PTR^:=NUMB END;
OSET4:REPEAT NEXT; CASE GO OF OLD11:BEGIN NEXT; GMZ:=GMZ+NUMB END;
OLD12:BEGIN NEXT; GMZ:=GMZ+NUMB SHL 1 END;
OLIT3:BEGIN WS; NEXT END ELSE WI(NUMB) END UNTIL GO<>OIDX1;
OSET1:BEGIN IF AVAR THEN IF NOT AIMP THEN WI(GMZ+2); NEXT;
PS:=GPS; SZ:=GSZ; I:=1; IF GO<>OSET2 THEN
REPEAT REPEAT IF GO<>OIDX2 THEN IF(GO<>OLIT1)OR(GS[1]='.')THEN FAIL(ESYN)
ELSE BEGIN ADDS(-I); NEXT; CASE GO OF
OLD11:BEGIN NEXT; I:=I+SUCC(NUMB)SHR 1 END;
OLD12:BEGIN NEXT; I:=I+NUMB END ELSE I:=SUCC(I) END;
END UNTIL ONOT(OIDX1) UNTIL ONOT(OIDX2);
IF GO=OSET2 THEN NEXT ELSE FAIL(EMIS); OLI(I); WO(OENTR); EXPR;
WO(OEXIT); GPS:=PS; GSZ:=SZ END ELSE IF AVAR THEN FAIL(ESYN)
END END END END END END;
PROCEDURE LOAD; VAR F:TEXT; PF:PEXT; C:CHAR; BEGIN GO:=OLIT1;
IF NOT FIND THEN BEGIN ADDS(0); PF:=GPF; C:=GC; GPF.ADR:=ADDR(F); GC:=CHR(32);
IF OPEN THEN BEGIN NEXT; ENTR; SHUT END ELSE FAIL(EMIS); GPF:=PF; GC:=C END END;
PROCEDURE SAVE; VAR F:FILE OF TCHR; BEGIN
{$IFDEF HPC}ERASE('@');{$ENDIF} ASSIGN(F,'@'); REWRITE(F);
IF IORESULT=0 THEN BEGIN BLOCKWRITE(F,GM.C,GM.V[VTOM]); CLOSE(F) END END;
BEGIN WRITELN; WRITELN('MPL COMPILER / COPYRIGHT (C) 2017-2019 DEREK JOHN EVANS');
IF PARAMCOUNT<1 THEN WRITELN('USAGE: MPL <FILE>')
ELSE BEGIN ARGV(GS,1); GPS.ADR:=ADDR(GM.I[TOM]);
FOR GI:=0 TO TOM DO GM.I[GI]:=0; GE:=0; GSZ:=0; GMZ:=ORD(VPRG)SHL 1;
WRITELN; WRITELN('COMPILING'); WRITELN; LOAD;
IF GE=0 THEN BEGIN GS:='.'; GO:=OLIT1; IF FIND THEN BEGIN
GM.V[VAUX]:=GMZ; OLI(GPI.PTR^); WO(OSET1); OLI(1); WO(OJSUB); WO(OABRT);
GM.V[VNIL]:=1234; GM.V[VTOM]:=GMZ END
ELSE FAIL(EMIS) END; WRITELN; IF GE<>0 THEN BEGIN WRITELN;
WRITELN('FAILED'); WRITELN; WRITELN(GE:5,' ERRORS') END
ELSE BEGIN WRITELN(GSZ:5,' SYMBOLS'); WRITELN(GMZ:5,' BYTES');
SAVE; WRITELN; WRITELN('COMPLETED') END END;
WRITELN; {$IFDEF FPC}READLN{$ENDIF}
END.