public override void Visit(AST_nonleaf node) { printTag(node); f.WriteLine(); int arity = node.NumChildren; indent++; for (int i = 0; i < arity; i++) { AST ch = node[i]; if (ch != null) { ch.Accept(this); } else { f.WriteLine("{0}-- missing child --", indentString(indent)); } } if (arity == 0) { f.WriteLine("{0}-- no children --", indentString(indent)); } indent--; }
public override void Visit( AST_nonleaf node ) { printTag(node); f.WriteLine(); int arity = node.NumChildren; indent++; for( int i = 0; i < arity; i++ ) { AST ch = node[i]; if (ch != null) ch.Accept(this); else f.WriteLine("{0}-- missing child --", indentString(indent)); } if (arity == 0) f.WriteLine("{0}-- no children --", indentString(indent)); indent--; }
// check if child nodes are equal to "tleaf" value, then set the node type equal to "tnode" private void basicTypeCheck( AST_nonleaf node, CbType tnode, bool arrays, bool structs, params CbType[] tleaves) { int children = node.NumChildren; node.Type = CbType.Error; bool err = false; bool suppress = false; CbType typ = null; // determine if type of first child matches accepted list node[0].Accept(this); if (node[0].Type == CbType.Error) { typ = CbType.Error; suppress = true; } else if (arrays && node[0].Type is CbArray || structs && node[0].Type is CbStruct) typ = node[0].Type; else { foreach (CbType tleaf in tleaves) { if (node[0].Type == tleaf) { typ = tleaf; break; } } if (typ == null) err = true; } // determine if types of other children match the first for (int i = 1; i < children; i++) { node[i].Accept(this); if (node[i].Type == CbType.Error) suppress = true; if (node[i].Type != typ) err = true; } if (err && !suppress) { if (children > 1) ReportError(node[0].LineNumber, "Cannot perform {0} operation on types '{1}' and '{2}'", node.Tag, node[0].Type, node[1].Type); else ReportError(node[0].LineNumber, "Cannot perform {0} operation on type '{1}'", node.Tag, node[0].Type); return; } if(tnode != null) node.Type = tnode; return; }
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]; 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); } } 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]; 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: // 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"); // now check the do statement node[1].Accept(this); // now check for the else statement node[2].Accept(this); // 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"); } }
// check if child nodes are equal to "tleaf" value, then set the node type equal to "tnode" private void basicTypeCheck(AST_nonleaf node, CbType tnode, bool arrays, bool structs, params CbType[] tleaves) { int children = node.NumChildren; node.Type = CbType.Error; bool err = false; bool suppress = false; CbType typ = null; // determine if type of first child matches accepted list node[0].Accept(this); if (node[0].Type == CbType.Error) { typ = CbType.Error; suppress = true; } else if (arrays && node[0].Type is CbArray || structs && node[0].Type is CbStruct) { typ = node[0].Type; } else { foreach (CbType tleaf in tleaves) { if (node[0].Type == tleaf) { typ = tleaf; break; } } if (typ == null) { err = true; } } // determine if types of other children match the first for (int i = 1; i < children; i++) { node[i].Accept(this); if (node[i].Type == CbType.Error) { suppress = true; } if (node[i].Type != typ) { err = true; } } if (err && !suppress) { if (children > 1) { ReportError(node[0].LineNumber, "Cannot perform {0} operation on types '{1}' and '{2}'", node.Tag, node[0].Type, node[1].Type); } else { ReportError(node[0].LineNumber, "Cannot perform {0} operation on type '{1}'", node.Tag, node[0].Type); } return; } if (tnode != null) { node.Type = tnode; } return; }
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"); } }
public virtual void Visit(AST_nonleaf n) { Console.WriteLine("Internal compiler error! This method should have been overridden"); }