Ejemplo n.º 1
0
        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");
            }
        }