COMPILER SimpleQ (* Simplified Modula-2 without PROCEDURE / FUNCTION, with a QBE backend for the scalar subset (see QbeGen for details). - program module only, no DEFINITION / IMPLEMENTATION split - no local modules, no EXPORT, no PRIORITY - no ProcedureDeclaration, FormalParameters, ProcedureType, ProcedureCall, ActualParameters, FORWARD, RETURN - statements: assignment, IF, CASE, WHILE, REPEAT, LOOP/EXIT, FOR, WITH - symbol table (SymTab) with static type checking: same error codes 200/201/202 and 210-224 as SimpleMod2 (Test1), same lenient rules (single pass, declare-before-use; INTEGER, CARDINAL and subranges form one integer family; no mixed INTEGER/REAL arithmetic; INTEGER assigns to REAL; 1-character literal is CHAR; InvalidType suppresses follow-on errors) - backend (QbeGen): emits QBE SSA to gen_qbe/.ssa (directory gen_qbe/ must exist), assembled with "qbe" and linked with "cc" into a native binary. Only scalar data is lowered: INTEGER/CARDINAL/SHORTINT/LONGINT/BOOLEAN/CHAR/enumerations as QBE "w", REAL/LONGREAL as QBE "d". Everything else parses and type-checks but gets error 230 (qbe backend: construct not supported in scalar subset): ARRAY/RECORD/SET/POINTER variables, string variables, WITH, IN, set literals, non-literal CONST expressions and BY steps, EXIT outside LOOP, imported names used as values. - test convention (the language has no I/O): a global VAR ExitCode : INTEGER; makes the generated main return its value as the process exit code; otherwise the program returns 0. *) IMPORT SymTab, QbeGen; CHARACTERS eol = CHR(13) . letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" . digit = "0123456789" . hexDigit = digit + "ABCDEF" . noQuote1 = ANY - "'" - eol . noQuote2 = ANY - '"' - eol . IGNORE CHR(9) .. CHR(13) COMMENTS FROM "(*" TO "*)" NESTED TOKENS ident = letter { letter | digit } . integer = digit { digit } | digit { digit } CONTEXT("..") | digit { hexDigit } "H" . real = digit { digit } "." { digit } [ "E" [ "+" | "-" ] digit { digit } ] . string = "'" { noQuote1 } "'" | '"' { noQuote2 } '"' . PRODUCTIONS SimpleQ (. VAR m1, m2: SymTab.Name; .) = "MODULE" GetIdent (. SymTab.Init; QbeGen.OpenModule(m1); IF ~SymTab.Enter(m1, SymTab.KindModule) THEN SemError(200) END .) ";" { Import } Block GetIdent (. IF ~SymTab.Equal(m1, m2) THEN SemError(202) END .) "." (. QbeGen.EndModule; SymTab.PrintTable; .) . Import (. VAR n: SymTab.Name; .) = "FROM" GetIdent (. IF ~SymTab.Enter(n, SymTab.KindImport) THEN SemError(200) END .) "IMPORT" ImportList ";" | "IMPORT" ImportList ";" . ImportList (. VAR n: SymTab.Name; .) = GetIdent (. IF ~SymTab.Enter(n, SymTab.KindImport) THEN SemError(200) END .) { "," GetIdent (. IF ~SymTab.Enter(n, SymTab.KindImport) THEN SemError(200) END .) } . Block = { Declaration } [ "BEGIN" (. QbeGen.BeginBody; .) StatSeq ] "END" . Declaration = "CONST" { ConstDecl ";" } | "TYPE" { TypeDecl ";" } | "VAR" { VarDecl ";" } . ConstDecl (. VAR n: SymTab.Name; t: SymTab.TypeIndex; qv: QbeGen.QVal; cls: INTEGER; .) = GetIdent (. IF ~SymTab.Enter(n, SymTab.KindConst) THEN SemError(200) END .) "=" ConstExpr (. SymTab.SetSymType(n, t); cls := SymTab.ClassOf(t); IF cls = SymTab.ClStr THEN SemError(230) ELSIF ~QbeGen.IsImm(qv) THEN SemError(230) END; QbeGen.DeclConst(n, qv, t); .) . ConstExpr = Expr . TypeDecl (. VAR n: SymTab.Name; t0, t1: SymTab.TypeIndex; .) = GetIdent (. IF ~SymTab.Enter(n, SymTab.KindType) THEN SemError(200) END; t0 := SymTab.NewAlias(); SymTab.SetSymType(n, t0); .) "=" Type (. IF t1 = t0 THEN SemError(223); SymTab.SetTarget(t0, SymTab.InvalidType) ELSE SymTab.SetTarget(t0, t1) END; .) . VarDecl (. VAR t: SymTab.TypeIndex; i: CARDINAL; nm: SymTab.Name; cls: INTEGER; .) = VarIdents ":" Type (. cls := SymTab.ClassOf(t); IF (cls # SymTab.ClInt) & (cls # SymTab.ClReal) & (cls # SymTab.ClBool) & (cls # SymTab.ClChar) & (cls # SymTab.ClEnum) THEN SemError(230) END; i := 0; WHILE i < SymTab.PendCount() DO SymTab.PendName(i, nm); QbeGen.DeclVar(nm, t); INC(i) END; SymTab.FixPending(t); .) . VarIdents (. VAR n: SymTab.Name; .) = GetIdent (. IF ~SymTab.EnterPending(n, SymTab.KindVar) THEN SemError(200) END .) { "," GetIdent (. IF ~SymTab.EnterPending(n, SymTab.KindVar) THEN SemError(200) END .) } . QualIdent (. VAR n, m: SymTab.Name; .) = GetIdent (. IF ~SymTab.Lookup(n) THEN SemError(201); t := SymTab.InvalidType ELSIF (SymTab.SymKind(n) # SymTab.KindType) & (SymTab.SymKind(n) # SymTab.KindPredef) & (SymTab.SymKind(n) # SymTab.KindImport) THEN SemError(221); t := SymTab.InvalidType ELSE t := SymTab.SymType(n) END; .) { "." GetIdent (. t := SymTab.InvalidType; .) } . (* Types: ProcedureType removed; subrange factored for LL(1) *) Type = SimpleType | ArrayType | RecordType | SetType | PointerType . SimpleType (. VAR t1, t2: SymTab.TypeIndex; q1, q2: QbeGen.QVal; .) = QualIdent [ "[" ConstExpr (. IF (t1 # SymTab.InvalidType) & (SymTab.ClassOf(t1) # SymTab.ClInt) & (SymTab.ClassOf(t1) # SymTab.ClChar) & (SymTab.ClassOf(t1) # SymTab.ClEnum) THEN SemError(224) END; .) ".." ConstExpr (. IF (t2 # SymTab.InvalidType) & (SymTab.ClassOf(t2) # SymTab.ClInt) & (SymTab.ClassOf(t2) # SymTab.ClChar) & (SymTab.ClassOf(t2) # SymTab.ClEnum) THEN SemError(224) END; .) "]" (. t := SymTab.NewSub(t1); .) ] | "[" ConstExpr (. IF (t1 # SymTab.InvalidType) & (SymTab.ClassOf(t1) # SymTab.ClInt) & (SymTab.ClassOf(t1) # SymTab.ClChar) & (SymTab.ClassOf(t1) # SymTab.ClEnum) THEN SemError(224) END; .) ".." ConstExpr (. IF (t2 # SymTab.InvalidType) & (SymTab.ClassOf(t2) # SymTab.ClInt) & (SymTab.ClassOf(t2) # SymTab.ClChar) & (SymTab.ClassOf(t2) # SymTab.ClEnum) THEN SemError(224) END; .) "]" (. t := SymTab.NewSub(t1); .) | Enum . Enum (. VAR n: SymTab.Name; qs: QbeGen.QVal; ord: INTEGER; .) = "(" (. t := SymTab.NewEnum(); ord := 0; .) GetIdent (. IF ~SymTab.Enter(n, SymTab.KindConst) THEN SemError(200) END; SymTab.SetSymType(n, t); QbeGen.IntStr(ord, qs); QbeGen.DeclConst(n, qs, t); INC(ord); .) { "," GetIdent (. IF ~SymTab.Enter(n, SymTab.KindConst) THEN SemError(200) END; SymTab.SetSymType(n, t); QbeGen.IntStr(ord, qs); QbeGen.DeclConst(n, qs, t); INC(ord); .) } ")" . ArrayType (. VAR s, s2, e: SymTab.TypeIndex; qs: QbeGen.QVal; .) = "ARRAY" SimpleType (. IF (s # SymTab.InvalidType) & (SymTab.ClassOf(s) # SymTab.ClInt) & (SymTab.ClassOf(s) # SymTab.ClChar) & (SymTab.ClassOf(s) # SymTab.ClEnum) THEN SemError(224) END; .) { "," SimpleType (. IF (s2 # SymTab.InvalidType) & (SymTab.ClassOf(s2) # SymTab.ClInt) & (SymTab.ClassOf(s2) # SymTab.ClChar) & (SymTab.ClassOf(s2) # SymTab.ClEnum) THEN SemError(224) END; .) } "OF" Type (. t := SymTab.NewArray(e); .) . RecordType = "RECORD" (. t := SymTab.NewRecord(); .) FieldSeq "END" . FieldSeq = Field { ";" Field } . Field (. VAR et: SymTab.TypeIndex; .) = [ FieldIdents ":" Type (. SymTab.FixPendingF(rt, et); .) ] . FieldIdents (. VAR n: SymTab.Name; .) = GetIdent (. IF ~SymTab.FieldPending(rt, n) THEN SemError(200) END .) { "," GetIdent (. IF ~SymTab.FieldPending(rt, n) THEN SemError(200) END .) } . SetType (. VAR s: SymTab.TypeIndex; .) = "SET" "OF" SimpleType (. IF (s # SymTab.InvalidType) & (SymTab.ClassOf(s) # SymTab.ClInt) & (SymTab.ClassOf(s) # SymTab.ClChar) & (SymTab.ClassOf(s) # SymTab.ClEnum) THEN SemError(224) END; t := SymTab.NewSet(s); .) . PointerType (. VAR b: SymTab.TypeIndex; .) = "POINTER" "TO" Type (. t := SymTab.NewPtr(b); .) . (* Statements: ProcedureCall and RETURN removed *) StatSeq = Stat { ";" Stat } . Stat (. VAR lx: QbeGen.QVal; .) = [ Assign | IfStat | CaseStat | WhileStat | RepeatStat | LoopStat | ForStat | WithStat | "EXIT" (. IF QbeGen.TopLoop(lx) THEN QbeGen.Jmp(lx) ELSE SemError(230) END; .) ] . Assign (. VAR dt, et: SymTab.TypeIndex; dk: INTEGER; qd, qe, qt: QbeGen.QVal; qn: SymTab.Name; sfx: BOOLEAN; isR, conv: BOOLEAN; .) = Design ":=" Expr (. IF (dt # SymTab.InvalidType) & (dk # SymTab.KindVar) & (dk # SymTab.KindField) & (dk # SymTab.KindImport) THEN SemError(210) ELSIF ~SymTab.Assignable(et, dt) THEN SemError(210) END; IF dk = SymTab.KindImport THEN SemError(230) END; isR := (dt # SymTab.InvalidType) & (SymTab.ClassOf(dt) = SymTab.ClReal); conv := isR & SymTab.IsIntFamily(et); IF ~sfx & (dk = SymTab.KindVar) THEN IF conv THEN QbeGen.ConvIR(qe, qt); QbeGen.StoreVar(qn, qt, TRUE) ELSE QbeGen.StoreVar(qn, qe, isR) END END; .) . IfStat (. VAR t: SymTab.TypeIndex; q: QbeGen.QVal; lThen, lElse, lEnd: QbeGen.QVal; hasElse: BOOLEAN; .) = "IF" Expr (. IF ~SymTab.BoolCheck(t) THEN SemError(214) END; QbeGen.NewLabel(lThen); QbeGen.NewLabel(lElse); QbeGen.NewLabel(lEnd); QbeGen.Jnz(q, lThen, lElse); QbeGen.EmitLabel(lThen); hasElse := FALSE; .) "THEN" StatSeq { "ELSIF" (. QbeGen.Jmp(lEnd); QbeGen.EmitLabel(lElse); .) Expr (. IF ~SymTab.BoolCheck(t) THEN SemError(214) END; QbeGen.NewLabel(lThen); QbeGen.NewLabel(lElse); QbeGen.Jnz(q, lThen, lElse); QbeGen.EmitLabel(lThen); .) "THEN" StatSeq } [ "ELSE" (. QbeGen.Jmp(lEnd); QbeGen.EmitLabel(lElse); hasElse := TRUE; .) StatSeq ] "END" (. IF ~hasElse THEN QbeGen.EmitLabel(lElse) END; QbeGen.EmitLabel(lEnd); .) . CaseStat (. VAR st: SymTab.TypeIndex; sq: QbeGen.QVal; lEnd: QbeGen.QVal; .) = "CASE" Expr "OF" (. QbeGen.NewLabel(lEnd); .) Case { "|" Case } [ "ELSE" StatSeq ] "END" (. QbeGen.EmitLabel(lEnd); .) . Case (. VAR lB, lN: QbeGen.QVal; .) = [ LabelList ":" (. QbeGen.EmitLabel(lB); .) StatSeq (. QbeGen.Jmp(endL); QbeGen.EmitLabel(lN); .) ] . LabelList = (. QbeGen.NewLabel(lB); QbeGen.NewLabel(lN); .) Labels { "," Labels } (. QbeGen.Jmp(lN); .) . Labels (. VAR t, t2: SymTab.TypeIndex; q, q2, qk, qg, ql, qb: QbeGen.QVal; lC: QbeGen.QVal; r, hasRange: BOOLEAN; .) = ConstExpr (. IF ~SymTab.EqCheck(t, sel) THEN SemError(213) END; r := (SymTab.ClassOf(sel) = SymTab.ClReal) & (SymTab.ClassOf(t) = SymTab.ClReal); hasRange := FALSE; .) [ ".." ConstExpr (. IF ~SymTab.EqCheck(t2, sel) THEN SemError(213) END; hasRange := TRUE; .) ] (. IF hasRange THEN QbeGen.Cmp(SymTab.OpGe, sq, q, qg, r); QbeGen.Cmp(SymTab.OpLe, sq, q2, ql, r); QbeGen.NewTemp(qb); QbeGen.Op3("and", qb, qg, ql, FALSE) ELSE QbeGen.Cmp(SymTab.OpEq, sq, q, qb, r) END; QbeGen.NewLabel(lC); QbeGen.Jnz(qb, lB, lC); QbeGen.EmitLabel(lC); .) . WhileStat (. VAR t: SymTab.TypeIndex; q: QbeGen.QVal; lC, lB, lE: QbeGen.QVal; .) = "WHILE" (. QbeGen.NewLabel(lC); QbeGen.NewLabel(lB); QbeGen.NewLabel(lE); QbeGen.EmitLabel(lC); .) Expr (. IF ~SymTab.BoolCheck(t) THEN SemError(214) END; QbeGen.Jnz(q, lB, lE); QbeGen.EmitLabel(lB); .) "DO" StatSeq "END" (. QbeGen.Jmp(lC); QbeGen.EmitLabel(lE); .) . RepeatStat (. VAR t: SymTab.TypeIndex; q: QbeGen.QVal; lTop, lE: QbeGen.QVal; .) = "REPEAT" (. QbeGen.NewLabel(lTop); QbeGen.NewLabel(lE); QbeGen.EmitLabel(lTop); .) StatSeq "UNTIL" Expr (. IF ~SymTab.BoolCheck(t) THEN SemError(214) END; QbeGen.Jnz(q, lE, lTop); QbeGen.EmitLabel(lE); .) . LoopStat (. VAR lTop, lE: QbeGen.QVal; .) = "LOOP" (. QbeGen.NewLabel(lTop); QbeGen.NewLabel(lE); QbeGen.EmitLabel(lTop); QbeGen.PushLoop(lE); .) StatSeq "END" (. QbeGen.Jmp(lTop); QbeGen.EmitLabel(lE); QbeGen.PopLoop; .) . ForStat (. VAR n, lv: SymTab.Name; lo, hi, by: SymTab.TypeIndex; qlo, qhi, qby, hiS, byS: QbeGen.QVal; qt, qk: QbeGen.QVal; lTop, lChk, lEnd: QbeGen.QVal; neg: BOOLEAN; .) = "FOR" GetIdent (. IF ~SymTab.Lookup(n) THEN SemError(201) ELSIF (SymTab.SymKind(n) # SymTab.KindVar) & (SymTab.SymKind(n) # SymTab.KindField) THEN SemError(220) ELSIF (SymTab.SymType(n) # SymTab.InvalidType) & ~SymTab.IsIntFamily( SymTab.SymType(n)) THEN SemError(220) END; QbeGen.CopyOp(n, lv); .) ":=" Expr (. IF (lo # SymTab.InvalidType) & ~SymTab.IsIntFamily(lo) THEN SemError(220) END; IF (lo # SymTab.InvalidType) & SymTab.IsIntFamily(lo) THEN QbeGen.StoreVar(lv, qlo, FALSE) ELSE QbeGen.StoreVar(lv, "0", FALSE) END; .) "TO" Expr (. IF (hi # SymTab.InvalidType) & ~SymTab.IsIntFamily(hi) THEN SemError(220) END; QbeGen.CopyOp(qhi, hiS); QbeGen.CopyOp("1", byS); neg := FALSE; by := SymTab.IntType(); .) [ "BY" ConstExpr (. IF (by # SymTab.InvalidType) & ~SymTab.IsIntFamily(by) THEN SemError(220) END; IF QbeGen.IsImm(qby) THEN QbeGen.CopyOp(qby, byS); neg := QbeGen.IsNeg(qby) ELSE SemError(230); QbeGen.CopyOp("1", byS); neg := FALSE END; .) ] "DO" (. QbeGen.NewLabel(lTop); QbeGen.NewLabel(lChk); QbeGen.NewLabel(lEnd); QbeGen.Jmp(lChk); QbeGen.EmitLabel(lTop); .) StatSeq "END" (. QbeGen.LoadVar(lv, FALSE, qt); QbeGen.NewTemp(qk); QbeGen.Op3("add", qk, qt, byS, FALSE); QbeGen.StoreVar(lv, qk, FALSE); QbeGen.EmitLabel(lChk); QbeGen.LoadVar(lv, FALSE, qt); QbeGen.NewTemp(qk); IF neg THEN QbeGen.Op3("csgew", qk, qt, hiS, FALSE) ELSE QbeGen.Op3("cslew", qk, qt, hiS, FALSE) END; QbeGen.Jnz(qk, lTop, lEnd); QbeGen.EmitLabel(lEnd); .) . WithStat (. VAR dt: SymTab.TypeIndex; dk: INTEGER; dq: QbeGen.QVal; qn: SymTab.Name; sfx: BOOLEAN; pushed: BOOLEAN; .) = "WITH" Design (. pushed := FALSE; IF dt # SymTab.InvalidType THEN pushed := SymTab.PushRecord(dt); IF ~pushed THEN SemError(215) END END; IF pushed THEN QbeGen.NoQbeEnter; SemError(230) END; .) "DO" StatSeq "END" (. IF pushed THEN SymTab.PopScope; QbeGen.NoQbeExit END; .) . (* Expressions: Designator without ActualParameters. Only record fields resolve qualified access; WITH pushes an inner scope so field names also work unqualified in its body (both report 230 in the scalar subset). Each expression synthesizes its SymTab type in t and its QBE operand in q (an immediate or a fresh %temporary holding the value). *) Design (. VAR n, m: SymTab.Name; it: SymTab.TypeIndex; qr: QbeGen.QVal; cls: INTEGER; .) = GetIdent (. QbeGen.CopyOp(n, qn); sfx := FALSE; IF ~SymTab.Lookup(n) THEN SemError(201); t := SymTab.InvalidType; k := -1; QbeGen.CopyOp("0", q) ELSE t := SymTab.SymType(n); k := SymTab.SymKind(n); IF k = SymTab.KindConst THEN IF SymTab.Equal(n, "TRUE") THEN t := SymTab.BoolType(); QbeGen.CopyOp("1", q) ELSIF SymTab.Equal(n, "FALSE") THEN t := SymTab.BoolType(); QbeGen.CopyOp("0", q) ELSE cls := SymTab.ClassOf(t); IF (t # SymTab.InvalidType) & (cls # SymTab.ClStr) & ((cls = SymTab.ClInt) OR (cls = SymTab.ClReal) OR (cls = SymTab.ClBool) OR (cls = SymTab.ClChar) OR (cls = SymTab.ClEnum)) THEN QbeGen.LoadVar(n, cls = SymTab.ClReal, q) ELSE QbeGen.CopyOp("0", q) END END ELSIF k = SymTab.KindVar THEN cls := SymTab.ClassOf(t); IF (cls = SymTab.ClInt) OR (cls = SymTab.ClReal) OR (cls = SymTab.ClBool) OR (cls = SymTab.ClChar) OR (cls = SymTab.ClEnum) THEN QbeGen.LoadVar(n, cls = SymTab.ClReal, q) ELSE SemError(230); QbeGen.CopyOp("0", q) END ELSE QbeGen.CopyOp("0", q); IF k = SymTab.KindField THEN IF ~QbeGen.NoQbe() THEN SemError(230) END ELSIF k = SymTab.KindImport THEN SemError(230) END END END; .) { "." (. sfx := TRUE; .) GetIdent (. IF t = SymTab.InvalidType THEN ELSIF SymTab.ClassOf(t) # SymTab.ClRecord THEN SemError(215); t := SymTab.InvalidType ELSIF ~SymTab.FieldExists(t, m) THEN SemError(216); t := SymTab.InvalidType ELSE t := SymTab.FieldType(t, m); SemError(230) END; QbeGen.CopyOp("0", q); .) | "[" (. sfx := TRUE; .) Expr (. IF t = SymTab.InvalidType THEN ELSIF SymTab.ClassOf(t) # SymTab.ClArray THEN SemError(217); t := SymTab.InvalidType ELSIF (it # SymTab.InvalidType) & ~SymTab.IsIntFamily(it) THEN SemError(218); t := SymTab.InvalidType ELSE t := SymTab.ArrayElem(t); SemError(230) END; QbeGen.CopyOp("0", q); .) { "," Expr (. IF (it # SymTab.InvalidType) & ~SymTab.IsIntFamily(it) THEN SemError(218) END; .) } "]" | "^" (. sfx := TRUE; IF t = SymTab.InvalidType THEN ELSIF SymTab.ClassOf(t) # SymTab.ClPtr THEN SemError(219); t := SymTab.InvalidType ELSE t := SymTab.PtrBase(t); SemError(230) END; QbeGen.CopyOp("0", q); .) } . Expr (. VAR t2: SymTab.TypeIndex; tc, op: INTEGER; q2, qk: QbeGen.QVal; r: BOOLEAN; .) = SimExpr [ Rel SimExpr (. IF op = SymTab.OpIn THEN IF SymTab.InCheck(t, t2) THEN t := SymTab.BoolType(); SemError(230) ELSE SemError(222); t := SymTab.InvalidType END; QbeGen.CopyOp("0", q) ELSE tc := SymTab.ClassOf(t); IF SymTab.RelCheck(t, t2, op) THEN t := SymTab.BoolType() ELSE SemError(213); t := SymTab.InvalidType END; IF t # SymTab.InvalidType THEN r := tc = SymTab.ClReal; QbeGen.Cmp(op, q, q2, qk, r); QbeGen.CopyOp(qk, q) ELSE QbeGen.CopyOp("0", q) END END; .) ] . Rel = "=" (. op := SymTab.OpEq; .) | "#" (. op := SymTab.OpNeq1; .) | "<>" (. op := SymTab.OpNeq2; .) | "<" (. op := SymTab.OpLt; .) | "<=" (. op := SymTab.OpLe; .) | ">" (. op := SymTab.OpGt; .) | ">=" (. op := SymTab.OpGe; .) | "IN" (. op := SymTab.OpIn; .) . SimExpr (. VAR t2, res2: SymTab.TypeIndex; op: INTEGER; q2, qt: QbeGen.QVal; neg, isR: BOOLEAN; .) = (. neg := FALSE; .) [ "+" | "-" (. neg := TRUE; .) ] Term (. IF neg THEN IF QbeGen.IsImm(q) THEN QbeGen.NegFold(q, q) ELSE QbeGen.NewTemp(qt); QbeGen.NegQ(q, qt, SymTab.ClassOf(t) = SymTab.ClReal); QbeGen.CopyOp(qt, q) END END; .) { AddOp Term (. IF op = SymTab.OpOr THEN IF SymTab.BoolCheck(t) & SymTab.BoolCheck(t2) THEN t := SymTab.BoolType() ELSE SemError(212); t := SymTab.InvalidType END; IF t # SymTab.InvalidType THEN QbeGen.NewTemp(qt); QbeGen.Op3("or", qt, q, q2, FALSE); QbeGen.CopyOp(qt, q) ELSE QbeGen.CopyOp("0", q) END ELSE IF SymTab.ArithCheck(t, t2, FALSE, res2) THEN t := res2 ELSE SemError(211); t := SymTab.InvalidType END; IF t # SymTab.InvalidType THEN isR := SymTab.ClassOf(t) = SymTab.ClReal; QbeGen.NewTemp(qt); IF op = SymTab.OpAdd THEN QbeGen.Op3("add", qt, q, q2, isR) ELSE QbeGen.Op3("sub", qt, q, q2, isR) END; QbeGen.CopyOp(qt, q) ELSE QbeGen.CopyOp("0", q) END END; .) } . AddOp = "+" (. op := SymTab.OpAdd; .) | "-" (. op := SymTab.OpSub; .) | "OR" (. op := SymTab.OpOr; .) . Term (. VAR t2, res2: SymTab.TypeIndex; op: INTEGER; q2, qt: QbeGen.QVal; isR: BOOLEAN; .) = Fact { MulOp Fact (. IF op = SymTab.OpAnd THEN IF SymTab.BoolCheck(t) & SymTab.BoolCheck(t2) THEN t := SymTab.BoolType() ELSE SemError(212); t := SymTab.InvalidType END; IF t # SymTab.InvalidType THEN QbeGen.NewTemp(qt); QbeGen.Op3("and", qt, q, q2, FALSE); QbeGen.CopyOp(qt, q) ELSE QbeGen.CopyOp("0", q) END ELSE IF SymTab.ArithCheck(t, t2, (op = SymTab.OpDiv) OR (op = SymTab.OpMod), res2) THEN t := res2 ELSE SemError(211); t := SymTab.InvalidType END; IF t # SymTab.InvalidType THEN isR := SymTab.ClassOf(t) = SymTab.ClReal; QbeGen.NewTemp(qt); IF op = SymTab.OpTimes THEN QbeGen.Op3("mul", qt, q, q2, isR) ELSIF op = SymTab.OpSlash THEN QbeGen.Op3("div", qt, q, q2, isR) ELSIF op = SymTab.OpDiv THEN QbeGen.Op3("div", qt, q, q2, FALSE) ELSE QbeGen.Op3("rem", qt, q, q2, FALSE) END; QbeGen.CopyOp(qt, q) ELSE QbeGen.CopyOp("0", q) END END; .) } . MulOp = "*" (. op := SymTab.OpTimes; .) | "/" (. op := SymTab.OpSlash; .) | "DIV" (. op := SymTab.OpDiv; .) | "MOD" (. op := SymTab.OpMod; .) | "AND" (. op := SymTab.OpAnd; .) | "&" (. op := SymTab.OpAnd; .) . Fact (. VAR s: ARRAY [0 .. 255] OF CHAR; t2, et, dt, st: SymTab.TypeIndex; dk: INTEGER; qd, q2: QbeGen.QVal; qnF: SymTab.Name; sfxF: BOOLEAN; .) = integer (. LexString(s); QbeGen.NormInt(s, q); t := SymTab.IntType(); .) | real (. LexString(s); QbeGen.NormReal(s, q); t := SymTab.RealType(); .) | string (. LexString(s); IF SymTab.StrLen(s) <= 3 THEN t := SymTab.CharType(); QbeGen.IntStr( QbeGen.CharVal(s), q) ELSE t := SymTab.NewStr(); SemError(230); QbeGen.CopyOp("0", q) END; .) | Design (. t := dt; QbeGen.CopyOp(qd, q); .) | "(" Expr ")" (. t := et; .) | ( "NOT" | "~" ) Fact (. IF SymTab.BoolCheck(t2) THEN t := SymTab.BoolType() ELSE SemError(212); t := SymTab.InvalidType END; IF t # SymTab.InvalidType THEN QbeGen.NotQ(q2, q) ELSE QbeGen.CopyOp("0", q) END; .) | SetLit (. t := st; SemError(230); QbeGen.CopyOp("0", q); .) . SetLit (. VAR first, et: SymTab.TypeIndex; qe: QbeGen.QVal; .) = "{" (. t := SymTab.SetFor(SymTab.IntType()); .) [ Elem (. first := et; t := SymTab.SetFor(et); .) { "," Elem (. IF ~SymTab.SetElemCheck(first, et) THEN SemError(222) END; .) } ] "}" . Elem (. VAR t2: SymTab.TypeIndex; q2: QbeGen.QVal; .) = Expr [ ".." Expr (. IF ~SymTab.SetElemCheck(t, t2) THEN SemError(222) END; .) ] . GetIdent = ident (. LexName(n); .) . END SimpleQ.