Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 7596 → Rev 7597

/programs/develop/oberon07/Source/STATEMENTS.ob07
0,0 → 1,3297
(*
BSD 2-Clause License
 
Copyright (c) 2018, 2019, Anton Krotov
All rights reserved.
*)
 
MODULE STATEMENTS;
 
IMPORT
 
PARS, PROG, SCAN, ARITH, STRINGS, LISTS, CODE, X86, AMD64,
ERRORS, MACHINE, AVL := AVLTREES, CONSOLE, C := COLLECTIONS, mConst := CONSTANTS;
 
 
CONST
 
eCONST = PARS.eCONST; eTYPE = PARS.eTYPE; eVAR = PARS.eVAR;
eEXPR = PARS.eEXPR; eVREC = PARS.eVREC; ePROC = PARS.ePROC;
eVPAR = PARS.eVPAR; ePARAM = PARS.ePARAM; eSTPROC = PARS.eSTPROC;
eSTFUNC = PARS.eSTFUNC; eSYSFUNC = PARS.eSYSFUNC; eSYSPROC = PARS.eSYSPROC;
eIMP = PARS.eIMP;
 
errASSERT = 1; errPTR = 2; errDIV = 3; errPROC = 4;
errGUARD = 5; errIDX = 6; errCASE = 7; errCOPY = 8;
errCHR = 9; errWCHR = 10; errBYTE = 11;
 
chkIDX* = 0; chkGUARD* = 1; chkPTR* = 2; chkCHR* = 3; chkWCHR* = 4; chkBYTE* = 5;
 
chkALL* = {chkIDX, chkGUARD, chkPTR, chkCHR, chkWCHR, chkBYTE};
 
 
TYPE
 
isXXX = PROCEDURE (e: PARS.EXPR): BOOLEAN;
 
RANGE = RECORD
 
a, b: INTEGER
 
END;
 
CASE_LABEL = POINTER TO rCASE_LABEL;
 
rCASE_LABEL = RECORD (AVL.DATA)
 
range: RANGE;
 
variant, self: INTEGER;
 
type: PROG.TYPE_;
 
prev: CASE_LABEL
 
END;
 
CASE_VARIANT = POINTER TO RECORD (LISTS.ITEM)
 
label: INTEGER;
cmd: CODE.COMMAND;
processed: BOOLEAN
 
END;
 
 
VAR
 
begcall, endcall: CODE.COMMAND;
 
checking: SET;
 
CaseLabels, CaseVar: C.COLLECTION;
 
CaseVariants: LISTS.LIST;
 
 
PROCEDURE isExpr (e: PARS.EXPR): BOOLEAN;
RETURN e.obj IN {eCONST, eVAR, eEXPR, eVPAR, ePARAM, eVREC}
END isExpr;
 
 
PROCEDURE isVar (e: PARS.EXPR): BOOLEAN;
RETURN e.obj IN {eVAR, eVPAR, ePARAM, eVREC}
END isVar;
 
 
PROCEDURE isBoolean (e: PARS.EXPR): BOOLEAN;
RETURN isExpr(e) & (e.type.typ = PROG.tBOOLEAN)
END isBoolean;
 
 
PROCEDURE isInteger (e: PARS.EXPR): BOOLEAN;
RETURN isExpr(e) & (e.type.typ = PROG.tINTEGER)
END isInteger;
 
 
PROCEDURE isByte (e: PARS.EXPR): BOOLEAN;
RETURN isExpr(e) & (e.type.typ = PROG.tBYTE)
END isByte;
 
 
PROCEDURE isInt (e: PARS.EXPR): BOOLEAN;
RETURN isByte(e) OR isInteger(e)
END isInt;
 
 
PROCEDURE isReal (e: PARS.EXPR): BOOLEAN;
RETURN isExpr(e) & (e.type.typ = PROG.tREAL)
END isReal;
 
 
PROCEDURE isSet (e: PARS.EXPR): BOOLEAN;
RETURN isExpr(e) & (e.type.typ = PROG.tSET)
END isSet;
 
 
PROCEDURE isString (e: PARS.EXPR): BOOLEAN;
RETURN (e.obj = eCONST) & (e.type.typ IN {PROG.tSTRING, PROG.tCHAR})
END isString;
 
 
PROCEDURE isStringW (e: PARS.EXPR): BOOLEAN;
RETURN (e.obj = eCONST) & (e.type.typ IN {PROG.tSTRING, PROG.tCHAR, PROG.tWCHAR})
END isStringW;
 
 
PROCEDURE isChar (e: PARS.EXPR): BOOLEAN;
RETURN isExpr(e) & (e.type.typ = PROG.tCHAR)
END isChar;
 
 
PROCEDURE isCharArray (e: PARS.EXPR): BOOLEAN;
RETURN isExpr(e) & (e.type.typ = PROG.tARRAY) & (e.type.base.typ = PROG.tCHAR)
END isCharArray;
 
 
PROCEDURE isCharW (e: PARS.EXPR): BOOLEAN;
RETURN isExpr(e) & (e.type.typ = PROG.tWCHAR)
END isCharW;
 
 
PROCEDURE isCharArrayW (e: PARS.EXPR): BOOLEAN;
RETURN isExpr(e) & (e.type.typ = PROG.tARRAY) & (e.type.base.typ = PROG.tWCHAR)
END isCharArrayW;
 
 
PROCEDURE isCharArrayX (e: PARS.EXPR): BOOLEAN;
RETURN isExpr(e) & (e.type.typ = PROG.tARRAY) & (e.type.base.typ IN {PROG.tCHAR, PROG.tWCHAR})
END isCharArrayX;
 
 
PROCEDURE isPtr (e: PARS.EXPR): BOOLEAN;
RETURN isExpr(e) & (e.type.typ = PROG.tPOINTER)
END isPtr;
 
 
PROCEDURE isRec (e: PARS.EXPR): BOOLEAN;
RETURN isExpr(e) & (e.type.typ = PROG.tRECORD)
END isRec;
 
 
PROCEDURE isArr (e: PARS.EXPR): BOOLEAN;
RETURN isExpr(e) & (e.type.typ = PROG.tARRAY)
END isArr;
 
 
PROCEDURE isProc (e: PARS.EXPR): BOOLEAN;
RETURN isExpr(e) & (e.type.typ = PROG.tPROCEDURE) OR (e.obj IN {ePROC, eIMP})
END isProc;
 
 
PROCEDURE isNil (e: PARS.EXPR): BOOLEAN;
RETURN e.type.typ = PROG.tNIL
END isNil;
 
 
PROCEDURE getpos (parser: PARS.PARSER; VAR pos: SCAN.POSITION);
BEGIN
pos := parser.lex.pos
END getpos;
 
 
PROCEDURE NextPos (parser: PARS.PARSER; VAR pos: SCAN.POSITION);
BEGIN
PARS.NextPos(parser, pos)
END NextPos;
 
 
PROCEDURE strlen (e: PARS.EXPR): INTEGER;
VAR
res: INTEGER;
 
BEGIN
ASSERT(isString(e));
IF e.type.typ = PROG.tCHAR THEN
res := 1
ELSE
res := LENGTH(e.value.string(SCAN.IDENT).s)
END
RETURN res
END strlen;
 
 
PROCEDURE _length (s: ARRAY OF CHAR): INTEGER;
VAR
i, res: INTEGER;
 
BEGIN
i := 0;
res := 0;
WHILE (i < LEN(s)) & (s[i] # 0X) DO
IF (s[i] <= CHR(127)) OR (s[i] >= CHR(192)) THEN
INC(res)
END;
INC(i)
END
 
RETURN res
END _length;
 
 
PROCEDURE utf8strlen (e: PARS.EXPR): INTEGER;
VAR
res: INTEGER;
 
BEGIN
ASSERT(isStringW(e));
IF e.type.typ IN {PROG.tCHAR, PROG.tWCHAR} THEN
res := 1
ELSE
res := _length(e.value.string(SCAN.IDENT).s)
END
RETURN res
END utf8strlen;
 
 
PROCEDURE StrToWChar (s: ARRAY OF CHAR): INTEGER;
VAR
res: ARRAY 2 OF WCHAR;
 
BEGIN
ASSERT(STRINGS.Utf8To16(s, res) = 1)
RETURN ORD(res[0])
END StrToWChar;
 
 
PROCEDURE isStringW1 (e: PARS.EXPR): BOOLEAN;
RETURN (e.obj = eCONST) & isString(e) & (utf8strlen(e) = 1) & (strlen(e) > 1)
END isStringW1;
 
 
PROCEDURE assigncomp (e: PARS.EXPR; t: PROG.TYPE_): BOOLEAN;
VAR
res: BOOLEAN;
 
 
PROCEDURE arrcomp (src, dst: PROG.TYPE_): BOOLEAN;
RETURN (dst.typ = PROG.tARRAY) & PROG.isOpenArray(src) &
~PROG.isOpenArray(src.base) & ~PROG.isOpenArray(dst.base) &
PROG.isTypeEq(src.base, dst.base)
END arrcomp;
 
 
BEGIN
IF isExpr(e) OR (e.obj IN {ePROC, eIMP}) THEN
IF arrcomp(e.type, t) THEN
res := TRUE
ELSIF isInt(e) & (t.typ IN {PROG.tBYTE, PROG.tINTEGER}) THEN
IF (e.obj = eCONST) & (t.typ = PROG.tBYTE) THEN
res := ARITH.range(e.value, 0, 255)
ELSE
res := TRUE
END
ELSIF isSet(e) & (t.typ = PROG.tSET) THEN
res := TRUE
ELSIF isBoolean(e) & (t.typ = PROG.tBOOLEAN) THEN
res := TRUE
ELSIF isReal(e) & (t.typ = PROG.tREAL) THEN
res := TRUE
ELSIF isChar(e) & (t.typ = PROG.tCHAR) THEN
res := TRUE
ELSIF (e.obj = eCONST) & isChar(e) & (t.typ = PROG.tWCHAR) THEN
res := TRUE
ELSIF isStringW1(e) & (t.typ = PROG.tWCHAR) THEN
res := TRUE
ELSIF isCharW(e) & (t.typ = PROG.tWCHAR) THEN
res := TRUE
ELSIF PROG.isBaseOf(t, e.type) THEN
res := TRUE
ELSIF ~PROG.isOpenArray(t) & ~PROG.isOpenArray(e.type) & PROG.isTypeEq(t, e.type) THEN
res := TRUE
ELSIF isNil(e) & (t.typ IN {PROG.tPOINTER, PROG.tPROCEDURE}) THEN
res := TRUE
ELSIF isString(e) & ((t.typ = PROG.tARRAY) & (t.base.typ = PROG.tCHAR) & (t.length > strlen(e))) THEN
res := TRUE
ELSIF isStringW(e) & ((t.typ = PROG.tARRAY) & (t.base.typ = PROG.tWCHAR) & (t.length > utf8strlen(e))) THEN
res := TRUE
ELSE
res := FALSE
END
ELSE
res := FALSE
END
RETURN res
END assigncomp;
 
 
PROCEDURE String (e: PARS.EXPR): INTEGER;
VAR
offset: INTEGER;
string: SCAN.IDENT;
 
BEGIN
IF strlen(e) # 1 THEN
string := e.value.string(SCAN.IDENT);
IF string.offset = -1 THEN
string.offset := CODE.putstr(string.s);
END;
offset := string.offset
ELSE
offset := CODE.putstr1(ARITH.Int(e.value))
END
 
RETURN offset
END String;
 
 
PROCEDURE StringW (e: PARS.EXPR): INTEGER;
VAR
offset: INTEGER;
string: SCAN.IDENT;
 
BEGIN
IF utf8strlen(e) # 1 THEN
string := e.value.string(SCAN.IDENT);
IF string.offsetW = -1 THEN
string.offsetW := CODE.putstrW(string.s);
END;
offset := string.offsetW
ELSE
IF e.type.typ IN {PROG.tWCHAR, PROG.tCHAR} THEN
offset := CODE.putstrW1(ARITH.Int(e.value))
ELSE (* e.type.typ = PROG.tSTRING *)
string := e.value.string(SCAN.IDENT);
IF string.offsetW = -1 THEN
string.offsetW := CODE.putstrW(string.s);
END;
offset := string.offsetW
END
END
 
RETURN offset
END StringW;
 
 
PROCEDURE CheckRange (range, line, errno: INTEGER);
VAR
label: INTEGER;
 
BEGIN
label := CODE.NewLabel();
CODE.AddCmd2(CODE.opCHKIDX, label, range);
CODE.OnError(line, errno);
CODE.SetLabel(label)
END CheckRange;
 
 
PROCEDURE assign (e: PARS.EXPR; VarType: PROG.TYPE_; line: INTEGER): BOOLEAN;
VAR
res: BOOLEAN;
label: INTEGER;
 
 
PROCEDURE arrcomp (src, dst: PROG.TYPE_): BOOLEAN;
RETURN (dst.typ = PROG.tARRAY) & PROG.isOpenArray(src) &
~PROG.isOpenArray(src.base) & ~PROG.isOpenArray(dst.base) &
PROG.isTypeEq(src.base, dst.base)
END arrcomp;
 
 
BEGIN
IF isExpr(e) OR (e.obj IN {ePROC, eIMP}) THEN
res := TRUE;
IF arrcomp(e.type, VarType) THEN
 
IF ~PROG.isOpenArray(VarType) THEN
CODE.AddCmd(CODE.opCONST, VarType.length)
END;
CODE.AddCmd(CODE.opCOPYA, VarType.base.size);
label := CODE.NewLabel();
CODE.AddJmpCmd(CODE.opJE, label);
CODE.OnError(line, errCOPY);
CODE.SetLabel(label)
 
ELSIF isInt(e) & (VarType.typ IN {PROG.tBYTE, PROG.tINTEGER}) THEN
IF VarType.typ = PROG.tINTEGER THEN
IF e.obj = eCONST THEN
CODE.AddCmd(CODE.opSAVEC, ARITH.Int(e.value))
ELSE
CODE.AddCmd0(CODE.opSAVE)
END
ELSE
IF e.obj = eCONST THEN
res := ARITH.range(e.value, 0, 255);
IF res THEN
CODE.AddCmd(CODE.opSAVE8C, ARITH.Int(e.value))
END
ELSE
IF chkBYTE IN checking THEN
label := CODE.NewLabel();
CODE.AddCmd2(CODE.opCHKBYTE, label, 0);
CODE.OnError(line, errBYTE);
CODE.SetLabel(label)
END;
CODE.AddCmd0(CODE.opSAVE8)
END
END
ELSIF isSet(e) & (VarType.typ = PROG.tSET) THEN
IF e.obj = eCONST THEN
CODE.AddCmd(CODE.opSAVEC, ARITH.Int(e.value))
ELSE
CODE.AddCmd0(CODE.opSAVE)
END
ELSIF isBoolean(e) & (VarType.typ = PROG.tBOOLEAN) THEN
IF e.obj = eCONST THEN
CODE.AddCmd(CODE.opSBOOLC, ARITH.Int(e.value))
ELSE
CODE.AddCmd0(CODE.opSBOOL)
END
ELSIF isReal(e) & (VarType.typ = PROG.tREAL) THEN
IF e.obj = eCONST THEN
CODE.Float(ARITH.Float(e.value))
END;
CODE.savef
ELSIF isChar(e) & (VarType.typ = PROG.tCHAR) THEN
IF e.obj = eCONST THEN
CODE.AddCmd(CODE.opSAVE8C, ARITH.Int(e.value))
ELSE
CODE.AddCmd0(CODE.opSAVE8)
END
ELSIF (e.obj = eCONST) & isChar(e) & (VarType.typ = PROG.tWCHAR) THEN
CODE.AddCmd(CODE.opSAVE16C, ARITH.Int(e.value))
ELSIF isStringW1(e) & (VarType.typ = PROG.tWCHAR) THEN
CODE.AddCmd(CODE.opSAVE16C, StrToWChar(e.value.string(SCAN.IDENT).s))
ELSIF isCharW(e) & (VarType.typ = PROG.tWCHAR) THEN
IF e.obj = eCONST THEN
CODE.AddCmd(CODE.opSAVE16C, ARITH.Int(e.value))
ELSE
CODE.AddCmd0(CODE.opSAVE16)
END
ELSIF PROG.isBaseOf(VarType, e.type) THEN
IF VarType.typ = PROG.tPOINTER THEN
CODE.AddCmd0(CODE.opSAVE)
ELSE
CODE.AddCmd(CODE.opCOPY, VarType.size)
END
ELSIF (e.type.typ = PROG.tCARD32) & (VarType.typ = PROG.tCARD32) THEN
CODE.AddCmd0(CODE.opSAVE32)
ELSIF (e.type.typ = PROG.tCARD16) & (VarType.typ = PROG.tCARD16) THEN
CODE.AddCmd0(CODE.opSAVE16)
ELSIF ~PROG.isOpenArray(VarType) & ~PROG.isOpenArray(e.type) & PROG.isTypeEq(VarType, e.type) THEN
IF e.obj = ePROC THEN
CODE.AssignProc(e.ident.proc.label)
ELSIF e.obj = eIMP THEN
CODE.AssignImpProc(e.ident.import)
ELSE
IF VarType.typ = PROG.tPROCEDURE THEN
CODE.AddCmd0(CODE.opSAVE)
ELSE
CODE.AddCmd(CODE.opCOPY, VarType.size)
END
END
ELSIF isNil(e) & (VarType.typ IN {PROG.tPOINTER, PROG.tPROCEDURE}) THEN
CODE.AddCmd(CODE.opSAVEC, 0)
ELSIF isString(e) & ((VarType.typ = PROG.tARRAY) & (VarType.base.typ = PROG.tCHAR) & (VarType.length > strlen(e))) THEN
CODE.saves(String(e), strlen(e) + 1)
ELSIF isStringW(e) & ((VarType.typ = PROG.tARRAY) & (VarType.base.typ = PROG.tWCHAR) & (VarType.length > utf8strlen(e))) THEN
CODE.saves(StringW(e), (utf8strlen(e) + 1) * 2)
ELSE
res := FALSE
END
ELSE
res := FALSE
END
RETURN res
END assign;
 
 
PROCEDURE LoadConst (e: PARS.EXPR);
BEGIN
CODE.AddCmd(CODE.opCONST, ARITH.Int(e.value))
END LoadConst;
 
 
PROCEDURE paramcomp (parser: PARS.PARSER; pos: SCAN.POSITION; e: PARS.EXPR; p: PROG.PARAM);
 
PROCEDURE arrcomp (e: PARS.EXPR; p: PROG.PARAM): BOOLEAN;
VAR
t1, t2: PROG.TYPE_;
 
BEGIN
t1 := p.type;
t2 := e.type;
WHILE (t2.typ = PROG.tARRAY) & PROG.isOpenArray(t1) DO
t1 := t1.base;
t2 := t2.base
END
 
RETURN PROG.isTypeEq(t1, t2)
END arrcomp;
 
 
PROCEDURE ArrLen (t: PROG.TYPE_; n: INTEGER): INTEGER;
VAR
res: INTEGER;
BEGIN
REPEAT
res := t.length;
t := t.base;
DEC(n)
UNTIL (n < 0) OR (t.typ # PROG.tARRAY);
ASSERT(n < 0)
RETURN res
END ArrLen;
 
 
PROCEDURE OpenArray (t, t2: PROG.TYPE_);
VAR
n: INTEGER;
d1, d2: INTEGER;
BEGIN
IF t.length # 0 THEN
CODE.AddCmd(CODE.opPARAM, 1);
n := PROG.Dim(t2) - 1;
WHILE n >= 0 DO
CODE.AddCmd(CODE.opCONST, ArrLen(t, n));
CODE.AddCmd(CODE.opPARAM, 1);
DEC(n)
END
ELSE
d1 := PROG.Dim(t);
d2 := PROG.Dim(t2);
IF d1 # d2 THEN
n := d2 - d1;
WHILE d2 > d1 DO
CODE.AddCmd(CODE.opCONST, ArrLen(t, d2 - 1));
DEC(d2)
END;
d2 := PROG.Dim(t2);
WHILE n > 0 DO
CODE.AddCmd(CODE.opROT, d2);
DEC(n)
END
END;
CODE.AddCmd(CODE.opPARAM, PROG.Dim(t2) + 1)
END
END OpenArray;
 
 
BEGIN
IF p.vPar THEN
 
PARS.check(isVar(e), parser, pos, 93);
IF p.type.typ = PROG.tRECORD THEN
PARS.check(PROG.isBaseOf(p.type, e.type), parser, pos, 66);
IF e.obj = eVREC THEN
IF e.ident # NIL THEN
CODE.AddCmd(CODE.opVADR, e.ident.offset - 1)
ELSE
CODE.AddCmd0(CODE.opPUSHT)
END
ELSE
CODE.AddCmd(CODE.opCONST, e.type.num)
END;
CODE.AddCmd(CODE.opPARAM, 2)
ELSIF PROG.isOpenArray(p.type) THEN
PARS.check(arrcomp(e, p), parser, pos, 66);
OpenArray(e.type, p.type)
ELSE
PARS.check(PROG.isTypeEq(e.type, p.type), parser, pos, 66);
CODE.AddCmd(CODE.opPARAM, 1)
END;
PARS.check(~e.readOnly, parser, pos, 94)
 
ELSE
PARS.check(isExpr(e) OR isProc(e), parser, pos, 66);
IF PROG.isOpenArray(p.type) THEN
IF e.type.typ = PROG.tARRAY THEN
PARS.check(arrcomp(e, p), parser, pos, 66);
OpenArray(e.type, p.type)
ELSIF isString(e) & (p.type.typ = PROG.tARRAY) & (p.type.base.typ = PROG.tCHAR) THEN
CODE.AddCmd(CODE.opSADR, String(e));
CODE.AddCmd(CODE.opPARAM, 1);
CODE.AddCmd(CODE.opCONST, strlen(e) + 1);
CODE.AddCmd(CODE.opPARAM, 1)
ELSIF isStringW(e) & (p.type.typ = PROG.tARRAY) & (p.type.base.typ = PROG.tWCHAR) THEN
CODE.AddCmd(CODE.opSADR, StringW(e));
CODE.AddCmd(CODE.opPARAM, 1);
CODE.AddCmd(CODE.opCONST, utf8strlen(e) + 1);
CODE.AddCmd(CODE.opPARAM, 1)
ELSE
PARS.error(parser, pos, 66)
END
ELSE
PARS.check(~PROG.isOpenArray(e.type), parser, pos, 66);
PARS.check(assigncomp(e, p.type), parser, pos, 66);
IF e.obj = eCONST THEN
IF e.type.typ = PROG.tREAL THEN
CODE.Float(ARITH.Float(e.value));
CODE.pushf
ELSIF e.type.typ = PROG.tNIL THEN
CODE.AddCmd(CODE.opCONST, 0);
CODE.AddCmd(CODE.opPARAM, 1)
ELSIF isStringW1(e) & (p.type.typ = PROG.tWCHAR) THEN
CODE.AddCmd(CODE.opCONST, StrToWChar(e.value.string(SCAN.IDENT).s));
CODE.AddCmd(CODE.opPARAM, 1)
ELSIF (e.type.typ = PROG.tSTRING) OR
(e.type.typ IN {PROG.tCHAR, PROG.tWCHAR}) & (p.type.typ = PROG.tARRAY) & (p.type.base.typ IN {PROG.tCHAR, PROG.tWCHAR}) THEN
CODE.SetMinDataSize(p.type.size);
IF p.type.base.typ = PROG.tCHAR THEN
CODE.AddCmd(CODE.opSADR, String(e))
ELSE (* WCHAR *)
CODE.AddCmd(CODE.opSADR, StringW(e))
END;
CODE.AddCmd(CODE.opPARAM, 1)
ELSE
LoadConst(e);
CODE.AddCmd(CODE.opPARAM, 1)
END
ELSIF e.obj = ePROC THEN
PARS.check(e.ident.global, parser, pos, 85);
CODE.PushProc(e.ident.proc.label);
CODE.AddCmd(CODE.opPARAM, 1)
ELSIF e.obj = eIMP THEN
CODE.PushImpProc(e.ident.import);
CODE.AddCmd(CODE.opPARAM, 1)
ELSIF isExpr(e) & (e.type.typ = PROG.tREAL) THEN
CODE.pushf
ELSE
IF (p.type.typ = PROG.tBYTE) & (e.type.typ = PROG.tINTEGER) & (chkBYTE IN checking) THEN
CheckRange(256, pos.line, errBYTE)
END;
CODE.AddCmd(CODE.opPARAM, 1)
END
END
 
END
END paramcomp;
 
 
PROCEDURE stProc (parser: PARS.PARSER; VAR e: PARS.EXPR);
VAR
e2: PARS.EXPR;
pos: SCAN.POSITION;
proc: INTEGER;
label: INTEGER;
n, i: INTEGER;
code: ARITH.VALUE;
e1: PARS.EXPR;
wchar: BOOLEAN;
cmd1,
cmd2: CODE.COMMAND;
 
 
PROCEDURE varparam (parser: PARS.PARSER; pos: SCAN.POSITION; isfunc: isXXX; readOnly: BOOLEAN; VAR e: PARS.EXPR);
BEGIN
parser.designator(parser, e);
PARS.check(isVar(e), parser, pos, 93);
PARS.check(isfunc(e), parser, pos, 66);
IF readOnly THEN
PARS.check(~e.readOnly, parser, pos, 94)
END
END varparam;
 
 
PROCEDURE shift_minmax (proc: INTEGER): CHAR;
VAR
res: CHAR;
BEGIN
CASE proc OF
|PROG.stASR: res := "A"
|PROG.stLSL: res := "L"
|PROG.stROR: res := "O"
|PROG.stLSR: res := "R"
|PROG.stMIN: res := "m"
|PROG.stMAX: res := "x"
END
RETURN res
END shift_minmax;
 
 
BEGIN
ASSERT(e.obj IN {eSTPROC, eSTFUNC, eSYSPROC, eSYSFUNC});
 
getpos(parser, pos);
proc := e.stproc;
 
IF e.obj IN {eSYSPROC, eSYSFUNC} THEN
IF parser.unit.scopeLvl > 0 THEN
parser.unit.scopes[parser.unit.scopeLvl].enter(CODE.COMMAND).allocReg := FALSE
END
END;
 
IF e.obj IN {eSTPROC, eSYSPROC} THEN
 
CASE proc OF
|PROG.stASSERT:
parser.expression(parser, e);
PARS.check(isBoolean(e), parser, pos, 66);
IF e.obj = eCONST THEN
IF ~ARITH.getBool(e.value) THEN
CODE.OnError(pos.line, errASSERT)
END
ELSE
label := CODE.NewLabel();
CODE.AddJmpCmd(CODE.opJE, label);
CODE.OnError(pos.line, errASSERT);
CODE.SetLabel(label)
END
 
|PROG.stINC, PROG.stDEC:
CODE.pushBegEnd(begcall, endcall);
varparam(parser, pos, isInt, TRUE, e);
IF e.type.typ = PROG.tINTEGER THEN
IF parser.sym = SCAN.lxCOMMA THEN
NextPos(parser, pos);
CODE.setlast(begcall);
parser.expression(parser, e2);
CODE.setlast(endcall.prev(CODE.COMMAND));
PARS.check(isInt(e2), parser, pos, 66);
IF e2.obj = eCONST THEN
CODE.AddCmd(CODE.opINCC + ORD(proc = PROG.stDEC), ARITH.Int(e2.value))
ELSE
CODE.AddCmd0(CODE.opINC + ORD(proc = PROG.stDEC))
END
ELSE
CODE.AddCmd0(CODE.opINC1 + ORD(proc = PROG.stDEC))
END
ELSE (* e.type.typ = PROG.tBYTE *)
IF parser.sym = SCAN.lxCOMMA THEN
NextPos(parser, pos);
CODE.setlast(begcall);
parser.expression(parser, e2);
CODE.setlast(endcall.prev(CODE.COMMAND));
PARS.check(isInt(e2), parser, pos, 66);
IF e2.obj = eCONST THEN
CODE.AddCmd(CODE.opINCCB + ORD(proc = PROG.stDEC), ARITH.Int(e2.value))
ELSE
CODE.AddCmd0(CODE.opINCB + ORD(proc = PROG.stDEC))
END
ELSE
CODE.AddCmd0(CODE.opINC1B + ORD(proc = PROG.stDEC))
END
END;
CODE.popBegEnd(begcall, endcall)
 
|PROG.stINCL, PROG.stEXCL:
CODE.pushBegEnd(begcall, endcall);
varparam(parser, pos, isSet, TRUE, e);
PARS.checklex(parser, SCAN.lxCOMMA);
NextPos(parser, pos);
CODE.setlast(begcall);
parser.expression(parser, e2);
CODE.setlast(endcall.prev(CODE.COMMAND));
PARS.check(isInt(e2), parser, pos, 66);
IF e2.obj = eCONST THEN
PARS.check(ARITH.range(e2.value, 0, MACHINE.target.maxSet), parser, pos, 56);
CODE.AddCmd(CODE.opINCLC + ORD(proc = PROG.stEXCL), ARITH.Int(e2.value))
ELSE
CODE.AddCmd0(CODE.opINCL + ORD(proc = PROG.stEXCL))
END;
CODE.popBegEnd(begcall, endcall)
 
|PROG.stNEW:
varparam(parser, pos, isPtr, TRUE, e);
CODE.New(e.type.base.size, e.type.base.num)
 
|PROG.stDISPOSE:
varparam(parser, pos, isPtr, TRUE, e);
CODE.AddCmd0(CODE.opDISP)
 
|PROG.stPACK:
varparam(parser, pos, isReal, TRUE, e);
PARS.checklex(parser, SCAN.lxCOMMA);
NextPos(parser, pos);
parser.expression(parser, e2);
PARS.check(isInt(e2), parser, pos, 66);
IF e2.obj = eCONST THEN
CODE.AddCmd(CODE.opPACKC, ARITH.Int(e2.value))
ELSE
CODE.AddCmd0(CODE.opPACK)
END
 
|PROG.stUNPK:
varparam(parser, pos, isReal, TRUE, e);
PARS.checklex(parser, SCAN.lxCOMMA);
NextPos(parser, pos);
varparam(parser, pos, isInteger, TRUE, e2);
CODE.AddCmd0(CODE.opUNPK)
 
|PROG.stCOPY:
parser.expression(parser, e);
IF isString(e) OR isCharArray(e) THEN
wchar := FALSE
ELSIF isStringW(e) OR isCharArrayW(e) THEN
wchar := TRUE
ELSE
PARS.check(FALSE, parser, pos, 66)
END;
 
IF isCharArrayX(e) & ~PROG.isOpenArray(e.type) THEN
CODE.AddCmd(CODE.opCONST, e.type.length)
END;
 
PARS.checklex(parser, SCAN.lxCOMMA);
NextPos(parser, pos);
 
IF wchar THEN
varparam(parser, pos, isCharArrayW, TRUE, e1)
ELSE
IF e.obj = eCONST THEN
varparam(parser, pos, isCharArrayX, TRUE, e1)
ELSE
varparam(parser, pos, isCharArray, TRUE, e1)
END;
 
wchar := e1.type.base.typ = PROG.tWCHAR
END;
 
IF ~PROG.isOpenArray(e1.type) THEN
CODE.AddCmd(CODE.opCONST, e1.type.length)
END;
 
IF e.obj = eCONST THEN
IF wchar THEN
CODE.AddCmd(CODE.opSADR, StringW(e));
CODE.AddCmd(CODE.opCONST, utf8strlen(e) + 1)
ELSE
CODE.AddCmd(CODE.opSADR, String(e));
CODE.AddCmd(CODE.opCONST, strlen(e) + 1)
END;
CODE.AddCmd(CODE.opCOPYS2, e1.type.base.size)
ELSE
CODE.AddCmd(CODE.opCOPYS, e1.type.base.size)
END
 
|PROG.sysGET:
parser.expression(parser, e);
PARS.check(isInt(e), parser, pos, 66);
IF e.obj = eCONST THEN
LoadConst(e)
END;
PARS.checklex(parser, SCAN.lxCOMMA);
NextPos(parser, pos);
parser.designator(parser, e2);
PARS.check(isVar(e2), parser, pos, 93);
PARS.check((e2.type.typ IN PROG.BASICTYPES) OR (e2.type.typ = PROG.tPOINTER) OR (e2.type.typ = PROG.tPROCEDURE), parser, pos, 66);
CODE.SysGet(e2.type.size)
 
|PROG.sysPUT, PROG.sysPUT8, PROG.sysPUT16, PROG.sysPUT32:
CODE.pushBegEnd(begcall, endcall);
parser.expression(parser, e);
PARS.check(isInt(e), parser, pos, 66);
IF e.obj = eCONST THEN
LoadConst(e)
END;
PARS.checklex(parser, SCAN.lxCOMMA);
NextPos(parser, pos);
CODE.setlast(begcall);
parser.expression(parser, e2);
PARS.check(isExpr(e2), parser, pos, 66);
 
IF proc = PROG.sysPUT THEN
PARS.check((e2.type.typ IN PROG.BASICTYPES) OR (e2.type.typ = PROG.tPOINTER) OR (e2.type.typ = PROG.tPROCEDURE), parser, pos, 66);
IF e2.obj = eCONST THEN
IF e2.type.typ = PROG.tREAL THEN
CODE.setlast(endcall.prev(CODE.COMMAND));
CODE.Float(ARITH.Float(e2.value));
CODE.savef
ELSE
LoadConst(e2);
CODE.setlast(endcall.prev(CODE.COMMAND));
CODE.SysPut(e2.type.size)
END
ELSE
CODE.setlast(endcall.prev(CODE.COMMAND));
IF e2.type.typ = PROG.tREAL THEN
CODE.savef
ELSIF e2.type.typ = PROG.tBYTE THEN
CODE.SysPut(PARS.program.stTypes.tINTEGER.size)
ELSE
CODE.SysPut(e2.type.size)
END
END
 
ELSIF (proc = PROG.sysPUT8) OR (proc = PROG.sysPUT16) OR (proc = PROG.sysPUT32) THEN
PARS.check(e2.type.typ IN {PROG.tINTEGER, PROG.tBYTE, PROG.tCHAR, PROG.tWCHAR, PROG.tCARD16, PROG.tCARD32}, parser, pos, 66);
IF e2.obj = eCONST THEN
LoadConst(e2)
END;
CODE.setlast(endcall.prev(CODE.COMMAND));
IF proc = PROG.sysPUT8 THEN
CODE.SysPut(1)
ELSIF proc = PROG.sysPUT16 THEN
CODE.SysPut(2)
ELSIF proc = PROG.sysPUT32 THEN
CODE.SysPut(4)
END
 
END;
CODE.popBegEnd(begcall, endcall)
 
|PROG.sysMOVE:
FOR i := 1 TO 2 DO
parser.expression(parser, e);
PARS.check(isInt(e), parser, pos, 66);
IF e.obj = eCONST THEN
LoadConst(e)
END;
PARS.checklex(parser, SCAN.lxCOMMA);
NextPos(parser, pos)
END;
 
parser.expression(parser, e);
PARS.check(isInt(e), parser, pos, 66);
IF e.obj = eCONST THEN
LoadConst(e)
END;
CODE.AddCmd0(CODE.opMOVE)
 
|PROG.sysCOPY:
FOR i := 1 TO 2 DO
parser.designator(parser, e);
PARS.check(isVar(e), parser, pos, 93);
n := PROG.Dim(e.type);
WHILE n > 0 DO
CODE.drop;
DEC(n)
END;
PARS.checklex(parser, SCAN.lxCOMMA);
NextPos(parser, pos)
END;
 
parser.expression(parser, e);
PARS.check(isInt(e), parser, pos, 66);
IF e.obj = eCONST THEN
LoadConst(e)
END;
CODE.AddCmd0(CODE.opMOVE)
 
|PROG.sysCODE:
REPEAT
getpos(parser, pos);
PARS.ConstExpression(parser, code);
PARS.check(code.typ = ARITH.tINTEGER, parser, pos, 43);
PARS.check(ARITH.range(code, 0, 255), parser, pos, 42);
IF parser.sym = SCAN.lxCOMMA THEN
PARS.Next(parser)
ELSE
PARS.checklex(parser, SCAN.lxRROUND)
END;
CODE.AddCmd(CODE.opCODE, ARITH.getInt(code))
UNTIL parser.sym = SCAN.lxRROUND
 
END;
 
e.obj := eEXPR;
e.type := NIL
 
ELSIF e.obj IN {eSTFUNC, eSYSFUNC} THEN
 
CASE e.stproc OF
|PROG.stABS:
parser.expression(parser, e);
PARS.check(isInt(e) OR isReal(e), parser, pos, 66);
IF e.obj = eCONST THEN
PARS.check(ARITH.abs(e.value), parser, pos, 39)
ELSE
CODE.abs(isReal(e))
END
 
|PROG.stASR, PROG.stLSL, PROG.stROR, PROG.stLSR, PROG.stMIN, PROG.stMAX:
parser.expression(parser, e);
PARS.check(isInt(e), parser, pos, 66);
PARS.checklex(parser, SCAN.lxCOMMA);
NextPos(parser, pos);
parser.expression(parser, e2);
PARS.check(isInt(e2), parser, pos, 66);
e.type := PARS.program.stTypes.tINTEGER;
IF (e.obj = eCONST) & (e2.obj = eCONST) THEN
ASSERT(ARITH.opInt(e.value, e2.value, shift_minmax(proc)))
ELSE
IF e.obj = eCONST THEN
CODE.shift_minmax1(shift_minmax(proc), ARITH.Int(e.value))
ELSIF e2.obj = eCONST THEN
CODE.shift_minmax2(shift_minmax(proc), ARITH.Int(e2.value))
ELSE
CODE.shift_minmax(shift_minmax(proc))
END;
e.obj := eEXPR
END
 
|PROG.stCHR:
parser.expression(parser, e);
PARS.check(isInt(e), parser, pos, 66);
e.type := PARS.program.stTypes.tCHAR;
IF e.obj = eCONST THEN
ARITH.setChar(e.value, ARITH.getInt(e.value));
PARS.check(ARITH.check(e.value), parser, pos, 107)
ELSE
IF chkCHR IN checking THEN
CheckRange(256, pos.line, errCHR)
ELSE
CODE.AddCmd0(CODE.opCHR)
END
END
 
|PROG.stWCHR:
parser.expression(parser, e);
PARS.check(isInt(e), parser, pos, 66);
e.type := PARS.program.stTypes.tWCHAR;
IF e.obj = eCONST THEN
ARITH.setWChar(e.value, ARITH.getInt(e.value));
PARS.check(ARITH.check(e.value), parser, pos, 101)
ELSE
IF chkWCHR IN checking THEN
CheckRange(65536, pos.line, errWCHR)
ELSE
CODE.AddCmd0(CODE.opWCHR)
END
END
 
|PROG.stFLOOR:
parser.expression(parser, e);
PARS.check(isReal(e), parser, pos, 66);
e.type := PARS.program.stTypes.tINTEGER;
IF e.obj = eCONST THEN
PARS.check(ARITH.floor(e.value), parser, pos, 39)
ELSE
CODE.floor
END
 
|PROG.stFLT:
parser.expression(parser, e);
PARS.check(isInt(e), parser, pos, 66);
e.type := PARS.program.stTypes.tREAL;
IF e.obj = eCONST THEN
ARITH.flt(e.value)
ELSE
PARS.check(CODE.flt(), parser, pos, 41)
END
 
|PROG.stLEN:
cmd1 := CODE.getlast();
varparam(parser, pos, isArr, FALSE, e);
IF e.type.length > 0 THEN
cmd2 := CODE.getlast();
CODE.delete2(cmd1.next, cmd2);
CODE.setlast(cmd1);
ASSERT(ARITH.setInt(e.value, e.type.length));
e.obj := eCONST
ELSE
CODE.len(PROG.Dim(e.type))
END;
e.type := PARS.program.stTypes.tINTEGER
 
|PROG.stLENGTH:
parser.expression(parser, e);
IF isCharArray(e) THEN
IF e.type.length > 0 THEN
CODE.AddCmd(CODE.opCONST, e.type.length)
END;
CODE.AddCmd0(CODE.opLENGTH)
ELSIF isCharArrayW(e) THEN
IF e.type.length > 0 THEN
CODE.AddCmd(CODE.opCONST, e.type.length)
END;
CODE.AddCmd0(CODE.opLENGTHW)
ELSE
PARS.check(FALSE, parser, pos, 66);
END;
e.type := PARS.program.stTypes.tINTEGER
 
|PROG.stODD:
parser.expression(parser, e);
PARS.check(isInt(e), parser, pos, 66);
e.type := PARS.program.stTypes.tBOOLEAN;
IF e.obj = eCONST THEN
ARITH.odd(e.value)
ELSE
CODE.odd
END
 
|PROG.stORD:
parser.expression(parser, e);
PARS.check(isChar(e) OR isBoolean(e) OR isSet(e) OR isCharW(e) OR isStringW1(e), parser, pos, 66);
IF e.obj = eCONST THEN
IF isStringW1(e) THEN
ASSERT(ARITH.setInt(e.value, StrToWChar(e.value.string(SCAN.IDENT).s)))
ELSE
ARITH.ord(e.value)
END
ELSE
IF isBoolean(e) THEN
CODE.ord
END
END;
e.type := PARS.program.stTypes.tINTEGER
 
|PROG.stBITS:
parser.expression(parser, e);
PARS.check(isInt(e), parser, pos, 66);
IF e.obj = eCONST THEN
ARITH.bits(e.value)
END;
e.type := PARS.program.stTypes.tSET
 
|PROG.sysADR:
parser.designator(parser, e);
IF isVar(e) THEN
n := PROG.Dim(e.type);
WHILE n > 0 DO
CODE.drop;
DEC(n)
END
ELSIF e.obj = ePROC THEN
CODE.PushProc(e.ident.proc.label)
ELSIF e.obj = eIMP THEN
CODE.PushImpProc(e.ident.import)
ELSE
PARS.check(FALSE, parser, pos, 108)
END;
e.type := PARS.program.stTypes.tINTEGER
 
|PROG.sysSADR:
parser.expression(parser, e);
PARS.check(isString(e), parser, pos, 66);
CODE.AddCmd(CODE.opSADR, String(e));
e.type := PARS.program.stTypes.tINTEGER;
e.obj := eEXPR
 
|PROG.sysWSADR:
parser.expression(parser, e);
PARS.check(isStringW(e), parser, pos, 66);
CODE.AddCmd(CODE.opSADR, StringW(e));
e.type := PARS.program.stTypes.tINTEGER;
e.obj := eEXPR
 
|PROG.sysTYPEID:
parser.expression(parser, e);
PARS.check(e.obj = eTYPE, parser, pos, 68);
IF e.type.typ = PROG.tRECORD THEN
ASSERT(ARITH.setInt(e.value, e.type.num))
ELSIF e.type.typ = PROG.tPOINTER THEN
ASSERT(ARITH.setInt(e.value, e.type.base.num))
ELSE
PARS.check(FALSE, parser, pos, 52)
END;
e.obj := eCONST;
e.type := PARS.program.stTypes.tINTEGER
 
|PROG.sysINF:
PARS.check(CODE.inf(), parser, pos, 41);
e.obj := eEXPR;
e.type := PARS.program.stTypes.tREAL
 
|PROG.sysSIZE:
parser.expression(parser, e);
PARS.check(e.obj = eTYPE, parser, pos, 68);
ASSERT(ARITH.setInt(e.value, e.type.size));
e.obj := eCONST;
e.type := PARS.program.stTypes.tINTEGER
 
END
 
END;
 
PARS.checklex(parser, SCAN.lxRROUND);
PARS.Next(parser);
 
IF e.obj # eCONST THEN
e.obj := eEXPR
END
 
END stProc;
 
 
PROCEDURE ActualParameters (parser: PARS.PARSER; VAR e: PARS.EXPR);
VAR
proc: PROG.TYPE_;
param: LISTS.ITEM;
e1: PARS.EXPR;
pos: SCAN.POSITION;
 
BEGIN
ASSERT(parser.sym = SCAN.lxLROUND);
 
IF (e.obj IN {ePROC, eIMP}) OR isExpr(e) THEN
proc := e.type;
PARS.check1(proc.typ = PROG.tPROCEDURE, parser, 86);
PARS.Next(parser);
 
param := proc.params.first;
WHILE param # NIL DO
getpos(parser, pos);
 
CODE.setlast(begcall);
 
IF param(PROG.PARAM).vPar THEN
parser.designator(parser, e1)
ELSE
parser.expression(parser, e1)
END;
paramcomp(parser, pos, e1, param(PROG.PARAM));
param := param.next;
IF param # NIL THEN
PARS.checklex(parser, SCAN.lxCOMMA);
PARS.Next(parser)
END
END;
 
PARS.checklex(parser, SCAN.lxRROUND);
PARS.Next(parser);
 
e.obj := eEXPR;
e.type := proc.base
 
ELSIF e.obj IN {eSTPROC, eSTFUNC, eSYSPROC, eSYSFUNC} THEN
PARS.Next(parser);
stProc(parser, e)
ELSE
PARS.check1(FALSE, parser, 86)
END
 
END ActualParameters;
 
 
PROCEDURE qualident (parser: PARS.PARSER; VAR e: PARS.EXPR);
VAR
ident: PROG.IDENT;
import: BOOLEAN;
pos: SCAN.POSITION;
 
BEGIN
PARS.checklex(parser, SCAN.lxIDENT);
getpos(parser, pos);
import := FALSE;
ident := parser.unit.idents.get(parser.unit, parser.lex.ident, FALSE);
PARS.check1(ident # NIL, parser, 48);
IF ident.typ = PROG.idMODULE THEN
PARS.ExpectSym(parser, SCAN.lxPOINT);
PARS.ExpectSym(parser, SCAN.lxIDENT);
ident := ident.unit.idents.get(ident.unit, parser.lex.ident, FALSE);
PARS.check1((ident # NIL) & ident.export, parser, 48);
import := TRUE
END;
PARS.Next(parser);
 
e.readOnly := FALSE;
e.ident := ident;
 
CASE ident.typ OF
|PROG.idCONST:
e.obj := eCONST;
e.type := ident.type;
e.value := ident.value
|PROG.idTYPE:
e.obj := eTYPE;
e.type := ident.type
|PROG.idVAR:
e.obj := eVAR;
e.type := ident.type;
e.readOnly := import
|PROG.idPROC:
e.obj := ePROC;
e.type := ident.type
|PROG.idIMP:
e.obj := eIMP;
e.type := ident.type
|PROG.idVPAR:
e.type := ident.type;
IF e.type.typ = PROG.tRECORD THEN
e.obj := eVREC
ELSE
e.obj := eVPAR
END
|PROG.idPARAM:
e.obj := ePARAM;
e.type := ident.type;
e.readOnly := (e.type.typ IN {PROG.tRECORD, PROG.tARRAY})
|PROG.idSTPROC:
e.obj := eSTPROC;
e.stproc := ident.stproc
|PROG.idSTFUNC:
e.obj := eSTFUNC;
e.stproc := ident.stproc
|PROG.idSYSPROC:
e.obj := eSYSPROC;
e.stproc := ident.stproc
|PROG.idSYSFUNC:
PARS.check(~parser.constexp, parser, pos, 109);
e.obj := eSYSFUNC;
e.stproc := ident.stproc
|PROG.idNONE:
PARS.check(FALSE, parser, pos, 115)
END;
 
IF isVar(e) THEN
PARS.check(e.ident.global OR (e.ident.scopeLvl = parser.unit.scopeLvl), parser, pos, 105)
END
 
END qualident;
 
 
PROCEDURE deref (pos: SCAN.POSITION; e: PARS.EXPR; load: BOOLEAN; error: INTEGER);
VAR
label: INTEGER;
 
BEGIN
IF load THEN
CODE.load(e.type.size)
END;
 
IF chkPTR IN checking THEN
label := CODE.NewLabel();
CODE.AddJmpCmd(CODE.opJNZ, label);
CODE.OnError(pos.line, error);
CODE.SetLabel(label)
END
END deref;
 
 
PROCEDURE designator (parser: PARS.PARSER; VAR e: PARS.EXPR);
VAR
field: PROG.FIELD;
pos: SCAN.POSITION;
t, idx: PARS.EXPR;
 
 
PROCEDURE LoadAdr (e: PARS.EXPR);
VAR
offset: INTEGER;
 
PROCEDURE OpenArray (e: PARS.EXPR);
VAR
offset, n: INTEGER;
BEGIN
offset := e.ident.offset;
n := PROG.Dim(e.type);
WHILE n >= 0 DO
CODE.AddCmd(CODE.opVADR, offset);
DEC(offset);
DEC(n)
END
END OpenArray;
 
 
BEGIN
IF e.obj = eVAR THEN
offset := PROG.getOffset(PARS.program, e.ident);
IF e.ident.global THEN
CODE.AddCmd(CODE.opGADR, offset)
ELSE
CODE.AddCmd(CODE.opLADR, -offset)
END
ELSIF e.obj = ePARAM THEN
IF (e.type.typ = PROG.tRECORD) OR ((e.type.typ = PROG.tARRAY) & (e.type.length > 0)) THEN
CODE.AddCmd(CODE.opVADR, e.ident.offset)
ELSIF PROG.isOpenArray(e.type) THEN
OpenArray(e)
ELSE
CODE.AddCmd(CODE.opLADR, e.ident.offset)
END
ELSIF e.obj IN {eVPAR, eVREC} THEN
IF PROG.isOpenArray(e.type) THEN
OpenArray(e)
ELSE
CODE.AddCmd(CODE.opVADR, e.ident.offset)
END
END
END LoadAdr;
 
 
PROCEDURE OpenIdx (parser: PARS.PARSER; pos: SCAN.POSITION; e: PARS.EXPR);
VAR
label: INTEGER;
type: PROG.TYPE_;
n, offset, k: INTEGER;
 
BEGIN
 
IF chkIDX IN checking THEN
label := CODE.NewLabel();
CODE.AddCmd2(CODE.opCHKIDX2, label, 0);
CODE.OnError(pos.line, errIDX);
CODE.SetLabel(label)
ELSE
CODE.AddCmd(CODE.opCHKIDX2, -1)
END;
 
type := PROG.OpenBase(e.type);
IF type.size # 1 THEN
CODE.AddCmd(CODE.opMULC, type.size)
END;
n := PROG.Dim(e.type) - 1;
k := n;
WHILE n > 0 DO
CODE.AddCmd0(CODE.opMUL);
DEC(n)
END;
CODE.AddCmd0(CODE.opADD);
offset := e.ident.offset - 1;
n := k;
WHILE n > 0 DO
CODE.AddCmd(CODE.opVADR, offset);
DEC(offset);
DEC(n)
END
END OpenIdx;
 
 
BEGIN
qualident(parser, e);
 
IF e.obj IN {ePROC, eIMP} THEN
PROG.UseProc(parser.unit, e.ident.proc)
END;
 
IF isVar(e) THEN
LoadAdr(e)
END;
 
WHILE parser.sym = SCAN.lxPOINT DO
getpos(parser, pos);
PARS.check1(isExpr(e) & (e.type.typ IN {PROG.tRECORD, PROG.tPOINTER}), parser, 73);
IF e.type.typ = PROG.tPOINTER THEN
deref(pos, e, TRUE, errPTR)
END;
PARS.ExpectSym(parser, SCAN.lxIDENT);
IF e.type.typ = PROG.tPOINTER THEN
e.type := e.type.base;
e.readOnly := FALSE
END;
field := e.type.fields.get(e.type, parser.lex.ident, parser.unit);
PARS.check1(field # NIL, parser, 74);
e.type := field.type;
IF e.obj = eVREC THEN
e.obj := eVPAR
END;
IF field.offset # 0 THEN
CODE.AddCmd(CODE.opADDR, field.offset)
END;
PARS.Next(parser);
e.ident := NIL
 
ELSIF parser.sym = SCAN.lxLSQUARE DO
 
REPEAT
 
PARS.check1(isArr(e), parser, 75);
NextPos(parser, pos);
parser.expression(parser, idx);
PARS.check(isInt(idx), parser, pos, 76);
 
IF idx.obj = eCONST THEN
IF e.type.length > 0 THEN
PARS.check(ARITH.range(idx.value, 0, e.type.length - 1), parser, pos, 83);
IF ARITH.Int(idx.value) > 0 THEN
CODE.AddCmd(CODE.opADDR, ARITH.Int(idx.value) * e.type.base.size)
END
ELSE
PARS.check(ARITH.range(idx.value, 0, MACHINE.target.maxInt), parser, pos, 83);
LoadConst(idx);
OpenIdx(parser, pos, e)
END
ELSE
IF e.type.length > 0 THEN
IF chkIDX IN checking THEN
CheckRange(e.type.length, pos.line, errIDX)
END;
IF e.type.base.size # 1 THEN
CODE.AddCmd(CODE.opMULC, e.type.base.size)
END;
CODE.AddCmd0(CODE.opADD)
ELSE
OpenIdx(parser, pos, e)
END
END;
 
e.type := e.type.base
 
UNTIL parser.sym # SCAN.lxCOMMA;
 
PARS.checklex(parser, SCAN.lxRSQUARE);
PARS.Next(parser);
e.ident := NIL
 
ELSIF parser.sym = SCAN.lxCARET DO
getpos(parser, pos);
PARS.check1(isPtr(e), parser, 77);
deref(pos, e, TRUE, errPTR);
e.type := e.type.base;
e.readOnly := FALSE;
PARS.Next(parser);
e.ident := NIL;
e.obj := eVREC
 
ELSIF (parser.sym = SCAN.lxLROUND) & isExpr(e) & (e.type.typ IN {PROG.tRECORD, PROG.tPOINTER}) DO
 
IF e.type.typ = PROG.tRECORD THEN
PARS.check1(e.obj = eVREC, parser, 78)
END;
NextPos(parser, pos);
qualident(parser, t);
PARS.check(t.obj = eTYPE, parser, pos, 79);
 
IF e.type.typ = PROG.tRECORD THEN
PARS.check(t.type.typ = PROG.tRECORD, parser, pos, 80);
IF chkGUARD IN checking THEN
IF e.ident = NIL THEN
CODE.TypeGuard(CODE.opTYPEGD, t.type.num, pos.line, errGUARD)
ELSE
CODE.AddCmd(CODE.opVADR, e.ident.offset - 1);
CODE.TypeGuard(CODE.opTYPEGR, t.type.num, pos.line, errGUARD)
END
END;
ELSE
PARS.check(t.type.typ = PROG.tPOINTER, parser, pos, 81);
IF chkGUARD IN checking THEN
CODE.TypeGuard(CODE.opTYPEGP, t.type.base.num, pos.line, errGUARD)
END
END;
 
PARS.check(PROG.isBaseOf(e.type, t.type), parser, pos, 82);
 
e.type := t.type;
 
PARS.checklex(parser, SCAN.lxRROUND);
PARS.Next(parser)
 
END
 
END designator;
 
 
PROCEDURE ProcCall (e: PARS.EXPR; procType: PROG.TYPE_; isfloat: BOOLEAN; VAR fregs: INTEGER; parser: PARS.PARSER; pos: SCAN.POSITION; CallStat: BOOLEAN);
VAR
cconv: INTEGER;
params: INTEGER;
callconv: INTEGER;
fparams: INTEGER;
int, flt: INTEGER;
stk_par: INTEGER;
 
BEGIN
cconv := procType.call;
params := procType.params.size;
 
IF cconv IN {PROG._win64, PROG.win64} THEN
callconv := CODE.call_win64;
fparams := LSL(ORD(procType.params.getfparams(procType, 3, int, flt)), 5) + MIN(params, 4)
ELSIF cconv IN {PROG._systemv, PROG.systemv} THEN
callconv := CODE.call_sysv;
fparams := LSL(ORD(procType.params.getfparams(procType, PROG.MAXSYSVPARAM - 1, int, flt)), 5) + params;
stk_par := MAX(0, int - 6) + MAX(0, flt - 8)
ELSE
callconv := CODE.call_stack;
fparams := 0
END;
CODE.setlast(begcall);
fregs := CODE.precall(isfloat);
 
IF cconv IN {PROG._ccall16, PROG.ccall16} THEN
CODE.AddCmd(CODE.opALIGN16, params)
ELSIF cconv IN {PROG._win64, PROG.win64} THEN
CODE.AddCmd(CODE.opWIN64ALIGN16, params)
ELSIF cconv IN {PROG._systemv, PROG.systemv} THEN
CODE.AddCmd(CODE.opSYSVALIGN16, params + stk_par)
END;
CODE.setlast(endcall.prev(CODE.COMMAND));
 
IF e.obj = eIMP THEN
CODE.CallImp(e.ident.import, callconv, fparams)
ELSIF e.obj = ePROC THEN
CODE.Call(e.ident.proc.label, callconv, fparams)
ELSIF isExpr(e) THEN
deref(pos, e, CallStat, errPROC);
CODE.CallP(callconv, fparams)
END;
 
IF cconv IN {PROG._ccall16, PROG.ccall16} THEN
CODE.AddCmd(CODE.opCLEANUP, params);
CODE.AddCmd0(CODE.opPOPSP)
ELSIF cconv IN {PROG._win64, PROG.win64} THEN
CODE.AddCmd(CODE.opCLEANUP, MAX(params + params MOD 2, 4) + 1);
CODE.AddCmd0(CODE.opPOPSP)
ELSIF cconv IN {PROG._systemv, PROG.systemv} THEN
CODE.AddCmd(CODE.opCLEANUP, params + stk_par);
CODE.AddCmd0(CODE.opPOPSP)
ELSIF cconv IN {PROG._ccall, PROG.ccall} THEN
CODE.AddCmd(CODE.opCLEANUP, params)
END;
 
IF ~CallStat THEN
IF isfloat THEN
PARS.check(CODE.resf(fregs), parser, pos, 41)
ELSE
CODE.res(fregs)
END
END
END ProcCall;
 
 
PROCEDURE expression (parser: PARS.PARSER; VAR e: PARS.EXPR);
VAR
pos, pos0, pos1: SCAN.POSITION;
 
op: INTEGER;
e1: PARS.EXPR;
constant: BOOLEAN;
operator: ARITH.RELATION;
error: INTEGER;
 
 
PROCEDURE relation (sym: INTEGER): BOOLEAN;
RETURN (sym = SCAN.lxEQ) OR (sym = SCAN.lxNE) OR
(sym = SCAN.lxLT) OR (sym = SCAN.lxLE) OR
(sym = SCAN.lxGT) OR (sym = SCAN.lxGE) OR
(sym = SCAN.lxIN) OR (sym = SCAN.lxIS)
END relation;
 
 
PROCEDURE AddOperator (sym: INTEGER): BOOLEAN;
RETURN (sym = SCAN.lxPLUS) OR (sym = SCAN.lxMINUS) OR
(sym = SCAN.lxOR)
END AddOperator;
 
 
PROCEDURE MulOperator (sym: INTEGER): BOOLEAN;
RETURN (sym = SCAN.lxMUL) OR (sym = SCAN.lxSLASH) OR
(sym = SCAN.lxDIV) OR (sym = SCAN.lxMOD) OR
(sym = SCAN.lxAND)
END MulOperator;
 
 
PROCEDURE element (parser: PARS.PARSER; VAR e: PARS.EXPR);
VAR
e1, e2: PARS.EXPR;
pos: SCAN.POSITION;
range: BOOLEAN;
 
BEGIN
range := FALSE;
getpos(parser, pos);
expression(parser, e1);
PARS.check(isInt(e1), parser, pos, 76);
 
IF e1.obj = eCONST THEN
PARS.check(ARITH.range(e1.value, 0, MACHINE.target.maxSet), parser, pos, 44)
END;
 
range := parser.sym = SCAN.lxRANGE;
 
IF range THEN
NextPos(parser, pos);
expression(parser, e2);
PARS.check(isInt(e2), parser, pos, 76);
 
IF e2.obj = eCONST THEN
PARS.check(ARITH.range(e2.value, 0, MACHINE.target.maxSet), parser, pos, 44)
END
ELSE
IF e1.obj = eCONST THEN
e2 := e1
END
END;
 
e.type := PARS.program.stTypes.tSET;
 
IF (e1.obj = eCONST) & (e2.obj = eCONST) THEN
ARITH.constrSet(e.value, e1.value, e2.value);
e.obj := eCONST
ELSE
IF range THEN
IF e1.obj = eCONST THEN
CODE.AddCmd(CODE.opRSETL, ARITH.Int(e1.value))
ELSIF e2.obj = eCONST THEN
CODE.AddCmd(CODE.opRSETR, ARITH.Int(e2.value))
ELSE
CODE.AddCmd0(CODE.opRSET)
END
ELSE
CODE.AddCmd0(CODE.opRSET1)
END;
e.obj := eEXPR
END
 
END element;
 
 
PROCEDURE set (parser: PARS.PARSER; VAR e: PARS.EXPR);
VAR
e1: PARS.EXPR;
 
BEGIN
ASSERT(parser.sym = SCAN.lxLCURLY);
 
e.obj := eCONST;
e.type := PARS.program.stTypes.tSET;
ARITH.emptySet(e.value);
 
PARS.Next(parser);
IF parser.sym # SCAN.lxRCURLY THEN
element(parser, e1);
 
IF e1.obj = eCONST THEN
ARITH.opSet(e.value, e1.value, "+")
ELSE
e.obj := eEXPR
END;
 
WHILE parser.sym = SCAN.lxCOMMA DO
PARS.Next(parser);
element(parser, e1);
IF (e.obj = eCONST) & (e1.obj = eCONST) THEN
ARITH.opSet(e.value, e1.value, "+")
ELSE
IF e.obj = eCONST THEN
CODE.AddCmd(CODE.opADDSL, ARITH.Int(e.value))
ELSIF e1.obj = eCONST THEN
CODE.AddCmd(CODE.opADDSR, ARITH.Int(e1.value))
ELSE
CODE.AddCmd0(CODE.opADDS)
END;
e.obj := eEXPR
END
END;
PARS.checklex(parser, SCAN.lxRCURLY)
END;
PARS.Next(parser);
END set;
 
 
PROCEDURE factor (parser: PARS.PARSER; VAR e: PARS.EXPR);
VAR
sym: INTEGER;
pos: SCAN.POSITION;
e1: PARS.EXPR;
isfloat: BOOLEAN;
fregs: INTEGER;
 
 
PROCEDURE LoadVar (e: PARS.EXPR; parser: PARS.PARSER; pos: SCAN.POSITION);
BEGIN
IF ~(e.type.typ IN {PROG.tRECORD, PROG.tARRAY}) THEN
IF e.type.typ = PROG.tREAL THEN
PARS.check(CODE.loadf(), parser, pos, 41)
ELSE
CODE.load(e.type.size)
END
END
END LoadVar;
 
 
BEGIN
sym := parser.sym;
 
IF (sym = SCAN.lxINTEGER) OR (sym = SCAN.lxHEX) OR (sym = SCAN.lxFLOAT) OR (sym = SCAN.lxCHAR) OR (sym = SCAN.lxSTRING) THEN
e.obj := eCONST;
e.value := parser.lex.value;
e.type := PARS.program.getType(PARS.program, e.value.typ);
PARS.Next(parser)
 
ELSIF sym = SCAN.lxNIL THEN
e.obj := eCONST;
e.type := PARS.program.stTypes.tNIL;
PARS.Next(parser)
 
ELSIF (sym = SCAN.lxTRUE) OR (sym = SCAN.lxFALSE) THEN
e.obj := eCONST;
ARITH.setbool(e.value, sym = SCAN.lxTRUE);
e.type := PARS.program.stTypes.tBOOLEAN;
PARS.Next(parser)
 
ELSIF sym = SCAN.lxLCURLY THEN
set(parser, e)
 
ELSIF sym = SCAN.lxIDENT THEN
getpos(parser, pos);
 
CODE.pushBegEnd(begcall, endcall);
 
designator(parser, e);
IF isVar(e) THEN
LoadVar(e, parser, pos)
END;
IF parser.sym = SCAN.lxLROUND THEN
e1 := e;
ActualParameters(parser, e);
PARS.check(e.type # NIL, parser, pos, 59);
isfloat := e.type.typ = PROG.tREAL;
IF e1.obj IN {ePROC, eIMP} THEN
ProcCall(e1, e1.ident.type, isfloat, fregs, parser, pos, FALSE)
ELSIF isExpr(e1) THEN
ProcCall(e1, e1.type, isfloat, fregs, parser, pos, FALSE)
END
END;
CODE.popBegEnd(begcall, endcall)
 
ELSIF sym = SCAN.lxLROUND THEN
PARS.Next(parser);
expression(parser, e);
PARS.checklex(parser, SCAN.lxRROUND);
PARS.Next(parser);
IF isExpr(e) & (e.obj # eCONST) THEN
e.obj := eEXPR
END
 
ELSIF sym = SCAN.lxNOT THEN
NextPos(parser, pos);
factor(parser, e);
PARS.check(isBoolean(e), parser, pos, 72);
IF e.obj # eCONST THEN
CODE.not;
e.obj := eEXPR
ELSE
ASSERT(ARITH.neg(e.value))
END
 
ELSE
PARS.check1(FALSE, parser, 34)
END
END factor;
 
 
PROCEDURE term (parser: PARS.PARSER; VAR e: PARS.EXPR);
VAR
pos: SCAN.POSITION;
op: INTEGER;
e1: PARS.EXPR;
 
label: INTEGER;
label1: INTEGER;
 
BEGIN
factor(parser, e);
label := -1;
 
WHILE MulOperator(parser.sym) DO
op := parser.sym;
getpos(parser, pos);
PARS.Next(parser);
 
IF op = SCAN.lxAND THEN
IF ~parser.constexp THEN
 
IF label = -1 THEN
label := CODE.NewLabel()
END;
 
IF e.obj = eCONST THEN
CODE.AddCmd(CODE.opCONST, ORD(ARITH.getBool(e.value)))
END;
CODE.AddJmpCmd(CODE.opJZ, label);
CODE.drop
END
END;
 
factor(parser, e1);
 
CASE op OF
|SCAN.lxMUL:
PARS.check(isInt(e) & isInt(e1) OR isReal(e) & isReal(e1) OR isSet(e) & isSet(e1), parser, pos, 37);
IF (e.obj = eCONST) & (e1.obj = eCONST) THEN
 
CASE e.value.typ OF
|ARITH.tINTEGER: PARS.check(ARITH.opInt(e.value, e1.value, "*"), parser, pos, 39)
|ARITH.tREAL: PARS.check(ARITH.opFloat(e.value, e1.value, "*"), parser, pos, 40)
|ARITH.tSET: ARITH.opSet(e.value, e1.value, "*")
END
 
ELSE
IF isInt(e) THEN
IF e.obj = eCONST THEN
CODE.AddCmd(CODE.opMULC, ARITH.Int(e.value))
ELSIF e1.obj = eCONST THEN
CODE.AddCmd(CODE.opMULC, ARITH.Int(e1.value))
ELSE
CODE.AddCmd0(CODE.opMUL)
END
ELSIF isReal(e) THEN
IF e.obj = eCONST THEN
CODE.Float(ARITH.Float(e.value))
ELSIF e1.obj = eCONST THEN
CODE.Float(ARITH.Float(e1.value))
END;
CODE.fbinop(CODE.opMULF)
ELSIF isSet(e) THEN
IF e.obj = eCONST THEN
CODE.AddCmd(CODE.opMULSC, ARITH.Int(e.value))
ELSIF e1.obj = eCONST THEN
CODE.AddCmd(CODE.opMULSC, ARITH.Int(e1.value))
ELSE
CODE.AddCmd0(CODE.opMULS)
END
END;
e.obj := eEXPR
END
 
|SCAN.lxSLASH:
PARS.check(isReal(e) & isReal(e1) OR isSet(e) & isSet(e1), parser, pos, 37);
IF (e1.obj = eCONST) & isReal(e1) THEN
PARS.check(~ARITH.isZero(e1.value), parser, pos, 45)
END;
IF (e.obj = eCONST) & (e1.obj = eCONST) THEN
 
CASE e.value.typ OF
|ARITH.tREAL: PARS.check(ARITH.opFloat(e.value, e1.value, "/"), parser, pos, 40)
|ARITH.tSET: ARITH.opSet(e.value, e1.value, "/")
END
 
ELSE
IF isReal(e) THEN
IF e.obj = eCONST THEN
CODE.Float(ARITH.Float(e.value));
CODE.fbinop(CODE.opDIVFI)
ELSIF e1.obj = eCONST THEN
CODE.Float(ARITH.Float(e1.value));
CODE.fbinop(CODE.opDIVF)
ELSE
CODE.fbinop(CODE.opDIVF)
END
ELSIF isSet(e) THEN
IF e.obj = eCONST THEN
CODE.AddCmd(CODE.opDIVSC, ARITH.Int(e.value))
ELSIF e1.obj = eCONST THEN
CODE.AddCmd(CODE.opDIVSC, ARITH.Int(e1.value))
ELSE
CODE.AddCmd0(CODE.opDIVS)
END
END;
e.obj := eEXPR
END
 
|SCAN.lxDIV, SCAN.lxMOD:
PARS.check(isInt(e) & isInt(e1), parser, pos, 37);
IF e1.obj = eCONST THEN
PARS.check(~ARITH.isZero(e1.value), parser, pos, 46)
END;
IF (e.obj = eCONST) & (e1.obj = eCONST) THEN
 
IF op = SCAN.lxDIV THEN
PARS.check(ARITH.opInt(e.value, e1.value, "D"), parser, pos, 39)
ELSE
ASSERT(ARITH.opInt(e.value, e1.value, "M"))
END
 
ELSE
IF e1.obj # eCONST THEN
label1 := CODE.NewLabel();
CODE.AddJmpCmd(CODE.opJNZ, label1)
END;
IF e.obj = eCONST THEN
CODE.OnError(pos.line, errDIV);
CODE.SetLabel(label1);
CODE.AddCmd(CODE.opDIVL + ORD(op = SCAN.lxMOD), ARITH.Int(e.value))
ELSIF e1.obj = eCONST THEN
CODE.AddCmd(CODE.opDIVR + ORD(op = SCAN.lxMOD), ARITH.Int(e1.value))
ELSE
CODE.OnError(pos.line, errDIV);
CODE.SetLabel(label1);
CODE.AddCmd0(CODE.opDIV + ORD(op = SCAN.lxMOD))
END;
e.obj := eEXPR
END
 
|SCAN.lxAND:
PARS.check(isBoolean(e) & isBoolean(e1), parser, pos, 37);
 
IF (e.obj = eCONST) & (e1.obj = eCONST) THEN
ARITH.opBoolean(e.value, e1.value, "&")
ELSE
e.obj := eEXPR;
IF e1.obj = eCONST THEN
CODE.AddCmd(CODE.opCONST, ORD(ARITH.getBool(e1.value)))
END
END
 
END
END;
 
IF label # -1 THEN
CODE.SetLabel(label)
END
END term;
 
 
PROCEDURE SimpleExpression (parser: PARS.PARSER; VAR e: PARS.EXPR);
VAR
pos: SCAN.POSITION;
op: INTEGER;
e1: PARS.EXPR;
 
plus, minus: BOOLEAN;
 
label: INTEGER;
 
BEGIN
plus := parser.sym = SCAN.lxPLUS;
minus := parser.sym = SCAN.lxMINUS;
 
IF plus OR minus THEN
getpos(parser, pos);
PARS.Next(parser)
END;
 
term(parser, e);
 
IF plus OR minus THEN
PARS.check(isInt(e) OR isReal(e) OR isSet(e), parser, pos, 36);
 
IF minus & (e.obj = eCONST) THEN
PARS.check(ARITH.neg(e.value), parser, pos, 39)
END;
 
IF e.obj # eCONST THEN
IF minus THEN
IF isInt(e) THEN
CODE.AddCmd0(CODE.opUMINUS)
ELSIF isReal(e) THEN
CODE.AddCmd0(CODE.opUMINF)
ELSIF isSet(e) THEN
CODE.AddCmd0(CODE.opUMINS)
END
END;
e.obj := eEXPR
END
END;
 
label := -1;
 
WHILE AddOperator(parser.sym) DO
 
op := parser.sym;
getpos(parser, pos);
PARS.Next(parser);
 
IF op = SCAN.lxOR THEN
 
IF ~parser.constexp THEN
 
IF label = -1 THEN
label := CODE.NewLabel()
END;
 
IF e.obj = eCONST THEN
CODE.AddCmd(CODE.opCONST, ORD(ARITH.getBool(e.value)))
END;
CODE.AddJmpCmd(CODE.opJNZ, label);
CODE.drop
END
 
END;
 
term(parser, e1);
 
CASE op OF
|SCAN.lxPLUS, SCAN.lxMINUS:
 
IF op = SCAN.lxPLUS THEN
op := ORD("+")
ELSE
op := ORD("-")
END;
 
PARS.check(isInt(e) & isInt(e1) OR isReal(e) & isReal(e1) OR isSet(e) & isSet(e1), parser, pos, 37);
IF (e.obj = eCONST) & (e1.obj = eCONST) THEN
 
CASE e.value.typ OF
|ARITH.tINTEGER: PARS.check(ARITH.opInt(e.value, e1.value, CHR(op)), parser, pos, 39)
|ARITH.tREAL: PARS.check(ARITH.opFloat(e.value, e1.value, CHR(op)), parser, pos, 40)
|ARITH.tSET: ARITH.opSet(e.value, e1.value, CHR(op))
END
 
ELSE
IF isInt(e) THEN
IF e.obj = eCONST THEN
CODE.AddCmd(CODE.opADDL + ORD(op = ORD("-")), ARITH.Int(e.value))
ELSIF e1.obj = eCONST THEN
CODE.AddCmd(CODE.opADDR + ORD(op = ORD("-")), ARITH.Int(e1.value))
ELSE
CODE.AddCmd0(CODE.opADD + ORD(op = ORD("-")))
END
ELSIF isReal(e) THEN
IF e.obj = eCONST THEN
CODE.Float(ARITH.Float(e.value));
CODE.fbinop(CODE.opADDFI + ORD(op = ORD("-")))
ELSIF e1.obj = eCONST THEN
CODE.Float(ARITH.Float(e1.value));
CODE.fbinop(CODE.opADDF + ORD(op = ORD("-")))
ELSE
CODE.fbinop(CODE.opADDF + ORD(op = ORD("-")))
END
ELSIF isSet(e) THEN
IF e.obj = eCONST THEN
CODE.AddCmd(CODE.opADDSL + ORD(op = ORD("-")), ARITH.Int(e.value))
ELSIF e1.obj = eCONST THEN
CODE.AddCmd(CODE.opADDSR + ORD(op = ORD("-")), ARITH.Int(e1.value))
ELSE
CODE.AddCmd0(CODE.opADDS + ORD(op = ORD("-")))
END
END;
e.obj := eEXPR
END
 
|SCAN.lxOR:
PARS.check(isBoolean(e) & isBoolean(e1), parser, pos, 37);
 
IF (e.obj = eCONST) & (e1.obj = eCONST) THEN
ARITH.opBoolean(e.value, e1.value, "|")
ELSE
e.obj := eEXPR;
IF e1.obj = eCONST THEN
CODE.AddCmd(CODE.opCONST, ORD(ARITH.getBool(e1.value)))
END
END
 
END
END;
 
IF label # -1 THEN
CODE.SetLabel(label)
END
 
END SimpleExpression;
 
 
PROCEDURE cmpcode (op: INTEGER): INTEGER;
VAR
res: INTEGER;
BEGIN
CASE op OF
|SCAN.lxEQ: res := 0
|SCAN.lxNE: res := 1
|SCAN.lxLT: res := 2
|SCAN.lxLE: res := 3
|SCAN.lxGT: res := 4
|SCAN.lxGE: res := 5
END
 
RETURN res
END cmpcode;
 
 
PROCEDURE BoolCmp (eq, val: BOOLEAN);
BEGIN
IF eq = val THEN
CODE.AddCmd0(CODE.opNER)
ELSE
CODE.AddCmd0(CODE.opEQR)
END
END BoolCmp;
 
 
PROCEDURE strcmp (VAR e, e1: PARS.EXPR; op: INTEGER): BOOLEAN;
VAR
res: BOOLEAN;
 
BEGIN
 
res := TRUE;
 
IF isString(e) & isCharArray(e1) THEN
CODE.AddCmd(CODE.opSADR, String(e));
CODE.AddCmd(CODE.opCONST, strlen(e) + 1);
CODE.AddCmd0(CODE.opEQS2 + cmpcode(op))
 
ELSIF isString(e) & isCharArrayW(e1) THEN
CODE.AddCmd(CODE.opSADR, StringW(e));
CODE.AddCmd(CODE.opCONST, utf8strlen(e) + 1);
CODE.AddCmd0(CODE.opEQSW2 + cmpcode(op))
 
ELSIF isStringW(e) & isCharArrayW(e1) THEN
CODE.AddCmd(CODE.opSADR, StringW(e));
CODE.AddCmd(CODE.opCONST, utf8strlen(e) + 1);
CODE.AddCmd0(CODE.opEQSW2 + cmpcode(op))
 
ELSIF isCharArray(e) & isString(e1) THEN
CODE.AddCmd(CODE.opSADR, String(e1));
CODE.AddCmd(CODE.opCONST, strlen(e1) + 1);
CODE.AddCmd0(CODE.opEQS + cmpcode(op))
 
ELSIF isCharArrayW(e) & isString(e1) THEN
CODE.AddCmd(CODE.opSADR, StringW(e1));
CODE.AddCmd(CODE.opCONST, utf8strlen(e1) + 1);
CODE.AddCmd0(CODE.opEQSW + cmpcode(op))
 
ELSIF isCharArrayW(e) & isStringW(e1) THEN
CODE.AddCmd(CODE.opSADR, StringW(e1));
CODE.AddCmd(CODE.opCONST, utf8strlen(e1) + 1);
CODE.AddCmd0(CODE.opEQSW + cmpcode(op))
 
ELSIF isCharArrayW(e) & isCharArrayW(e1) THEN
CODE.AddCmd0(CODE.opEQSW + cmpcode(op))
 
ELSIF isCharArray(e) & isCharArray(e1) THEN
CODE.AddCmd0(CODE.opEQS + cmpcode(op))
 
ELSIF isString(e) & isString(e1) THEN
PARS.strcmp(e.value, e1.value, op)
 
ELSE
res := FALSE
 
END
 
RETURN res
END strcmp;
 
 
BEGIN
getpos(parser, pos0);
SimpleExpression(parser, e);
IF relation(parser.sym) THEN
IF (isCharArray(e) OR isCharArrayW(e)) & (e.type.length # 0) THEN
CODE.AddCmd(CODE.opCONST, e.type.length)
END;
op := parser.sym;
getpos(parser, pos);
PARS.Next(parser);
 
pos1 := parser.lex.pos;
SimpleExpression(parser, e1);
 
IF (isCharArray(e1) OR isCharArrayW(e1)) & (e1.type.length # 0) THEN
CODE.AddCmd(CODE.opCONST, e1.type.length)
END;
 
constant := (e.obj = eCONST) & (e1.obj = eCONST);
 
CASE op OF
|SCAN.lxEQ: operator := "="
|SCAN.lxNE: operator := "#"
|SCAN.lxLT: operator := "<"
|SCAN.lxLE: operator := "<="
|SCAN.lxGT: operator := ">"
|SCAN.lxGE: operator := ">="
|SCAN.lxIN: operator := "IN"
|SCAN.lxIS: operator := ""
END;
 
error := 0;
 
CASE op OF
|SCAN.lxEQ, SCAN.lxNE:
 
IF isInt(e) & isInt(e1) OR isSet(e) & isSet(e1) OR isChar(e) & isChar(e1) OR isCharW(e) & isCharW(e1) OR
isCharW(e) & isChar(e1) & (e1.obj = eCONST) OR isCharW(e1) & isChar(e) & (e.obj = eCONST) OR
isCharW(e1) & (e1.obj = eCONST) & isChar(e) & (e.obj = eCONST) OR
isCharW(e) & (e.obj = eCONST) & isChar(e1) & (e1.obj = eCONST) OR
isPtr(e) & isPtr(e1) & (PROG.isBaseOf(e.type, e1.type) OR PROG.isBaseOf(e1.type, e.type)) THEN
IF constant THEN
ARITH.relation(e.value, e1.value, operator, error)
ELSE
IF e.obj = eCONST THEN
CODE.AddCmd(CODE.opEQ + cmpcode(op) + 6, ARITH.Int(e.value))
ELSIF e1.obj = eCONST THEN
CODE.AddCmd(CODE.opEQ + cmpcode(op) + 12, ARITH.Int(e1.value))
ELSE
CODE.AddCmd0(CODE.opEQ + cmpcode(op))
END
END
 
ELSIF isStringW1(e) & isCharW(e1) THEN
CODE.AddCmd(CODE.opEQ + cmpcode(op) + 6, StrToWChar(e.value.string(SCAN.IDENT).s))
 
ELSIF isStringW1(e1) & isCharW(e) THEN
CODE.AddCmd(CODE.opEQ + cmpcode(op) + 12, StrToWChar(e1.value.string(SCAN.IDENT).s))
 
ELSIF isBoolean(e) & isBoolean(e1) THEN
IF constant THEN
ARITH.relation(e.value, e1.value, operator, error)
ELSE
IF e.obj = eCONST THEN
BoolCmp(op = SCAN.lxEQ, ARITH.Int(e.value) # 0)
ELSIF e1.obj = eCONST THEN
BoolCmp(op = SCAN.lxEQ, ARITH.Int(e1.value) # 0)
ELSE
IF op = SCAN.lxEQ THEN
CODE.AddCmd0(CODE.opEQB)
ELSE
CODE.AddCmd0(CODE.opNEB)
END
END
END
 
ELSIF isReal(e) & isReal(e1) THEN
IF constant THEN
ARITH.relation(e.value, e1.value, operator, error)
ELSE
IF e.obj = eCONST THEN
CODE.Float(ARITH.Float(e.value));
CODE.fcmp(CODE.opEQF + cmpcode(op) + 6)
ELSIF e1.obj = eCONST THEN
CODE.Float(ARITH.Float(e1.value));
CODE.fcmp(CODE.opEQF + cmpcode(op))
ELSE
CODE.fcmp(CODE.opEQF + cmpcode(op))
END
END
 
ELSIF (isStringW(e) OR isCharArrayX(e)) & (isStringW(e1) OR isCharArrayX(e1)) THEN
IF ~strcmp(e, e1, op) THEN
PARS.error(parser, pos, 37)
END
 
ELSIF isPtr(e) & isNil(e1) OR isNil(e) & isPtr(e1) THEN
CODE.AddCmd0(CODE.opEQ + cmpcode(op) + 6)
 
ELSIF isProc(e) & isNil(e1) THEN
IF e.obj IN {ePROC, eIMP} THEN
PARS.check(e.ident.global, parser, pos0, 85);
constant := TRUE;
e.obj := eCONST;
ARITH.setbool(e.value, op = SCAN.lxNE)
ELSE
CODE.AddCmd0(CODE.opEQ + cmpcode(op) + 6)
END
 
ELSIF isNil(e) & isProc(e1) THEN
IF e1.obj IN {ePROC, eIMP} THEN
PARS.check(e1.ident.global, parser, pos1, 85);
constant := TRUE;
e.obj := eCONST;
ARITH.setbool(e.value, op = SCAN.lxNE)
ELSE
CODE.AddCmd0(CODE.opEQ + cmpcode(op) + 6)
END
 
ELSIF isProc(e) & isProc(e1) & PROG.isTypeEq(e.type, e1.type) THEN
IF e.obj = ePROC THEN
PARS.check(e.ident.global, parser, pos0, 85)
END;
IF e1.obj = ePROC THEN
PARS.check(e1.ident.global, parser, pos1, 85)
END;
IF (e.obj IN {ePROC, eIMP}) & (e1.obj IN {ePROC, eIMP}) THEN
constant := TRUE;
e.obj := eCONST;
IF op = SCAN.lxEQ THEN
ARITH.setbool(e.value, e.ident = e1.ident)
ELSE
ARITH.setbool(e.value, e.ident # e1.ident)
END
ELSIF e.obj = ePROC THEN
CODE.ProcCmp(e.ident.proc.label, cmpcode(op) = 0)
ELSIF e1.obj = ePROC THEN
CODE.ProcCmp(e1.ident.proc.label, cmpcode(op) = 0)
ELSIF e.obj = eIMP THEN
CODE.ProcImpCmp(e.ident.import, cmpcode(op) = 0)
ELSIF e1.obj = eIMP THEN
CODE.ProcImpCmp(e1.ident.import, cmpcode(op) = 0)
ELSE
CODE.AddCmd0(CODE.opEQ + cmpcode(op))
END
 
ELSIF isNil(e) & isNil(e1) THEN
constant := TRUE;
e.obj := eCONST;
ARITH.setbool(e.value, op = SCAN.lxEQ)
 
ELSE
PARS.error(parser, pos, 37)
END
 
|SCAN.lxLT, SCAN.lxLE, SCAN.lxGT, SCAN.lxGE:
IF isInt(e) & isInt(e1) OR isChar(e) & isChar(e1) OR isCharW(e) & isCharW(e1) OR
isCharW(e) & isChar(e1) & (e1.obj = eCONST) OR isCharW(e1) & isChar(e) & (e.obj = eCONST) OR
isCharW(e1) & (e1.obj = eCONST) & isChar(e) & (e.obj = eCONST) OR
isCharW(e) & (e.obj = eCONST) & isChar(e1) & (e1.obj = eCONST) THEN
 
IF constant THEN
ARITH.relation(e.value, e1.value, operator, error)
ELSE
IF e.obj = eCONST THEN
CODE.AddCmd(CODE.opEQ + cmpcode(op) + 6, ARITH.Int(e.value))
ELSIF e1.obj = eCONST THEN
CODE.AddCmd(CODE.opEQ + cmpcode(op) + 12, ARITH.Int(e1.value))
ELSE
CODE.AddCmd0(CODE.opEQ + cmpcode(op))
END
END
 
ELSIF isStringW1(e) & isCharW(e1) THEN
CODE.AddCmd(CODE.opEQ + cmpcode(op) + 6, StrToWChar(e.value.string(SCAN.IDENT).s))
 
ELSIF isStringW1(e1) & isCharW(e) THEN
CODE.AddCmd(CODE.opEQ + cmpcode(op) + 12, StrToWChar(e1.value.string(SCAN.IDENT).s))
 
ELSIF isReal(e) & isReal(e1) THEN
IF constant THEN
ARITH.relation(e.value, e1.value, operator, error)
ELSE
IF e.obj = eCONST THEN
CODE.Float(ARITH.Float(e.value));
CODE.fcmp(CODE.opEQF + cmpcode(op) + 6)
ELSIF e1.obj = eCONST THEN
CODE.Float(ARITH.Float(e1.value));
CODE.fcmp(CODE.opEQF + cmpcode(op))
ELSE
CODE.fcmp(CODE.opEQF + cmpcode(op))
END
END
 
ELSIF (isStringW(e) OR isCharArrayX(e)) & (isStringW(e1) OR isCharArrayX(e1)) THEN
IF ~strcmp(e, e1, op) THEN
PARS.error(parser, pos, 37)
END
 
ELSE
PARS.error(parser, pos, 37)
END
 
|SCAN.lxIN:
PARS.check(isInt(e) & isSet(e1), parser, pos, 37);
IF e.obj = eCONST THEN
PARS.check(ARITH.range(e.value, 0, MACHINE.target.maxSet), parser, pos0, 56)
END;
IF constant THEN
ARITH.relation(e.value, e1.value, operator, error)
ELSE
IF e.obj = eCONST THEN
CODE.AddCmd(CODE.opINL, ARITH.Int(e.value))
ELSIF e1.obj = eCONST THEN
CODE.AddCmd(CODE.opINR, ARITH.Int(e1.value))
ELSE
CODE.AddCmd0(CODE.opIN)
END
END
 
|SCAN.lxIS:
PARS.check(isExpr(e) & (e.type.typ IN {PROG.tRECORD, PROG.tPOINTER}), parser, pos, 73);
IF e.type.typ = PROG.tRECORD THEN
PARS.check(e.obj = eVREC, parser, pos0, 78)
END;
PARS.check(e1.obj = eTYPE, parser, pos1, 79);
 
IF e.type.typ = PROG.tRECORD THEN
PARS.check(e1.type.typ = PROG.tRECORD, parser, pos1, 80);
IF e.ident = NIL THEN
CODE.TypeCheck(e1.type.num)
ELSE
CODE.AddCmd(CODE.opVADR, e.ident.offset - 1);
CODE.TypeCheckRec(e1.type.num)
END
ELSE
PARS.check(e1.type.typ = PROG.tPOINTER, parser, pos1, 81);
CODE.TypeCheck(e1.type.base.num)
END;
 
PARS.check(PROG.isBaseOf(e.type, e1.type), parser, pos1, 82)
 
END;
 
ASSERT(error = 0);
 
e.type := PARS.program.stTypes.tBOOLEAN;
 
IF ~constant THEN
e.obj := eEXPR
END
 
END
END expression;
 
 
PROCEDURE ElementaryStatement (parser: PARS.PARSER);
VAR
e, e1: PARS.EXPR;
pos: SCAN.POSITION;
line: INTEGER;
call: BOOLEAN;
fregs: INTEGER;
 
BEGIN
getpos(parser, pos);
 
CODE.pushBegEnd(begcall, endcall);
 
designator(parser, e);
 
IF parser.sym = SCAN.lxASSIGN THEN
line := parser.lex.pos.line;
PARS.check(isVar(e), parser, pos, 93);
PARS.check(~e.readOnly, parser, pos, 94);
 
CODE.setlast(begcall);
 
NextPos(parser, pos);
expression(parser, e1);
 
CODE.setlast(endcall.prev(CODE.COMMAND));
 
PARS.check(assign(e1, e.type, line), parser, pos, 91);
IF e1.obj = ePROC THEN
PARS.check(e1.ident.global, parser, pos, 85)
END;
call := FALSE
ELSIF parser.sym = SCAN.lxEQ THEN
PARS.check1(FALSE, parser, 96)
ELSIF parser.sym = SCAN.lxLROUND THEN
e1 := e;
ActualParameters(parser, e1);
PARS.check((e1.type = NIL) OR ODD(e.type.call), parser, pos, 92);
call := TRUE
ELSE
PARS.check(isProc(e), parser, pos, 86);
PARS.check((e.type.base = NIL) OR ODD(e.type.call), parser, pos, 92);
PARS.check1(e.type.params.first = NIL, parser, 64);
call := TRUE
END;
 
IF call THEN
IF e.obj IN {ePROC, eIMP} THEN
ProcCall(e, e.ident.type, FALSE, fregs, parser, pos, TRUE)
ELSIF isExpr(e) THEN
ProcCall(e, e.type, FALSE, fregs, parser, pos, TRUE)
END
END;
 
CODE.popBegEnd(begcall, endcall)
END ElementaryStatement;
 
 
PROCEDURE IfStatement (parser: PARS.PARSER; if: BOOLEAN);
VAR
e: PARS.EXPR;
pos: SCAN.POSITION;
 
label, L: INTEGER;
 
BEGIN
L := CODE.NewLabel();
 
IF ~if THEN
CODE.AddCmd0(CODE.opLOOP);
CODE.SetLabel(L)
END;
 
REPEAT
NextPos(parser, pos);
 
label := CODE.NewLabel();
 
expression(parser, e);
PARS.check(isBoolean(e), parser, pos, 72);
 
IF e.obj = eCONST THEN
IF ~ARITH.getBool(e.value) THEN
CODE.AddJmpCmd(CODE.opJMP, label)
END
ELSE
CODE.AddJmpCmd(CODE.opJNE, label)
END;
 
IF if THEN
PARS.checklex(parser, SCAN.lxTHEN)
ELSE
PARS.checklex(parser, SCAN.lxDO)
END;
 
PARS.Next(parser);
parser.StatSeq(parser);
 
CODE.AddJmpCmd(CODE.opJMP, L);
CODE.SetLabel(label)
 
UNTIL parser.sym # SCAN.lxELSIF;
 
IF if THEN
IF parser.sym = SCAN.lxELSE THEN
PARS.Next(parser);
parser.StatSeq(parser)
END;
CODE.SetLabel(L)
END;
 
PARS.checklex(parser, SCAN.lxEND);
 
IF ~if THEN
CODE.AddCmd0(CODE.opENDLOOP)
END;
 
PARS.Next(parser)
END IfStatement;
 
 
PROCEDURE RepeatStatement (parser: PARS.PARSER);
VAR
e: PARS.EXPR;
pos: SCAN.POSITION;
label: INTEGER;
 
BEGIN
CODE.AddCmd0(CODE.opLOOP);
 
label := CODE.NewLabel();
CODE.SetLabel(label);
 
PARS.Next(parser);
parser.StatSeq(parser);
PARS.checklex(parser, SCAN.lxUNTIL);
NextPos(parser, pos);
expression(parser, e);
PARS.check(isBoolean(e), parser, pos, 72);
 
IF e.obj = eCONST THEN
IF ~ARITH.getBool(e.value) THEN
CODE.AddJmpCmd(CODE.opJMP, label)
END
ELSE
CODE.AddJmpCmd(CODE.opJNE, label)
END;
 
CODE.AddCmd0(CODE.opENDLOOP)
END RepeatStatement;
 
 
PROCEDURE LabelCmp (a, b: AVL.DATA): INTEGER;
VAR
La, Ra, Lb, Rb, res: INTEGER;
 
BEGIN
La := a(CASE_LABEL).range.a;
Ra := a(CASE_LABEL).range.b;
Lb := b(CASE_LABEL).range.a;
Rb := b(CASE_LABEL).range.b;
IF (Ra < Lb) OR (La > Rb) THEN
res := ORD(La > Lb) - ORD(La < Lb)
ELSE
res := 0
END
 
RETURN res
END LabelCmp;
 
 
PROCEDURE DestroyLabel (VAR label: AVL.DATA);
BEGIN
C.push(CaseLabels, label);
label := NIL
END DestroyLabel;
 
 
PROCEDURE NewVariant (label: INTEGER; cmd: CODE.COMMAND): CASE_VARIANT;
VAR
res: CASE_VARIANT;
citem: C.ITEM;
 
BEGIN
citem := C.pop(CaseVar);
IF citem = NIL THEN
NEW(res)
ELSE
res := citem(CASE_VARIANT)
END;
 
res.label := label;
res.cmd := cmd;
res.processed := FALSE
 
RETURN res
END NewVariant;
 
 
PROCEDURE CaseStatement (parser: PARS.PARSER);
VAR
e: PARS.EXPR;
pos: SCAN.POSITION;
 
 
PROCEDURE isRecPtr (caseExpr: PARS.EXPR): BOOLEAN;
RETURN isRec(caseExpr) OR isPtr(caseExpr)
END isRecPtr;
 
 
PROCEDURE Label (parser: PARS.PARSER; caseExpr: PARS.EXPR; VAR type: PROG.TYPE_): INTEGER;
VAR
a: INTEGER;
label: PARS.EXPR;
pos: SCAN.POSITION;
value: ARITH.VALUE;
 
BEGIN
getpos(parser, pos);
type := NIL;
 
IF isChar(caseExpr) THEN
PARS.ConstExpression(parser, value);
PARS.check(value.typ = ARITH.tCHAR, parser, pos, 99);
a := ARITH.getInt(value)
ELSIF isCharW(caseExpr) THEN
PARS.ConstExpression(parser, value);
IF (value.typ = ARITH.tSTRING) & (_length(value.string(SCAN.IDENT).s) = 1) & (LENGTH(value.string(SCAN.IDENT).s) > 1) THEN
ASSERT(ARITH.setInt(value, StrToWChar(value.string(SCAN.IDENT).s)))
ELSE
PARS.check(value.typ IN {ARITH.tWCHAR, ARITH.tCHAR}, parser, pos, 99)
END;
a := ARITH.getInt(value)
ELSIF isInt(caseExpr) THEN
PARS.ConstExpression(parser, value);
PARS.check(value.typ = ARITH.tINTEGER, parser, pos, 99);
a := ARITH.getInt(value)
ELSIF isRecPtr(caseExpr) THEN
qualident(parser, label);
PARS.check(label.obj = eTYPE, parser, pos, 79);
PARS.check(PROG.isBaseOf(caseExpr.type, label.type), parser, pos, 99);
IF isRec(caseExpr) THEN
a := label.type.num
ELSE
a := label.type.base.num
END;
type := label.type
END
 
RETURN a
END Label;
 
 
PROCEDURE CheckType (node: AVL.NODE; type: PROG.TYPE_; parser: PARS.PARSER; pos: SCAN.POSITION);
BEGIN
IF node # NIL THEN
PARS.check(~(PROG.isBaseOf(node.data(CASE_LABEL).type, type) OR PROG.isBaseOf(type, node.data(CASE_LABEL).type)), parser, pos, 100);
CheckType(node.left, type, parser, pos);
CheckType(node.right, type, parser, pos)
END
END CheckType;
 
 
PROCEDURE LabelRange (parser: PARS.PARSER; caseExpr: PARS.EXPR; VAR tree: AVL.NODE; variant: INTEGER): AVL.NODE;
VAR
label: CASE_LABEL;
citem: C.ITEM;
pos, pos1: SCAN.POSITION;
node: AVL.NODE;
newnode: BOOLEAN;
range: RANGE;
 
BEGIN
citem := C.pop(CaseLabels);
IF citem = NIL THEN
NEW(label)
ELSE
label := citem(CASE_LABEL)
END;
 
label.variant := variant;
label.self := CODE.NewLabel();
 
getpos(parser, pos1);
range.a := Label(parser, caseExpr, label.type);
 
IF parser.sym = SCAN.lxRANGE THEN
PARS.check1(~isRecPtr(caseExpr), parser, 53);
NextPos(parser, pos);
range.b := Label(parser, caseExpr, label.type);
PARS.check(range.a <= range.b, parser, pos, 103)
ELSE
range.b := range.a
END;
 
label.range := range;
 
IF isRecPtr(caseExpr) THEN
CheckType(tree, label.type, parser, pos1)
END;
tree := AVL.insert(tree, label, LabelCmp, newnode, node);
PARS.check(newnode, parser, pos1, 100)
 
RETURN node
 
END LabelRange;
 
 
PROCEDURE CaseLabelList (parser: PARS.PARSER; caseExpr: PARS.EXPR; VAR tree: AVL.NODE; variant: INTEGER): AVL.NODE;
VAR
exit: BOOLEAN;
res: AVL.NODE;
 
BEGIN
exit := FALSE;
REPEAT
res := LabelRange(parser, caseExpr, tree, variant);
IF parser.sym = SCAN.lxCOMMA THEN
PARS.check1(~isRecPtr(caseExpr), parser, 53);
PARS.Next(parser)
ELSE
exit := TRUE
END
UNTIL exit
 
RETURN res
END CaseLabelList;
 
 
PROCEDURE case (parser: PARS.PARSER; caseExpr: PARS.EXPR; VAR tree: AVL.NODE; end: INTEGER);
VAR
sym: INTEGER;
t: PROG.TYPE_;
variant: INTEGER;
node: AVL.NODE;
last: CODE.COMMAND;
 
BEGIN
sym := parser.sym;
IF sym # SCAN.lxBAR THEN
variant := CODE.NewLabel();
node := CaseLabelList(parser, caseExpr, tree, variant);
PARS.checklex(parser, SCAN.lxCOLON);
PARS.Next(parser);
IF isRecPtr(caseExpr) THEN
t := caseExpr.type;
caseExpr.ident.type := node.data(CASE_LABEL).type
END;
 
last := CODE.getlast();
CODE.SetLabel(variant);
 
IF ~isRecPtr(caseExpr) THEN
LISTS.push(CaseVariants, NewVariant(variant, last))
END;
 
parser.StatSeq(parser);
CODE.AddJmpCmd(CODE.opJMP, end);
 
IF isRecPtr(caseExpr) THEN
caseExpr.ident.type := t
END
END
END case;
 
 
PROCEDURE Table (node: AVL.NODE; else: INTEGER);
VAR
L, R: INTEGER;
range: RANGE;
left, right: AVL.NODE;
last: CODE.COMMAND;
v: CASE_VARIANT;
 
BEGIN
IF node # NIL THEN
 
range := node.data(CASE_LABEL).range;
 
left := node.left;
IF left # NIL THEN
L := left.data(CASE_LABEL).self
ELSE
L := else
END;
 
right := node.right;
IF right # NIL THEN
R := right.data(CASE_LABEL).self
ELSE
R := else
END;
 
last := CODE.getlast();
 
v := CaseVariants.last(CASE_VARIANT);
WHILE (v # NIL) & (v.label # 0) & (v.label # node.data(CASE_LABEL).variant) DO
v := v.prev(CASE_VARIANT)
END;
 
ASSERT((v # NIL) & (v.label # 0));
CODE.setlast(v.cmd);
 
CODE.SetLabel(node.data(CASE_LABEL).self);
CODE.case(range.a, range.b, L, R);
IF v.processed THEN
CODE.AddJmpCmd(CODE.opJMP, node.data(CASE_LABEL).variant)
END;
v.processed := TRUE;
 
CODE.setlast(last);
 
Table(left, else);
Table(right, else)
END
END Table;
 
 
PROCEDURE TableT (node: AVL.NODE);
BEGIN
IF node # NIL THEN
CODE.caset(node.data(CASE_LABEL).range.a, node.data(CASE_LABEL).variant);
 
TableT(node.left);
TableT(node.right)
END
END TableT;
 
 
PROCEDURE ParseCase (parser: PARS.PARSER; e: PARS.EXPR; pos: SCAN.POSITION);
VAR
table, end, else: INTEGER;
tree: AVL.NODE;
item: LISTS.ITEM;
 
BEGIN
LISTS.push(CaseVariants, NewVariant(0, NIL));
end := CODE.NewLabel();
else := CODE.NewLabel();
table := CODE.NewLabel();
CODE.AddCmd(CODE.opSWITCH, ORD(isRecPtr(e)));
CODE.AddJmpCmd(CODE.opJMP, table);
 
tree := NIL;
 
case(parser, e, tree, end);
WHILE parser.sym = SCAN.lxBAR DO
PARS.Next(parser);
case(parser, e, tree, end)
END;
 
CODE.SetLabel(else);
IF parser.sym = SCAN.lxELSE THEN
PARS.Next(parser);
parser.StatSeq(parser);
CODE.AddJmpCmd(CODE.opJMP, end)
ELSE
CODE.OnError(pos.line, errCASE)
END;
 
PARS.checklex(parser, SCAN.lxEND);
PARS.Next(parser);
 
IF isRecPtr(e) THEN
CODE.SetLabel(table);
TableT(tree);
CODE.AddJmpCmd(CODE.opJMP, else)
ELSE
tree.data(CASE_LABEL).self := table;
Table(tree, else)
END;
 
AVL.destroy(tree, DestroyLabel);
CODE.SetLabel(end);
CODE.AddCmd0(CODE.opENDSW);
 
REPEAT
item := LISTS.pop(CaseVariants);
C.push(CaseVar, item)
UNTIL item(CASE_VARIANT).cmd = NIL
 
END ParseCase;
 
 
BEGIN
NextPos(parser, pos);
expression(parser, e);
PARS.check(isInt(e) OR isChar(e) OR isCharW(e) OR isPtr(e) OR isRec(e), parser, pos, 95);
IF isRecPtr(e) THEN
PARS.check(isVar(e), parser, pos, 93);
PARS.check(e.ident # NIL, parser, pos, 106)
END;
IF isRec(e) THEN
PARS.check(e.obj = eVREC, parser, pos, 78)
END;
 
IF e.obj = eCONST THEN
LoadConst(e)
ELSIF isRec(e) THEN
CODE.drop;
CODE.AddCmd(CODE.opLADR, e.ident.offset - 1);
CODE.load(PARS.program.target.word)
ELSIF isPtr(e) THEN
deref(pos, e, FALSE, errPTR);
CODE.AddCmd(CODE.opSUBR, PARS.program.target.word);
CODE.load(PARS.program.target.word)
END;
 
PARS.checklex(parser, SCAN.lxOF);
PARS.Next(parser);
ParseCase(parser, e, pos)
END CaseStatement;
 
 
PROCEDURE ForStatement (parser: PARS.PARSER);
VAR
e: PARS.EXPR;
pos: SCAN.POSITION;
step: ARITH.VALUE;
st: INTEGER;
ident: PROG.IDENT;
offset: INTEGER;
L1, L2: INTEGER;
 
BEGIN
CODE.AddCmd0(CODE.opLOOP);
 
L1 := CODE.NewLabel();
L2 := CODE.NewLabel();
 
PARS.ExpectSym(parser, SCAN.lxIDENT);
ident := parser.unit.idents.get(parser.unit, parser.lex.ident, TRUE);
PARS.check1(ident # NIL, parser, 48);
PARS.check1(ident.typ = PROG.idVAR, parser, 93);
PARS.check1(ident.type.typ = PROG.tINTEGER, parser, 97);
PARS.ExpectSym(parser, SCAN.lxASSIGN);
NextPos(parser, pos);
expression(parser, e);
PARS.check(isInt(e), parser, pos, 76);
 
offset := PROG.getOffset(PARS.program, ident);
 
IF ident.global THEN
CODE.AddCmd(CODE.opGADR, offset)
ELSE
CODE.AddCmd(CODE.opLADR, -offset)
END;
 
IF e.obj = eCONST THEN
CODE.AddCmd(CODE.opSAVEC, ARITH.Int(e.value))
ELSE
CODE.AddCmd0(CODE.opSAVE)
END;
 
CODE.SetLabel(L1);
 
IF ident.global THEN
CODE.AddCmd(CODE.opGADR, offset)
ELSE
CODE.AddCmd(CODE.opLADR, -offset)
END;
CODE.load(ident.type.size);
 
PARS.checklex(parser, SCAN.lxTO);
NextPos(parser, pos);
expression(parser, e);
PARS.check(isInt(e), parser, pos, 76);
 
IF parser.sym = SCAN.lxBY THEN
NextPos(parser, pos);
PARS.ConstExpression(parser, step);
PARS.check(step.typ = ARITH.tINTEGER, parser, pos, 76);
st := ARITH.getInt(step);
PARS.check(st # 0, parser, pos, 98)
ELSE
st := 1
END;
 
IF e.obj = eCONST THEN
IF st > 0 THEN
CODE.AddCmd(CODE.opLER, ARITH.Int(e.value))
ELSE
CODE.AddCmd(CODE.opGER, ARITH.Int(e.value))
END
ELSE
IF st > 0 THEN
CODE.AddCmd0(CODE.opLE)
ELSE
CODE.AddCmd0(CODE.opGE)
END
END;
 
CODE.AddJmpCmd(CODE.opJNE, L2);
 
PARS.checklex(parser, SCAN.lxDO);
PARS.Next(parser);
parser.StatSeq(parser);
 
IF ident.global THEN
CODE.AddCmd(CODE.opGADR, offset)
ELSE
CODE.AddCmd(CODE.opLADR, -offset)
END;
 
IF st = 1 THEN
CODE.AddCmd0(CODE.opINC1)
ELSIF st = -1 THEN
CODE.AddCmd0(CODE.opDEC1)
ELSE
IF st > 0 THEN
CODE.AddCmd(CODE.opINCC, st)
ELSE
CODE.AddCmd(CODE.opDECC, -st)
END
END;
 
CODE.AddJmpCmd(CODE.opJMP, L1);
 
PARS.checklex(parser, SCAN.lxEND);
PARS.Next(parser);
 
CODE.SetLabel(L2);
 
CODE.AddCmd0(CODE.opENDLOOP)
 
END ForStatement;
 
 
PROCEDURE statement (parser: PARS.PARSER);
VAR
sym: INTEGER;
 
BEGIN
sym := parser.sym;
 
IF sym = SCAN.lxIDENT THEN
ElementaryStatement(parser)
ELSIF sym = SCAN.lxIF THEN
IfStatement(parser, TRUE)
ELSIF sym = SCAN.lxWHILE THEN
IfStatement(parser, FALSE)
ELSIF sym = SCAN.lxREPEAT THEN
RepeatStatement(parser)
ELSIF sym = SCAN.lxCASE THEN
CaseStatement(parser)
ELSIF sym = SCAN.lxFOR THEN
ForStatement(parser)
END
END statement;
 
 
PROCEDURE StatSeq (parser: PARS.PARSER);
BEGIN
statement(parser);
WHILE parser.sym = SCAN.lxSEMI DO
PARS.Next(parser);
statement(parser)
END
END StatSeq;
 
 
PROCEDURE chkreturn (parser: PARS.PARSER; e: PARS.EXPR; t: PROG.TYPE_; pos: SCAN.POSITION): BOOLEAN;
VAR
res: BOOLEAN;
 
BEGIN
res := assigncomp(e, t);
IF res THEN
IF e.obj = eCONST THEN
IF e.type.typ = PROG.tREAL THEN
CODE.Float(ARITH.Float(e.value))
ELSIF e.type.typ = PROG.tNIL THEN
CODE.AddCmd(CODE.opCONST, 0)
ELSE
LoadConst(e)
END
ELSIF (e.type.typ = PROG.tINTEGER) & (t.typ = PROG.tBYTE) & (chkBYTE IN checking) THEN
CheckRange(256, pos.line, errBYTE)
ELSIF e.obj = ePROC THEN
PARS.check(e.ident.global, parser, pos, 85);
CODE.PushProc(e.ident.proc.label)
ELSIF e.obj = eIMP THEN
CODE.PushImpProc(e.ident.import)
END;
 
IF e.type.typ = PROG.tREAL THEN
CODE.retf
END
END
 
RETURN res
END chkreturn;
 
 
PROCEDURE setrtl;
VAR
rtl: PROG.UNIT;
 
 
PROCEDURE getproc (rtl: PROG.UNIT; name: SCAN.LEXSTR; idx: INTEGER);
VAR
id: PROG.IDENT;
 
BEGIN
id := rtl.idents.get(rtl, SCAN.enterid(name), FALSE);
 
IF (id # NIL) & (id.import # NIL) THEN
CODE.codes.rtl[idx] := -id.import(CODE.IMPORT_PROC).label;
id.proc.used := TRUE
ELSIF (id # NIL) & (id.proc # NIL) THEN
CODE.codes.rtl[idx] := id.proc.label;
id.proc.used := TRUE
ELSE
ERRORS.error5("procedure ", mConst.RTL_NAME, ".", name, " not found")
END
END getproc;
 
 
BEGIN
rtl := PARS.program.rtl;
ASSERT(rtl # NIL);
 
getproc(rtl, "_move", CODE._move);
getproc(rtl, "_move2", CODE._move2);
getproc(rtl, "_set", CODE._set);
getproc(rtl, "_set2", CODE._set2);
getproc(rtl, "_div", CODE._div);
getproc(rtl, "_mod", CODE._mod);
getproc(rtl, "_div2", CODE._div2);
getproc(rtl, "_mod2", CODE._mod2);
getproc(rtl, "_arrcpy", CODE._arrcpy);
getproc(rtl, "_rot", CODE._rot);
getproc(rtl, "_new", CODE._new);
getproc(rtl, "_dispose", CODE._dispose);
getproc(rtl, "_strcmp", CODE._strcmp);
getproc(rtl, "_error", CODE._error);
getproc(rtl, "_is", CODE._is);
getproc(rtl, "_isrec", CODE._isrec);
getproc(rtl, "_guard", CODE._guard);
getproc(rtl, "_guardrec", CODE._guardrec);
getproc(rtl, "_length", CODE._length);
getproc(rtl, "_init", CODE._init);
getproc(rtl, "_dllentry", CODE._dllentry);
getproc(rtl, "_strcpy", CODE._strcpy);
getproc(rtl, "_exit", CODE._exit);
getproc(rtl, "_strcpy2", CODE._strcpy2);
getproc(rtl, "_lengthw", CODE._lengthw);
getproc(rtl, "_strcmp2", CODE._strcmp2);
getproc(rtl, "_strcmpw", CODE._strcmpw);
getproc(rtl, "_strcmpw2", CODE._strcmpw2);
 
END setrtl;
 
 
PROCEDURE compile* (path, lib_path, modname, outname: PARS.PATH; target, version, stack, base: INTEGER; pic: BOOLEAN; chk: SET);
VAR
parser: PARS.PARSER;
ext: PARS.PATH;
amd64: BOOLEAN;
 
BEGIN
amd64 := target IN {mConst.Target_iConsole64, mConst.Target_iGUI64, mConst.Target_iDLL64, mConst.Target_iELF64};
ext := mConst.FILE_EXT;
CaseLabels := C.create();
CaseVar := C.create();
 
CaseVariants := LISTS.create(NIL);
LISTS.push(CaseVariants, NewVariant(0, NIL));
 
checking := chk;
 
IF amd64 THEN
CODE.init(6, CODE.little_endian)
ELSE
CODE.init(8, CODE.little_endian)
END;
 
parser := PARS.create(path, lib_path, StatSeq, expression, designator, chkreturn);
IF parser.open(parser, mConst.RTL_NAME) THEN
parser.parse(parser);
PARS.destroy(parser)
ELSE
PARS.destroy(parser);
parser := PARS.create(lib_path, lib_path, StatSeq, expression, designator, chkreturn);
IF parser.open(parser, mConst.RTL_NAME) THEN
parser.parse(parser);
PARS.destroy(parser)
ELSE
ERRORS.error5("file ", lib_path, mConst.RTL_NAME, mConst.FILE_EXT, " not found")
END
END;
 
parser := PARS.create(path, lib_path, StatSeq, expression, designator, chkreturn);
parser.main := TRUE;
 
IF parser.open(parser, modname) THEN
parser.parse(parser)
ELSE
ERRORS.error5("file ", path, modname, mConst.FILE_EXT, " not found")
END;
 
PARS.destroy(parser);
 
IF PARS.program.bss > mConst.MAX_GLOBAL_SIZE THEN
ERRORS.error1("size of global variables is too large")
END;
 
setrtl;
 
PROG.DelUnused(PARS.program, CODE.DelImport);
 
CODE.codes.bss := PARS.program.bss;
IF amd64 THEN
AMD64.CodeGen(CODE.codes, outname, target, stack, base)
ELSE
X86.CodeGen(CODE.codes, outname, target, stack, base, version, pic)
END
END compile;
 
 
END STATEMENTS.