public override void Visit(AST_nonleaf node) { int children = node.NumChildren; switch (node.Tag) { case NodeType.Program: // do a multiple pre-pass over the declarations of structs, methods and consts prePass(node[2]); // now check that the using list is OK node[0].Accept(this); // now typecheck the bodies of method declarations node[2].Accept(this); break; case NodeType.Const: // get type of value node[2].Accept(this); CbType typact = node[2].Type; // look up declared type (can only be int or string) CbType typdec = lookUpIdenType(node[0]); if (typdec != CbType.Int && typdec != CbType.String) { ReportError(node[0].LineNumber, "Constants can only be of type int or string"); } else if (typact != CbType.Int && typact != CbType.String) { ReportError(node[0].LineNumber, "Constants must be set to a number or string constant"); } else if (typdec != typact) { ReportError(node[0].LineNumber, "Mismatched constant type declaration ({0} = {1})", typdec, typact); } break; case NodeType.Struct: // Nothing to do ... this has been handled by the prePass method below break; case NodeType.Method: // We have to typecheck the method localSymbols.Empty(); // clear the symbol table node[2].Accept(this); string name = ((AST_leaf)(node[1])).Sval; CbMethod meth = getMethod(name, node[2], false); if (meth == null) { ReportError(node[0].LineNumber, "Unknown method encountered: ({0})", name); } else { // 1. initialize the symbol table with the formal parameters CbType rettyp = meth.ResultType; AST formals = node[2]; for (int i = 0; i < formals.NumChildren; i++) { SymTabEntry ste = localSymbols.Binding(((AST_leaf)formals[i][1]).Sval, node.LineNumber); ste.Type = lookUpType(formals[i][0]); } // 2. visit the body of the method, type checking it AST block = node[3]; LocalVar = 0; block.Accept(this); // check that return statements match return type of method for (int i = 0; i < block.NumChildren; i++) { if (block[i].Tag == NodeType.Return && block[i].Type != rettyp) { ReportError(block[i].LineNumber, "Return statement type ({0}) doesn't match method return type ({1})", block[i].Type, rettyp); } } } // set ival to local number of variables in method ((AST_leaf)(node[1])).Ival = LocalVar; break; case NodeType.FieldDecl: // Nothing to do ... this has been handled by the prePass method below break; case NodeType.Formal: node.Type = lookUpType(node[0]); // Nothing else to do ... this has been handled by the prePass method below break; case NodeType.Array: node[0].Accept(this); break; case NodeType.LocalDecl: CbType typ = lookUpType(node[0]); // add identifiers to symbol table AST idList = node[1]; // add number of local variables to local list LocalVar += idList.NumChildren; for (int i = 0; i < idList.NumChildren; i++) { // check for duplicates if (localSymbols.Lookup(((AST_leaf)idList[i]).Sval) != null) { ReportError(node[0].LineNumber, "Duplicate variable declaration: {0}", ((AST_leaf)idList[i]).Sval); } else { localSymbols.Binding(((AST_leaf)idList[i]).Sval, node.LineNumber).Type = typ; } } break; case NodeType.Assign: // check left side node[0].Accept(this); // check right side node[1].Accept(this); // check that right and left hand sides have the same type if (node[0].Type != node[1].Type && node[0].Type != CbType.Error && node[1].Type != CbType.Error) { ReportError(node[0].LineNumber, "Cannot convert from type {1} to {0}", node[0].Type, node[1].Type); } node.Type = node[0].Type; break; case NodeType.Call: node.Type = CbType.Error; // check parameters node[1].Accept(this); // find calling method name string mname = null; // check for cbio.write if (node[0].Tag == NodeType.Dot) { if (node[0][0].Tag != NodeType.Ident || node[0][1].Tag != NodeType.Ident) { ReportError(node[0].LineNumber, "Invalid method call"); } else { mname = ((AST_leaf)node[0][0]).Sval + "." + ((AST_leaf)node[0][1]).Sval; } } else { mname = ((AST_leaf)node[0]).Sval; } if (mname != null && node[1].Type != CbType.Error) { // check for valid method call CbMethod method = getMethod(mname, node[1], true); if (method != null) { node.Type = method.ResultType; } } break; case NodeType.PlusPlus: basicTypeCheck(node, null, false, false, CbType.Int); // no type declaration break; case NodeType.MinusMinus: basicTypeCheck(node, null, false, false, CbType.Int); // no type declaration break; case NodeType.If: int temp1, temp2; // check boolean parameter node[0].Accept(this); if (node[0].Type != CbType.Bool && node[0].Type != CbType.Error) { ReportError(node[0].LineNumber, "Invalid boolean expression for if statement"); } // store current # of local variables temp1 = LocalVar; LocalVar = 0; // now check the do statement node[1].Accept(this); temp2 = LocalVar; // now check for the else statement node[2].Accept(this); // add # of local variables of function to global variable if (temp2 > LocalVar) { LocalVar = temp1 + temp2; } else { LocalVar = temp1 + LocalVar; } // no type declaration break; case NodeType.While: // check boolean parameter node[0].Accept(this); // now check the do statement node[1].Accept(this); if (node[0].Type != CbType.Bool) { ReportError(node[0].LineNumber, "Invalid boolean expression for while statement"); } // no type declaration break; case NodeType.Read: // The two children should be the method 'cbio.read' and the variable v // don't visit LHS, just check that call is to cbio.read and type of parameter is int node[1].Accept(this); if (node[0].Tag != NodeType.Dot || node[1].Type != CbType.Int || node[0][0].Tag != NodeType.Ident || node[0][1].Tag != NodeType.Ident || ((AST_leaf)node[0][0]).Sval != "cbio" || ((AST_leaf)node[0][1]).Sval != "read") { ReportError(node[0].LineNumber, "Invalid read method call"); } break; case NodeType.Add: basicTypeCheck(node, CbType.Int, false, false, CbType.Int); break; case NodeType.Sub: basicTypeCheck(node, CbType.Int, false, false, CbType.Int); break; case NodeType.Mul: basicTypeCheck(node, CbType.Int, false, false, CbType.Int); break; case NodeType.Div: basicTypeCheck(node, CbType.Int, false, false, CbType.Int); break; case NodeType.Mod: basicTypeCheck(node, CbType.Int, false, false, CbType.Int); break; case NodeType.And: basicTypeCheck(node, CbType.Bool, false, false, CbType.Bool); break; case NodeType.Or: basicTypeCheck(node, CbType.Bool, false, false, CbType.Bool); break; case NodeType.Equals: basicTypeCheck(node, CbType.Bool, true, true, CbType.Int, CbType.String); break; case NodeType.NotEquals: basicTypeCheck(node, CbType.Bool, true, true, CbType.Int, CbType.String); break; case NodeType.LessThan: basicTypeCheck(node, CbType.Bool, false, false, CbType.Int, CbType.String); break; case NodeType.GreaterThan: basicTypeCheck(node, CbType.Bool, false, false, CbType.Int, CbType.String); break; case NodeType.LessOrEqual: basicTypeCheck(node, CbType.Bool, false, false, CbType.Int, CbType.String); break; case NodeType.GreaterOrEqual: basicTypeCheck(node, CbType.Bool, false, false, CbType.Int, CbType.String); break; case NodeType.UnaryMinus: basicTypeCheck(node, CbType.Int, false, false, CbType.Int); break; case NodeType.Dot: // visit left hand side node[0].Accept(this); string rhs = ((AST_leaf)node[1]).Sval; node.Type = CbType.Error; // semantic check for string.Length or arr.Length if (node[0].Type == CbType.String || node[0].Type is CbArray) { if (rhs == "Length") { node.Type = CbType.Int; } else { ReportError(node[0].LineNumber, "Invalid usage of .Length"); } } else if (node[0].Type != CbType.Error) { if (node[0].Type is CbStruct) { // check fields of struct string structname = ((CbStruct)node[0].Type).Name; IDictionary <string, CbField> fields = ((CbStruct)node[0].Type).Fields; CbField field; if (fields.TryGetValue(rhs, out field)) { node.Type = field.Type; } else { ReportError(node[0].LineNumber, "Invalid field {0} of struct {1}", rhs, structname); } } else { ReportError(node[0].LineNumber, "Invalid usage of dot on type {0}", node[0].Type); } } break; case NodeType.NewStruct: // look up type node.Type = lookUpType(node[0]); break; case NodeType.NewArray: // visit array size node[1].Accept(this); node.Type = CbType.Error; // check array type if (node[1].Type != CbType.Int && node[1].Type != CbType.Error) { ReportError(node[1].LineNumber, "Array size must be of type int"); } else if (node[1].Type != CbType.Error) { // declare type node.Type = CbType.Array(lookUpType(node[0])); // check for invalid type if (node.Type == CbType.Error) { ReportError(node[0].LineNumber, "Invalid array type {0}", ((AST_leaf)node[0]).Sval); } } break; case NodeType.Index: // visit LHS node[0].Accept(this); // visit RHS node[1].Accept(this); // perform error checking node.Type = CbType.Error; if (node[1].Type != CbType.Int && node[1].Type != CbType.Error) { ReportError(node[0].LineNumber, "Array index type must be int, not {0}", node[1].Type); } else if (!(node[0].Type is CbArray) && node[0].Type != CbType.Error) { ReportError(node[0].LineNumber, "Array indexing used on non-array type {0}", node[0].Type); } else if (node[0].Type != CbType.Error && node[1].Type != CbType.Error) { node.Type = ((CbArray)node[0].Type).ElementType; } break; default: throw new Exception("{0} is not a tag compatible with an AST_nonleaf node"); } }