// adds all predeclared identifiers and methods private void addPredeclared() { // cbio // void read( out int val ) string name = "cbio.read"; CbType typm = CbType.Void; CbMethod method = new CbMethod(name, typm); method.ArgType.Add(CbType.Int); List <CbMethod> read = new List <CbMethod>(); read.Add(method); // void write( int val ) name = "cbio.write"; method = new CbMethod(name, typm); method.ArgType.Add(CbType.Int); List <CbMethod> writes = new List <CbMethod>(); writes.Add(method); // void write( string val ) method = new CbMethod(name, typm); method.ArgType.Add(CbType.String); writes.Add(method); Methods.Add(name, writes); }
// Makes two shallow passes over the declarations inside a class to obtain // the names and types of all consts, methods and structs declared at the // top level; without this info, typechecking cannot begin. private void prePass( AST node ) { addPredeclared(); AST_kary decls = node as AST_kary; if (decls == null || decls.Tag != NodeType.DeclList) throw new Exception("Bad argument passed to prePass"); // make one pass over the declarations just to enter the names of structs into // the Structs table int arity = decls.NumChildren; for( int i = 0; i < arity; i++ ) { AST ch = decls[i]; if (ch.Tag != NodeType.Struct) continue; // it was not a struct declaration string name = ((AST_leaf)(ch[0])).Sval; if (Structs.ContainsKey(name)) ReportError(ch[0].LineNumber, "Duplicate declaration of struct {0}", name); else Structs.Add(name, new CbStruct(name)); } // now make a second pass over the declarations to // 1. add const declarations to the Consts table // 2. add method declarations to the Methods table // 3. fill in the field details for each struct in the Structs table for( int i = 0; i < arity; i++ ) { AST ch = decls[i]; string name; CbType typ; int argsize; switch(ch.Tag) { case NodeType.Struct: CbStruct str; name = ((AST_leaf)(ch[0])).Sval; // find existing struct Structs.TryGetValue(name, out str); // do error checking AST fldlst = ch[1]; AST fld; AST idlst; argsize = fldlst.NumChildren; int idsize; // iterate through the field list for (int j = 0; j < argsize; j++) { // get field declaration fld = fldlst[j]; // find declaration type typ = lookUpType(fld[0]); // get list of variables declared idlst = fld[1]; idsize = idlst.NumChildren; // add all declared variables to the struct for(int k = 0; k < idsize; k++) str.AddField(((AST_leaf)(idlst[k])).Sval, typ); } break; case NodeType.Const: // Add the name and type of this constant to the Consts table name = ((AST_leaf)(ch[1])).Sval; typ = lookUpType(ch[0]); if (Consts.ContainsKey(name)) ReportError(ch[0].LineNumber, "Duplicate declaration of const {0}", name); else Consts.Add(name, typ); break; case NodeType.Method: name = ((AST_leaf)(ch[1])).Sval; ch[2].Accept(this); // check for duplicate method CbMethod existing = getMethod(name, ch[2], false); if (existing != null) { ReportError(ch[0].LineNumber, "Duplicate declaration of method {0}", name); break; } CbType typm = CbType.Void; // create new CbMethod if(ch[0] != null) typm = lookUpType(ch[0]); CbMethod method = new CbMethod(name, typm); // add argument list (full signature) to CbMethod AST args = ch[2]; for (int j = 0; j < args.NumChildren; j++) { method.ArgType.Add(lookUpType(args[j][0])); } // get list of overloaded methods sharing same name List<CbMethod> methods; if (!Methods.TryGetValue(name, out methods)) // if none exist create new list methods = new List<CbMethod>(); // add method to list methods.Add(method); // replace old list of methods with this name with new list Methods[name] = methods; break; default: throw new Exception("Unexpected node type " + ch.Tag); } } }
// adds all predeclared identifiers and methods private void addPredeclared() { // cbio // void read( out int val ) string name = "cbio.read"; CbType typm = CbType.Void; CbMethod method = new CbMethod(name, typm); method.ArgType.Add(CbType.Int); List<CbMethod> read = new List<CbMethod>(); read.Add(method); // void write( int val ) name = "cbio.write"; method = new CbMethod(name, typm); method.ArgType.Add(CbType.Int); List<CbMethod> writes = new List<CbMethod>(); writes.Add(method); // void write( string val ) method = new CbMethod(name, typm); method.ArgType.Add(CbType.String); writes.Add(method); Methods.Add(name, writes); }
// Makes two shallow passes over the declarations inside a class to obtain // the names and types of all consts, methods and structs declared at the // top level; without this info, typechecking cannot begin. private void prePass(AST node) { addPredeclared(); AST_kary decls = node as AST_kary; if (decls == null || decls.Tag != NodeType.DeclList) { throw new Exception("Bad argument passed to prePass"); } // make one pass over the declarations just to enter the names of structs into // the Structs table int arity = decls.NumChildren; for (int i = 0; i < arity; i++) { AST ch = decls[i]; if (ch.Tag != NodeType.Struct) { continue; // it was not a struct declaration } string name = ((AST_leaf)(ch[0])).Sval; if (Structs.ContainsKey(name)) { ReportError(ch[0].LineNumber, "Duplicate declaration of struct {0}", name); } else { Structs.Add(name, new CbStruct(name)); } } // now make a second pass over the declarations to // 1. add const declarations to the Consts table // 2. add method declarations to the Methods table // 3. fill in the field details for each struct in the Structs table for (int i = 0; i < arity; i++) { AST ch = decls[i]; string name; CbType typ; int argsize; switch (ch.Tag) { case NodeType.Struct: CbStruct str; name = ((AST_leaf)(ch[0])).Sval; // find existing struct Structs.TryGetValue(name, out str); // do error checking AST fldlst = ch[1]; AST fld; AST idlst; argsize = fldlst.NumChildren; int idsize; // iterate through the field list for (int j = 0; j < argsize; j++) { // get field declaration fld = fldlst[j]; // find declaration type typ = lookUpType(fld[0]); // get list of variables declared idlst = fld[1]; idsize = idlst.NumChildren; // add all declared variables to the struct for (int k = 0; k < idsize; k++) { str.AddField(((AST_leaf)(idlst[k])).Sval, typ); } } break; case NodeType.Const: // Add the name and type of this constant to the Consts table name = ((AST_leaf)(ch[1])).Sval; typ = lookUpType(ch[0]); if (Consts.ContainsKey(name)) { ReportError(ch[0].LineNumber, "Duplicate declaration of const {0}", name); } else { Consts.Add(name, typ); } break; case NodeType.Method: name = ((AST_leaf)(ch[1])).Sval; ch[2].Accept(this); // check for duplicate method CbMethod existing = getMethod(name, ch[2], false); if (existing != null) { ReportError(ch[0].LineNumber, "Duplicate declaration of method {0}", name); break; } CbType typm = CbType.Void; // create new CbMethod if (ch[0] != null) { typm = lookUpType(ch[0]); } CbMethod method = new CbMethod(name, typm); // add argument list (full signature) to CbMethod AST args = ch[2]; for (int j = 0; j < args.NumChildren; j++) { method.ArgType.Add(lookUpType(args[j][0])); } // get list of overloaded methods sharing same name List <CbMethod> methods; if (!Methods.TryGetValue(name, out methods)) // if none exist create new list { methods = new List <CbMethod>(); } // add method to list methods.Add(method); // replace old list of methods with this name with new list Methods[name] = methods; break; default: throw new Exception("Unexpected node type " + ch.Tag); } } }
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"); } }