/* * @(#)Checker.java 2.1 2003/10/07 * * Copyright (C) 1999, 2003 D.A. Watt and D.F. Brown * Dept. of Computing Science, University of Glasgow, Glasgow G12 8QQ Scotland * and School of Computer and Math Sciences, The Robert Gordon University, * St. Andrew Street, Aberdeen AB25 1HG, Scotland. * All rights reserved. * * This software is provided free for educational use only. It may * not be used for commercial purposes without the prior written permission * of the authors. */ package Triangle.ContextualAnalyzer; import Triangle.ErrorReporter; import Triangle.StdEnvironment; import Triangle.AbstractSyntaxTrees.AnyTypeDenoter; import Triangle.AbstractSyntaxTrees.ArrayExpression; import Triangle.AbstractSyntaxTrees.ArrayTypeDenoter; import Triangle.AbstractSyntaxTrees.AssignCommand; import Triangle.AbstractSyntaxTrees.BinaryExpression; import Triangle.AbstractSyntaxTrees.BinaryOperatorDeclaration; import Triangle.AbstractSyntaxTrees.BoolTypeDenoter; import Triangle.AbstractSyntaxTrees.CallCommand; import Triangle.AbstractSyntaxTrees.CallExpression; import Triangle.AbstractSyntaxTrees.CharTypeDenoter; import Triangle.AbstractSyntaxTrees.CharacterExpression; import Triangle.AbstractSyntaxTrees.CharacterLiteral; import Triangle.AbstractSyntaxTrees.ConstActualParameter; import Triangle.AbstractSyntaxTrees.ConstDeclaration; import Triangle.AbstractSyntaxTrees.ConstFormalParameter; import Triangle.AbstractSyntaxTrees.Declaration; import Triangle.AbstractSyntaxTrees.DotVname; import Triangle.AbstractSyntaxTrees.EmptyActualParameterSequence; import Triangle.AbstractSyntaxTrees.EmptyCommand; import Triangle.AbstractSyntaxTrees.EmptyExpression; import Triangle.AbstractSyntaxTrees.EmptyFormalParameterSequence; import Triangle.AbstractSyntaxTrees.ErrorTypeDenoter; import Triangle.AbstractSyntaxTrees.FieldTypeDenoter; import Triangle.AbstractSyntaxTrees.FormalParameter; import Triangle.AbstractSyntaxTrees.FormalParameterSequence; import Triangle.AbstractSyntaxTrees.FuncActualParameter; import Triangle.AbstractSyntaxTrees.FuncDeclaration; import Triangle.AbstractSyntaxTrees.FuncFormalParameter; import Triangle.AbstractSyntaxTrees.Identifier; import Triangle.AbstractSyntaxTrees.IfCommand; import Triangle.AbstractSyntaxTrees.IfExpression; import Triangle.AbstractSyntaxTrees.IntTypeDenoter; import Triangle.AbstractSyntaxTrees.IntegerExpression; import Triangle.AbstractSyntaxTrees.IntegerLiteral; import Triangle.AbstractSyntaxTrees.LetCommand; import Triangle.AbstractSyntaxTrees.LetExpression; import Triangle.AbstractSyntaxTrees.MultipleActualParameterSequence; import Triangle.AbstractSyntaxTrees.MultipleArrayAggregate; import Triangle.AbstractSyntaxTrees.MultipleFieldTypeDenoter; import Triangle.AbstractSyntaxTrees.MultipleFormalParameterSequence; import Triangle.AbstractSyntaxTrees.MultipleRecordAggregate; import Triangle.AbstractSyntaxTrees.Operator; import Triangle.AbstractSyntaxTrees.ProcActualParameter; import Triangle.AbstractSyntaxTrees.ProcDeclaration; import Triangle.AbstractSyntaxTrees.ProcFormalParameter; import Triangle.AbstractSyntaxTrees.Program; import Triangle.AbstractSyntaxTrees.RecordExpression; import Triangle.AbstractSyntaxTrees.RecordTypeDenoter; import Triangle.AbstractSyntaxTrees.SequentialCommand; import Triangle.AbstractSyntaxTrees.SequentialDeclaration; import Triangle.AbstractSyntaxTrees.SimpleTypeDenoter; import Triangle.AbstractSyntaxTrees.SimpleVname; import Triangle.AbstractSyntaxTrees.SingleActualParameterSequence; import Triangle.AbstractSyntaxTrees.SingleArrayAggregate; import Triangle.AbstractSyntaxTrees.SingleFieldTypeDenoter; import Triangle.AbstractSyntaxTrees.SingleFormalParameterSequence; import Triangle.AbstractSyntaxTrees.SingleRecordAggregate; import Triangle.AbstractSyntaxTrees.SubscriptVname; import Triangle.AbstractSyntaxTrees.Terminal; import Triangle.AbstractSyntaxTrees.TypeDeclaration; import Triangle.AbstractSyntaxTrees.TypeDenoter; import Triangle.AbstractSyntaxTrees.UnaryExpression; import Triangle.AbstractSyntaxTrees.UnaryOperatorDeclaration; import Triangle.AbstractSyntaxTrees.VarActualParameter; import Triangle.AbstractSyntaxTrees.VarDeclaration; import Triangle.AbstractSyntaxTrees.VarFormalParameter; import Triangle.AbstractSyntaxTrees.Visitor; import Triangle.AbstractSyntaxTrees.VnameExpression; import Triangle.AbstractSyntaxTrees.WhileCommand; import Triangle.SyntacticAnalyzer.SourcePosition; public final class Checker implements Visitor { // Commands // Always returns null. Does not use the given object. @Override public Object visitAssignCommand(AssignCommand ast, Object o) { TypeDenoter vType = (TypeDenoter) ast.V.visit(this, null); TypeDenoter eType = (TypeDenoter) ast.E.visit(this, null); if (!ast.V.variable) reporter.reportError("LHS of assignment is not a variable", "", ast.V.position); if (!eType.equals(vType)) reporter.reportError("assignment incompatibilty", "", ast.position); return null; } @Override public Object visitCallCommand(CallCommand ast, Object o) { Declaration binding = (Declaration) ast.I.visit(this, null); if (binding == null) reportUndeclared(ast.I); else if (binding instanceof ProcDeclaration) { ast.APS.visit(this, ((ProcDeclaration) binding).FPS); } else if (binding instanceof ProcFormalParameter) { ast.APS.visit(this, ((ProcFormalParameter) binding).FPS); } else reporter.reportError("\"%\" is not a procedure identifier", ast.I.spelling, ast.I.position); return null; } @Override public Object visitEmptyCommand(EmptyCommand ast, Object o) { return null; } @Override public Object visitIfCommand(IfCommand ast, Object o) { TypeDenoter eType = (TypeDenoter) ast.E.visit(this, null); if (!eType.equals(StdEnvironment.booleanType)) reporter.reportError("Boolean expression expected here", "", ast.E.position); ast.C1.visit(this, null); ast.C2.visit(this, null); return null; } @Override public Object visitLetCommand(LetCommand ast, Object o) { idTable.openScope(); ast.D.visit(this, null); ast.C.visit(this, null); idTable.closeScope(); return null; } @Override public Object visitSequentialCommand(SequentialCommand ast, Object o) { ast.C1.visit(this, null); ast.C2.visit(this, null); return null; } @Override public Object visitWhileCommand(WhileCommand ast, Object o) { TypeDenoter eType = (TypeDenoter) ast.E.visit(this, null); if (!eType.equals(StdEnvironment.booleanType)) reporter.reportError("Boolean expression expected here", "", ast.E.position); ast.C.visit(this, null); return null; } // Expressions // Returns the TypeDenoter denoting the type of the expression. Does // not use the given object. @Override public Object visitArrayExpression(ArrayExpression ast, Object o) { TypeDenoter elemType = (TypeDenoter) ast.AA.visit(this, null); IntegerLiteral il = new IntegerLiteral(new Integer(ast.AA.elemCount).toString(), ast.position); ast.type = new ArrayTypeDenoter(il, elemType, ast.position); return ast.type; } @Override public Object visitBinaryExpression(BinaryExpression ast, Object o) { TypeDenoter e1Type = (TypeDenoter) ast.E1.visit(this, null); TypeDenoter e2Type = (TypeDenoter) ast.E2.visit(this, null); Declaration binding = (Declaration) ast.O.visit(this, null); if (binding == null) reportUndeclared(ast.O); else { if (!(binding instanceof BinaryOperatorDeclaration)) reporter.reportError("\"%\" is not a binary operator", ast.O.spelling, ast.O.position); BinaryOperatorDeclaration bbinding = (BinaryOperatorDeclaration) binding; if (bbinding.ARG1 == StdEnvironment.anyType) { // this operator must be "=" or "\=" if (!e1Type.equals(e2Type)) reporter.reportError("incompatible argument types for \"%\"", ast.O.spelling, ast.position); } else if (!e1Type.equals(bbinding.ARG1)) reporter.reportError("wrong argument type for \"%\"", ast.O.spelling, ast.E1.position); else if (!e2Type.equals(bbinding.ARG2)) reporter.reportError("wrong argument type for \"%\"", ast.O.spelling, ast.E2.position); ast.type = bbinding.RES; } return ast.type; } @Override public Object visitCallExpression(CallExpression ast, Object o) { Declaration binding = (Declaration) ast.I.visit(this, null); if (binding == null) { reportUndeclared(ast.I); ast.type = StdEnvironment.errorType; } else if (binding instanceof FuncDeclaration) { ast.APS.visit(this, ((FuncDeclaration) binding).FPS); ast.type = ((FuncDeclaration) binding).T; } else if (binding instanceof FuncFormalParameter) { ast.APS.visit(this, ((FuncFormalParameter) binding).FPS); ast.type = ((FuncFormalParameter) binding).T; } else reporter.reportError("\"%\" is not a function identifier", ast.I.spelling, ast.I.position); return ast.type; } @Override public Object visitCharacterExpression(CharacterExpression ast, Object o) { ast.type = StdEnvironment.charType; return ast.type; } @Override public Object visitEmptyExpression(EmptyExpression ast, Object o) { ast.type = null; return ast.type; } @Override public Object visitIfExpression(IfExpression ast, Object o) { TypeDenoter e1Type = (TypeDenoter) ast.E1.visit(this, null); if (!e1Type.equals(StdEnvironment.booleanType)) reporter.reportError("Boolean expression expected here", "", ast.E1.position); TypeDenoter e2Type = (TypeDenoter) ast.E2.visit(this, null); TypeDenoter e3Type = (TypeDenoter) ast.E3.visit(this, null); if (!e2Type.equals(e3Type)) reporter.reportError("incompatible limbs in if-expression", "", ast.position); ast.type = e2Type; return ast.type; } @Override public Object visitIntegerExpression(IntegerExpression ast, Object o) { ast.type = StdEnvironment.integerType; return ast.type; } @Override public Object visitLetExpression(LetExpression ast, Object o) { idTable.openScope(); ast.D.visit(this, null); ast.type = (TypeDenoter) ast.E.visit(this, null); idTable.closeScope(); return ast.type; } @Override public Object visitRecordExpression(RecordExpression ast, Object o) { FieldTypeDenoter rType = (FieldTypeDenoter) ast.RA.visit(this, null); ast.type = new RecordTypeDenoter(rType, ast.position); return ast.type; } @Override public Object visitUnaryExpression(UnaryExpression ast, Object o) { TypeDenoter eType = (TypeDenoter) ast.E.visit(this, null); Declaration binding = (Declaration) ast.O.visit(this, null); if (binding == null) { reportUndeclared(ast.O); ast.type = StdEnvironment.errorType; } else if (!(binding instanceof UnaryOperatorDeclaration)) reporter.reportError("\"%\" is not a unary operator", ast.O.spelling, ast.O.position); else { UnaryOperatorDeclaration ubinding = (UnaryOperatorDeclaration) binding; if (!eType.equals(ubinding.ARG)) reporter.reportError("wrong argument type for \"%\"", ast.O.spelling, ast.O.position); ast.type = ubinding.RES; } return ast.type; } @Override public Object visitVnameExpression(VnameExpression ast, Object o) { ast.type = (TypeDenoter) ast.V.visit(this, null); return ast.type; } // Declarations // Always returns null. Does not use the given object. @Override public Object visitBinaryOperatorDeclaration(BinaryOperatorDeclaration ast, Object o) { return null; } @Override public Object visitConstDeclaration(ConstDeclaration ast, Object o) { TypeDenoter eType = (TypeDenoter) ast.E.visit(this, null); idTable.enter(ast.I.spelling, ast); if (ast.duplicated) reporter.reportError("identifier \"%\" already declared", ast.I.spelling, ast.position); return null; } @Override public Object visitFuncDeclaration(FuncDeclaration ast, Object o) { ast.T = (TypeDenoter) ast.T.visit(this, null); idTable.enter(ast.I.spelling, ast); // permits recursion if (ast.duplicated) reporter.reportError("identifier \"%\" already declared", ast.I.spelling, ast.position); idTable.openScope(); ast.FPS.visit(this, null); TypeDenoter eType = (TypeDenoter) ast.E.visit(this, null); idTable.closeScope(); if (!ast.T.equals(eType)) reporter.reportError("body of function \"%\" has wrong type", ast.I.spelling, ast.E.position); return null; } @Override public Object visitProcDeclaration(ProcDeclaration ast, Object o) { idTable.enter(ast.I.spelling, ast); // permits recursion if (ast.duplicated) reporter.reportError("identifier \"%\" already declared", ast.I.spelling, ast.position); idTable.openScope(); ast.FPS.visit(this, null); ast.C.visit(this, null); idTable.closeScope(); return null; } @Override public Object visitSequentialDeclaration(SequentialDeclaration ast, Object o) { ast.D1.visit(this, null); ast.D2.visit(this, null); return null; } @Override public Object visitTypeDeclaration(TypeDeclaration ast, Object o) { ast.T = (TypeDenoter) ast.T.visit(this, null); idTable.enter(ast.I.spelling, ast); if (ast.duplicated) reporter.reportError("identifier \"%\" already declared", ast.I.spelling, ast.position); return null; } @Override public Object visitUnaryOperatorDeclaration(UnaryOperatorDeclaration ast, Object o) { return null; } @Override public Object visitVarDeclaration(VarDeclaration ast, Object o) { ast.T = (TypeDenoter) ast.T.visit(this, null); idTable.enter(ast.I.spelling, ast); if (ast.duplicated) reporter.reportError("identifier \"%\" already declared", ast.I.spelling, ast.position); return null; } // Array Aggregates // Returns the TypeDenoter for the Array Aggregate. Does not use the // given object. @Override public Object visitMultipleArrayAggregate(MultipleArrayAggregate ast, Object o) { TypeDenoter eType = (TypeDenoter) ast.E.visit(this, null); TypeDenoter elemType = (TypeDenoter) ast.AA.visit(this, null); ast.elemCount = ast.AA.elemCount + 1; if (!eType.equals(elemType)) reporter.reportError("incompatible array-aggregate element", "", ast.E.position); return elemType; } @Override public Object visitSingleArrayAggregate(SingleArrayAggregate ast, Object o) { TypeDenoter elemType = (TypeDenoter) ast.E.visit(this, null); ast.elemCount = 1; return elemType; } // Record Aggregates // Returns the TypeDenoter for the Record Aggregate. Does not use the // given object. @Override public Object visitMultipleRecordAggregate(MultipleRecordAggregate ast, Object o) { TypeDenoter eType = (TypeDenoter) ast.E.visit(this, null); FieldTypeDenoter rType = (FieldTypeDenoter) ast.RA.visit(this, null); TypeDenoter fType = checkFieldIdentifier(rType, ast.I); if (fType != StdEnvironment.errorType) reporter.reportError("duplicate field \"%\" in record", ast.I.spelling, ast.I.position); ast.type = new MultipleFieldTypeDenoter(ast.I, eType, rType, ast.position); return ast.type; } @Override public Object visitSingleRecordAggregate(SingleRecordAggregate ast, Object o) { TypeDenoter eType = (TypeDenoter) ast.E.visit(this, null); ast.type = new SingleFieldTypeDenoter(ast.I, eType, ast.position); return ast.type; } // Formal Parameters // Always returns null. Does not use the given object. @Override public Object visitConstFormalParameter(ConstFormalParameter ast, Object o) { ast.T = (TypeDenoter) ast.T.visit(this, null); idTable.enter(ast.I.spelling, ast); if (ast.duplicated) reporter.reportError("duplicated formal parameter \"%\"", ast.I.spelling, ast.position); return null; } @Override public Object visitFuncFormalParameter(FuncFormalParameter ast, Object o) { idTable.openScope(); ast.FPS.visit(this, null); idTable.closeScope(); ast.T = (TypeDenoter) ast.T.visit(this, null); idTable.enter(ast.I.spelling, ast); if (ast.duplicated) reporter.reportError("duplicated formal parameter \"%\"", ast.I.spelling, ast.position); return null; } @Override public Object visitProcFormalParameter(ProcFormalParameter ast, Object o) { idTable.openScope(); ast.FPS.visit(this, null); idTable.closeScope(); idTable.enter(ast.I.spelling, ast); if (ast.duplicated) reporter.reportError("duplicated formal parameter \"%\"", ast.I.spelling, ast.position); return null; } @Override public Object visitVarFormalParameter(VarFormalParameter ast, Object o) { ast.T = (TypeDenoter) ast.T.visit(this, null); idTable.enter(ast.I.spelling, ast); if (ast.duplicated) reporter.reportError("duplicated formal parameter \"%\"", ast.I.spelling, ast.position); return null; } @Override public Object visitEmptyFormalParameterSequence(EmptyFormalParameterSequence ast, Object o) { return null; } @Override public Object visitMultipleFormalParameterSequence(MultipleFormalParameterSequence ast, Object o) { ast.FP.visit(this, null); ast.FPS.visit(this, null); return null; } @Override public Object visitSingleFormalParameterSequence(SingleFormalParameterSequence ast, Object o) { ast.FP.visit(this, null); return null; } // Actual Parameters // Always returns null. Uses the given FormalParameter. @Override public Object visitConstActualParameter(ConstActualParameter ast, Object o) { FormalParameter fp = (FormalParameter) o; TypeDenoter eType = (TypeDenoter) ast.E.visit(this, null); if (!(fp instanceof ConstFormalParameter)) reporter.reportError("const actual parameter not expected here", "", ast.position); else if (!eType.equals(((ConstFormalParameter) fp).T)) reporter.reportError("wrong type for const actual parameter", "", ast.E.position); return null; } @Override public Object visitFuncActualParameter(FuncActualParameter ast, Object o) { FormalParameter fp = (FormalParameter) o; Declaration binding = (Declaration) ast.I.visit(this, null); if (binding == null) reportUndeclared(ast.I); else if (!(binding instanceof FuncDeclaration || binding instanceof FuncFormalParameter)) reporter.reportError("\"%\" is not a function identifier", ast.I.spelling, ast.I.position); else if (!(fp instanceof FuncFormalParameter)) reporter.reportError("func actual parameter not expected here", "", ast.position); else { FormalParameterSequence FPS = null; TypeDenoter T = null; if (binding instanceof FuncDeclaration) { FPS = ((FuncDeclaration) binding).FPS; T = ((FuncDeclaration) binding).T; } else { FPS = ((FuncFormalParameter) binding).FPS; T = ((FuncFormalParameter) binding).T; } if (!FPS.equals(((FuncFormalParameter) fp).FPS)) reporter.reportError("wrong signature for function \"%\"", ast.I.spelling, ast.I.position); else if (!T.equals(((FuncFormalParameter) fp).T)) reporter.reportError("wrong type for function \"%\"", ast.I.spelling, ast.I.position); } return null; } @Override public Object visitProcActualParameter(ProcActualParameter ast, Object o) { FormalParameter fp = (FormalParameter) o; Declaration binding = (Declaration) ast.I.visit(this, null); if (binding == null) reportUndeclared(ast.I); else if (!(binding instanceof ProcDeclaration || binding instanceof ProcFormalParameter)) reporter.reportError("\"%\" is not a procedure identifier", ast.I.spelling, ast.I.position); else if (!(fp instanceof ProcFormalParameter)) reporter.reportError("proc actual parameter not expected here", "", ast.position); else { FormalParameterSequence FPS = null; if (binding instanceof ProcDeclaration) FPS = ((ProcDeclaration) binding).FPS; else FPS = ((ProcFormalParameter) binding).FPS; if (!FPS.equals(((ProcFormalParameter) fp).FPS)) reporter.reportError("wrong signature for procedure \"%\"", ast.I.spelling, ast.I.position); } return null; } @Override public Object visitVarActualParameter(VarActualParameter ast, Object o) { FormalParameter fp = (FormalParameter) o; TypeDenoter vType = (TypeDenoter) ast.V.visit(this, null); if (!ast.V.variable) reporter.reportError("actual parameter is not a variable", "", ast.V.position); else if (!(fp instanceof VarFormalParameter)) reporter.reportError("var actual parameter not expected here", "", ast.V.position); else if (!vType.equals(((VarFormalParameter) fp).T)) reporter.reportError("wrong type for var actual parameter", "", ast.V.position); return null; } @Override public Object visitEmptyActualParameterSequence(EmptyActualParameterSequence ast, Object o) { FormalParameterSequence fps = (FormalParameterSequence) o; if (!(fps instanceof EmptyFormalParameterSequence)) reporter.reportError("too few actual parameters", "", ast.position); return null; } @Override public Object visitMultipleActualParameterSequence(MultipleActualParameterSequence ast, Object o) { FormalParameterSequence fps = (FormalParameterSequence) o; if (!(fps instanceof MultipleFormalParameterSequence)) reporter.reportError("too many actual parameters", "", ast.position); else { ast.AP.visit(this, ((MultipleFormalParameterSequence) fps).FP); ast.APS.visit(this, ((MultipleFormalParameterSequence) fps).FPS); } return null; } @Override public Object visitSingleActualParameterSequence(SingleActualParameterSequence ast, Object o) { FormalParameterSequence fps = (FormalParameterSequence) o; if (!(fps instanceof SingleFormalParameterSequence)) reporter.reportError("incorrect number of actual parameters", "", ast.position); else { ast.AP.visit(this, ((SingleFormalParameterSequence) fps).FP); } return null; } // Type Denoters // Returns the expanded version of the TypeDenoter. Does not // use the given object. @Override public Object visitAnyTypeDenoter(AnyTypeDenoter ast, Object o) { return StdEnvironment.anyType; } @Override public Object visitArrayTypeDenoter(ArrayTypeDenoter ast, Object o) { ast.T = (TypeDenoter) ast.T.visit(this, null); if ((Integer.valueOf(ast.IL.spelling).intValue()) == 0) reporter.reportError("arrays must not be empty", "", ast.IL.position); return ast; } @Override public Object visitBoolTypeDenoter(BoolTypeDenoter ast, Object o) { return StdEnvironment.booleanType; } @Override public Object visitCharTypeDenoter(CharTypeDenoter ast, Object o) { return StdEnvironment.charType; } @Override public Object visitErrorTypeDenoter(ErrorTypeDenoter ast, Object o) { return StdEnvironment.errorType; } @Override public Object visitSimpleTypeDenoter(SimpleTypeDenoter ast, Object o) { Declaration binding = (Declaration) ast.I.visit(this, null); if (binding == null) { reportUndeclared(ast.I); return StdEnvironment.errorType; } else if (!(binding instanceof TypeDeclaration)) { reporter.reportError("\"%\" is not a type identifier", ast.I.spelling, ast.I.position); return StdEnvironment.errorType; } return ((TypeDeclaration) binding).T; } @Override public Object visitIntTypeDenoter(IntTypeDenoter ast, Object o) { return StdEnvironment.integerType; } @Override public Object visitRecordTypeDenoter(RecordTypeDenoter ast, Object o) { ast.FT = (FieldTypeDenoter) ast.FT.visit(this, null); return ast; } @Override public Object visitMultipleFieldTypeDenoter(MultipleFieldTypeDenoter ast, Object o) { ast.T = (TypeDenoter) ast.T.visit(this, null); ast.FT.visit(this, null); return ast; } @Override public Object visitSingleFieldTypeDenoter(SingleFieldTypeDenoter ast, Object o) { ast.T = (TypeDenoter) ast.T.visit(this, null); return ast; } // Literals, Identifiers and Operators @Override public Object visitCharacterLiteral(CharacterLiteral CL, Object o) { return StdEnvironment.charType; } @Override public Object visitIdentifier(Identifier I, Object o) { Declaration binding = idTable.retrieve(I.spelling); if (binding != null) I.decl = binding; return binding; } @Override public Object visitIntegerLiteral(IntegerLiteral IL, Object o) { return StdEnvironment.integerType; } @Override public Object visitOperator(Operator O, Object o) { Declaration binding = idTable.retrieve(O.spelling); if (binding != null) O.decl = binding; return binding; } // Value-or-variable names // Determines the address of a named object (constant or variable). // This consists of a base object, to which 0 or more field-selection // or array-indexing operations may be applied (if it is a record or // array). As much as possible of the address computation is done at // compile-time. Code is generated only when necessary to evaluate // index expressions at run-time. // currentLevel is the routine level where the v-name occurs. // frameSize is the anticipated size of the local stack frame when // the object is addressed at run-time. // It returns the description of the base object. // offset is set to the total of any field offsets (plus any offsets // due to index expressions that happen to be literals). // indexed is set to true iff there are any index expressions (other // than literals). In that case code is generated to compute the // offset due to these indexing operations at run-time. // Returns the TypeDenoter of the Vname. Does not use the // given object. @Override public Object visitDotVname(DotVname ast, Object o) { ast.type = null; TypeDenoter vType = (TypeDenoter) ast.V.visit(this, null); ast.variable = ast.V.variable; if (!(vType instanceof RecordTypeDenoter)) reporter.reportError("record expected here", "", ast.V.position); else { ast.type = checkFieldIdentifier(((RecordTypeDenoter) vType).FT, ast.I); if (ast.type == StdEnvironment.errorType) reporter.reportError("no field \"%\" in this record type", ast.I.spelling, ast.I.position); } return ast.type; } @Override public Object visitSimpleVname(SimpleVname ast, Object o) { ast.variable = false; ast.type = StdEnvironment.errorType; Declaration binding = (Declaration) ast.I.visit(this, null); if (binding == null) reportUndeclared(ast.I); else if (binding instanceof ConstDeclaration) { ast.type = ((ConstDeclaration) binding).E.type; ast.variable = false; } else if (binding instanceof VarDeclaration) { ast.type = ((VarDeclaration) binding).T; ast.variable = true; } else if (binding instanceof ConstFormalParameter) { ast.type = ((ConstFormalParameter) binding).T; ast.variable = false; } else if (binding instanceof VarFormalParameter) { ast.type = ((VarFormalParameter) binding).T; ast.variable = true; } else reporter.reportError("\"%\" is not a const or var identifier", ast.I.spelling, ast.I.position); return ast.type; } @Override public Object visitSubscriptVname(SubscriptVname ast, Object o) { TypeDenoter vType = (TypeDenoter) ast.V.visit(this, null); ast.variable = ast.V.variable; TypeDenoter eType = (TypeDenoter) ast.E.visit(this, null); if (vType != StdEnvironment.errorType) { if (!(vType instanceof ArrayTypeDenoter)) reporter.reportError("array expected here", "", ast.V.position); else { if (!eType.equals(StdEnvironment.integerType)) reporter.reportError("Integer expression expected here", "", ast.E.position); ast.type = ((ArrayTypeDenoter) vType).T; } } return ast.type; } // Programs @Override public Object visitProgram(Program ast, Object o) { ast.C.visit(this, null); return null; } // Checks whether the source program, represented by its AST, satisfies the // language's scope rules and type rules. // Also decorates the AST as follows: // (a) Each applied occurrence of an identifier or operator is linked to // the corresponding declaration of that identifier or operator. // (b) Each expression and value-or-variable-name is decorated by its type. // (c) Each type identifier is replaced by the type it denotes. // Types are represented by small ASTs. public void check(Program ast) { ast.visit(this, null); } ///////////////////////////////////////////////////////////////////////////// public Checker(ErrorReporter reporter) { this.reporter = reporter; this.idTable = new IdentificationTable(); establishStdEnvironment(); } private IdentificationTable idTable; private static SourcePosition dummyPos = new SourcePosition(); private ErrorReporter reporter; // Reports that the identifier or operator used at a leaf of the AST // has not been declared. private void reportUndeclared(Terminal leaf) { reporter.reportError("\"%\" is not declared", leaf.spelling, leaf.position); } private static TypeDenoter checkFieldIdentifier(FieldTypeDenoter ast, Identifier I) { if (ast instanceof MultipleFieldTypeDenoter) { MultipleFieldTypeDenoter ft = (MultipleFieldTypeDenoter) ast; if (ft.I.spelling.compareTo(I.spelling) == 0) { I.decl = ast; return ft.T; } else { return checkFieldIdentifier(ft.FT, I); } } else if (ast instanceof SingleFieldTypeDenoter) { SingleFieldTypeDenoter ft = (SingleFieldTypeDenoter) ast; if (ft.I.spelling.compareTo(I.spelling) == 0) { I.decl = ast; return ft.T; } } return StdEnvironment.errorType; } // Creates a small AST to represent the "declaration" of a standard // type, and enters it in the identification table. private TypeDeclaration declareStdType(String id, TypeDenoter typedenoter) { TypeDeclaration binding; binding = new TypeDeclaration(new Identifier(id, dummyPos), typedenoter, dummyPos); idTable.enter(id, binding); return binding; } // Creates a small AST to represent the "declaration" of a standard // type, and enters it in the identification table. private ConstDeclaration declareStdConst(String id, TypeDenoter constType) { IntegerExpression constExpr; ConstDeclaration binding; // constExpr used only as a placeholder for constType constExpr = new IntegerExpression(null, dummyPos); constExpr.type = constType; binding = new ConstDeclaration(new Identifier(id, dummyPos), constExpr, dummyPos); idTable.enter(id, binding); return binding; } // Creates a small AST to represent the "declaration" of a standard // type, and enters it in the identification table. private ProcDeclaration declareStdProc(String id, FormalParameterSequence fps) { ProcDeclaration binding; binding = new ProcDeclaration(new Identifier(id, dummyPos), fps, new EmptyCommand(dummyPos), dummyPos); idTable.enter(id, binding); return binding; } // Creates a small AST to represent the "declaration" of a standard // type, and enters it in the identification table. private FuncDeclaration declareStdFunc(String id, FormalParameterSequence fps, TypeDenoter resultType) { FuncDeclaration binding; binding = new FuncDeclaration(new Identifier(id, dummyPos), fps, resultType, new EmptyExpression(dummyPos), dummyPos); idTable.enter(id, binding); return binding; } // Creates a small AST to represent the "declaration" of a // unary operator, and enters it in the identification table. // This "declaration" summarises the operator's type info. private UnaryOperatorDeclaration declareStdUnaryOp(String op, TypeDenoter argType, TypeDenoter resultType) { UnaryOperatorDeclaration binding; binding = new UnaryOperatorDeclaration(new Operator(op, dummyPos), argType, resultType, dummyPos); idTable.enter(op, binding); return binding; } // Creates a small AST to represent the "declaration" of a // binary operator, and enters it in the identification table. // This "declaration" summarises the operator's type info. private BinaryOperatorDeclaration declareStdBinaryOp(String op, TypeDenoter arg1Type, TypeDenoter arg2type, TypeDenoter resultType) { BinaryOperatorDeclaration binding; binding = new BinaryOperatorDeclaration(new Operator(op, dummyPos), arg1Type, arg2type, resultType, dummyPos); idTable.enter(op, binding); return binding; } // Creates small ASTs to represent the standard types. // Creates small ASTs to represent "declarations" of standard types, // constants, procedures, functions, and operators. // Enters these "declarations" in the identification table. private final static Identifier dummyI = new Identifier("", dummyPos); private void establishStdEnvironment() { // idTable.startIdentification(); StdEnvironment.booleanType = new BoolTypeDenoter(dummyPos); StdEnvironment.integerType = new IntTypeDenoter(dummyPos); StdEnvironment.charType = new CharTypeDenoter(dummyPos); StdEnvironment.anyType = new AnyTypeDenoter(dummyPos); StdEnvironment.errorType = new ErrorTypeDenoter(dummyPos); StdEnvironment.booleanDecl = declareStdType("Boolean", StdEnvironment.booleanType); StdEnvironment.falseDecl = declareStdConst("false", StdEnvironment.booleanType); StdEnvironment.trueDecl = declareStdConst("true", StdEnvironment.booleanType); StdEnvironment.notDecl = declareStdUnaryOp("\\", StdEnvironment.booleanType, StdEnvironment.booleanType); StdEnvironment.andDecl = declareStdBinaryOp("/\\", StdEnvironment.booleanType, StdEnvironment.booleanType, StdEnvironment.booleanType); StdEnvironment.orDecl = declareStdBinaryOp("\\/", StdEnvironment.booleanType, StdEnvironment.booleanType, StdEnvironment.booleanType); StdEnvironment.integerDecl = declareStdType("Integer", StdEnvironment.integerType); StdEnvironment.maxintDecl = declareStdConst("maxint", StdEnvironment.integerType); StdEnvironment.addDecl = declareStdBinaryOp("+", StdEnvironment.integerType, StdEnvironment.integerType, StdEnvironment.integerType); StdEnvironment.subtractDecl = declareStdBinaryOp("-", StdEnvironment.integerType, StdEnvironment.integerType, StdEnvironment.integerType); StdEnvironment.multiplyDecl = declareStdBinaryOp("*", StdEnvironment.integerType, StdEnvironment.integerType, StdEnvironment.integerType); StdEnvironment.divideDecl = declareStdBinaryOp("/", StdEnvironment.integerType, StdEnvironment.integerType, StdEnvironment.integerType); StdEnvironment.moduloDecl = declareStdBinaryOp("//", StdEnvironment.integerType, StdEnvironment.integerType, StdEnvironment.integerType); StdEnvironment.lessDecl = declareStdBinaryOp("<", StdEnvironment.integerType, StdEnvironment.integerType, StdEnvironment.booleanType); StdEnvironment.notgreaterDecl = declareStdBinaryOp("<=", StdEnvironment.integerType, StdEnvironment.integerType, StdEnvironment.booleanType); StdEnvironment.greaterDecl = declareStdBinaryOp(">", StdEnvironment.integerType, StdEnvironment.integerType, StdEnvironment.booleanType); StdEnvironment.notlessDecl = declareStdBinaryOp(">=", StdEnvironment.integerType, StdEnvironment.integerType, StdEnvironment.booleanType); StdEnvironment.charDecl = declareStdType("Char", StdEnvironment.charType); StdEnvironment.chrDecl = declareStdFunc("chr", new SingleFormalParameterSequence( new ConstFormalParameter(dummyI, StdEnvironment.integerType, dummyPos), dummyPos), StdEnvironment.charType); StdEnvironment.ordDecl = declareStdFunc("ord", new SingleFormalParameterSequence( new ConstFormalParameter(dummyI, StdEnvironment.charType, dummyPos), dummyPos), StdEnvironment.integerType); StdEnvironment.eofDecl = declareStdFunc("eof", new EmptyFormalParameterSequence(dummyPos), StdEnvironment.booleanType); StdEnvironment.eolDecl = declareStdFunc("eol", new EmptyFormalParameterSequence(dummyPos), StdEnvironment.booleanType); StdEnvironment.getDecl = declareStdProc("get", new SingleFormalParameterSequence( new VarFormalParameter(dummyI, StdEnvironment.charType, dummyPos), dummyPos)); StdEnvironment.putDecl = declareStdProc("put", new SingleFormalParameterSequence( new ConstFormalParameter(dummyI, StdEnvironment.charType, dummyPos), dummyPos)); StdEnvironment.getintDecl = declareStdProc("getint", new SingleFormalParameterSequence( new VarFormalParameter(dummyI, StdEnvironment.integerType, dummyPos), dummyPos)); StdEnvironment.putintDecl = declareStdProc("putint", new SingleFormalParameterSequence( new ConstFormalParameter(dummyI, StdEnvironment.integerType, dummyPos), dummyPos)); StdEnvironment.geteolDecl = declareStdProc("geteol", new EmptyFormalParameterSequence(dummyPos)); StdEnvironment.puteolDecl = declareStdProc("puteol", new EmptyFormalParameterSequence(dummyPos)); StdEnvironment.equalDecl = declareStdBinaryOp("=", StdEnvironment.anyType, StdEnvironment.anyType, StdEnvironment.booleanType); StdEnvironment.unequalDecl = declareStdBinaryOp("\\=", StdEnvironment.anyType, StdEnvironment.anyType, StdEnvironment.booleanType); } }