// Nothing to resolve for literal expressions protected override Exp ResolveExpAsRight(ISemanticResolver s) { // Always resolve our children ResolveExpAsRight(ref m_left, s); ResolveExpAsRight(ref m_right, s); // If we don't match a predefined operator, then check for overloads if (!MatchesPredefinedOp(Op, this.Left.CLRType, this.Right.CLRType)) { // Packagage Left & Right into parameters for a method call ArgExp [] args = new ArgExp [2] { new ArgExp(EArgFlow.cIn, m_left), new ArgExp(EArgFlow.cIn, m_right) }; // Check for delegate combination // D operator+(D, D) // D operator-(D, D) if (AST.DelegateDecl.IsDelegate(m_left.CLRType)) { if (m_left.CLRType == m_right.CLRType) { if (Op == BinaryOp.cAdd || Op == BinaryOp.cSub) { System.Type d = m_left.CLRType; // Translates to: // op+ --> (D) System.Delegate.Combine(left, right) // op- --> (D) System.Delegate.Remove(left, right) TypeEntry tDelegate = s.LookupSystemType("MulticastDelegate"); string stName = (Op == BinaryOp.cAdd) ? "Combine" : "Remove"; bool dummy; MethodExpEntry sym = tDelegate.LookupMethod(s, new Identifier(stName), new Type[] { d, d}, out dummy); Exp call2 = new CastObjExp( new ResolvedTypeSig(d, s), new MethodCallExp( null,sym, args, s) ); Exp.ResolveExpAsRight(ref call2, s); return call2; } } } // end delgate op+ check // Check for System.String.Concat(). // @todo - this should be able to compress an entire subtree, not just 2 args. // (ie, a+b+c -> String.Concat(a,b,c); // So we can't merge this into the SearchForOverload. // But for now we'll be lazy... if ((Op == BinaryOp.cAdd) && (Left.CLRType == typeof(string) || Right.CLRType == typeof(string))) { Exp call2 = new MethodCallExp( new DotObjExp( new SimpleObjExp( new Identifier("System", this.m_filerange) ), new Identifier("String", this.m_filerange)), new Identifier("Concat", m_filerange), args); call2.SetLocation(this.Location); Exp.ResolveExpAsRight(ref call2, s); return call2; } MethodExpEntry m = SearchForOverloadedOp(s); if (m == null && (Op == BinaryOp.cEqu || Op == BinaryOp.cNeq)) { // If it's '==' or '!=', then it's ok if we didn't find // an overload. } else { // Couldn't find an overload, throw error if (m == null) { //ThrowError_NoAcceptableOperator(s, this.Location, m_left.CLRType, m_right.CLRType, Op); ThrowError(SymbolError.NoAcceptableOperator(this.Location, m_left.CLRType, m_right.CLRType, Op)); } // Replace this node w/ the method call MethodCallExp call = new MethodCallExp(null, m, args, s); call.SetLocation(this.Location); return call; } } CalcCLRType(s); return this; }
// Semantic resolution. // This is where we check for Set-Property transformations (where an // assignment gets changed into a methodcall) protected override Exp ResolveExpAsRight(ISemanticResolver s) { // Resolve the leftside of the operator Exp.ResolveExpAsLeft(ref m_oeLeft, s); // Event transform actually occurs in the assignment node. // A.e = A.e + d --> A.add_e(d) // We have to do this before we resolve the RHS of the operator (Since we // can't resolve events as a RHS). if (m_oeLeft is EventExp) { EventExp nodeEvent = (EventExp)m_oeLeft; EventExpEntry e = nodeEvent.Symbol; // Here we just do some asserts. BinaryExp b = this.m_expRight as BinaryExp; Debug.Assert(b != null, "bad formed event +=,-="); // By now, we know we have something of the form A = B + C // Make sure that A=B. Since we resolved A as left, must resolve B as left too. Exp eTempLeft = b.Left; Exp.ResolveExpAsLeft(ref eTempLeft, s); Debug.Assert(eTempLeft is EventExp); Debug.Assert(Object.ReferenceEquals(((EventExp) eTempLeft).Symbol, e)); // symbols should be exact references // Resolve C (the delegate that we're adding to the event) Exp eTempRight = b.Right; Exp.ResolveExpAsRight(ref eTempRight, s); Debug.Assert(AST.DelegateDecl.IsDelegate(eTempRight.CLRType), "Event only ops w/ delegates"); // @todo -legit/ Debug.Assert(b.Op == BinaryExp.BinaryOp.cAdd || b.Op == BinaryExp.BinaryOp.cSub); MethodExpEntry m2 = (b.Op == BinaryExp.BinaryOp.cAdd) ? e.AddMethod : e.RemoveMethod; Exp e2 = new MethodCallExp( nodeEvent.InstanceExp, m2, new ArgExp[] { new ArgExp(EArgFlow.cIn, eTempRight) }, s ); Exp.ResolveExpAsRight(ref e2, s); return e2; } Exp.ResolveExpAsRight(ref m_expRight, s); // Check for calling add_, remove on events // a.E += X // a.E = a.E + X (parser transforms) // if E is a delegate, and RHS is structured like E + X // then transform to a.add_E(X) or a.remove_E(x) // @todo - use the EventInfo to get exact add / remove functions if (DelegateDecl.IsDelegate(m_oeLeft.CLRType)) { // Events can only exist on a class AST.FieldExp f = m_oeLeft as FieldExp; if (f == null) goto NotAnEvent; Exp eInstance = f.InstanceExp; // ok if static BinaryExp rhs = m_expRight as BinaryExp; if (rhs == null) goto NotAnEvent; // Check if RHS is a.E + X if ((rhs.Left != m_oeLeft) || (rhs.Right.CLRType != rhs.Left.CLRType)) goto NotAnEvent; string stEventName = f.Symbol.Name; string stOpName; if (rhs.Op == BinaryExp.BinaryOp.cAdd) stOpName = "add_" + stEventName; else if (rhs.Op == BinaryExp.BinaryOp.cSub) stOpName = "remove_" + stEventName; else goto NotAnEvent; // a.add_E(X); Exp e = new MethodCallExp( eInstance, new Identifier(stOpName), new ArgExp[] { new ArgExp(EArgFlow.cIn, rhs.Right) } ); Exp.ResolveExpAsRight(ref e, s); e.SetLocation(this.Location); return e; NotAnEvent: ; } // Check for set-indexer if (m_oeLeft is ArrayAccessExp) { ArrayAccessExp a = m_oeLeft as ArrayAccessExp; if (a.IsIndexer) { // Leftside: get_Item(idx, value); System.Type [] alParams = new Type [] { a.ExpIndex.CLRType, m_expRight.CLRType }; TypeEntry t = s.ResolveCLRTypeToBlueType(a.Left.CLRType); MethodExpEntry m = t.LookupIndexer(a.Left.Location, s, alParams, true); Exp e = new MethodCallExp( a.Left, m, new ArgExp[] { new ArgExp(EArgFlow.cIn, a.ExpIndex), new ArgExp(EArgFlow.cIn, m_expRight) }, s); Exp.ResolveExpAsRight(ref e, s); e.SetLocation(this.Location); return e; } } // Check for transforming properties into MethodCalls if (m_oeLeft is PropertyExp) { PropertyExp p = (PropertyExp) m_oeLeft; Exp e = new MethodCallExp( p.InstanceExp, p.Symbol.SymbolSet, new ArgExp[] { new ArgExp(EArgFlow.cIn, m_expRight) }, s); Exp.ResolveExpAsRight(ref e, s); e.SetLocation(this.Location); return e; } CalcCLRType(s); // Ensure type match s.EnsureAssignable(m_expRight, m_oeLeft.CLRType); return this; }
//----------------------------------------------------------------------------- // Parse a single atom of an expression // Atom expressions are the basic building blocks of expressions and // don't contain any operators in them. // Atoms are combined together to form more complex expressions. // One caveat: // Type casting & parenthesis look really similar... //----------------------------------------------------------------------------- protected Exp ParseExpAtom() { // Parse a single term Token t = m_lexer.PeekNextToken(); // Either expression in parenthesis // could be a typecast if (t.TokenType == Token.Type.cLParen) { ConsumeNextToken(); Exp e = ParseExp(); ReadExpectedToken(Token.Type.cRParen); // Typecast if the next token is in the first set of an expression // --> (Type) exp t = m_lexer.PeekNextToken(); if (IsStartOfExp(t)) { TypeSig tSig = ConvertExpToType(e); Exp eSource = this.ParsePrimaryExp(); return new CastObjExp(tSig, eSource); } return e; } // Check for 'new' if (t.TokenType == Token.Type.cNew) return ParseNewExp(); // Check for identifier or methodcall // E -> i // E -> i ( ... ) if (t.TokenType == Token.Type.cId) { Identifier id = ReadExpectedIdentifier(); Token t2 = m_lexer.PeekNextToken(); // if next char after id is a '(', then this is a method call // with an implied 'this' pointer on the left side if (t2.TokenType == Token.Type.cLParen) { ArgExp [] arParams = ParseArgList(); MethodCallExp m = new MethodCallExp(null, id, arParams); return m; } else { return new SimpleObjExp(id); } } // Check for literals if (t.TokenType == Token.Type.cNull) { ConsumeNextToken(); return new NullExp(t.Location); } if (t.TokenType == Token.Type.cString) { ConsumeNextToken(); return new StringExp(t.Text, t.Location); } if (t.TokenType == Token.Type.cInt) { ConsumeNextToken(); return new IntExp(t.IntValue, t.Location); } if (t.TokenType == Token.Type.cChar) { ConsumeNextToken(); return new CharExp(t.CharValue, t.Location); } if (t.TokenType == Token.Type.cBool) { ConsumeNextToken(); return new BoolExp(t.BoolValue, t.Location); } ThrowError(E_UnexpectedToken(t)); return null; }
//----------------------------------------------------------------------------- // E -> E . i // E -> E . i (...) // E -> E [ E] //----------------------------------------------------------------------------- protected Exp ParsePrimaryExp() { Exp eFinal = ParseExpAtom(); // Now, since ObjExp are left-linear, we can actually parse them recursively // We parsed the base case, so we just keep iterating through deciding // which rule to apply. eFinal contains the root of the ast we're building Token t; while(true) { t = m_lexer.PeekNextToken(); // If next char is '.', then we're either doing: // E -> E . i // E -> E . i (...) if (t.TokenType == Token.Type.cDot) { ConsumeNextToken(); // eat the dot Identifier stId = ReadExpectedIdentifier(); Token t2 = m_lexer.PeekNextToken(); // MethodCall - if next character is a '(' // E -> E . i (...) if (t2.TokenType == Token.Type.cLParen) { ArgExp [] arParams = ParseArgList(); eFinal = new MethodCallExp(eFinal, stId, arParams); continue; } // Dot operator - for all other cases // E -> E . i else { eFinal = new DotObjExp(eFinal, stId); continue; } } // If next char is a '[', then this is an array access // E -> E [ E ] else if (t.TokenType == Token.Type.cLSquare) { ConsumeNextToken(); Exp eIdx = ParseExp(); ReadExpectedToken(Token.Type.cRSquare); eFinal = new ArrayAccessExp(eFinal, eIdx); continue; } // If we got to here, then we're done so break out of loop break; } // end while // @hack // Since expressions can be types (ie, that's how we parse a TypeCast) // Check if this is an array type if (t.TokenType == Token.Type.cLRSquare) { NonRefTypeSig sigElemType = new SimpleTypeSig(eFinal); TypeSig tSig = ParseOptionalArrayDecl(sigElemType); return new TempTypeExp(tSig); } return eFinal; }
protected override Exp ResolveExpAsRight(ISemanticResolver s) { //CalcCLRType(s); //return this; // Transform into a get Exp eResolved = new MethodCallExp( this.InstanceExp, Symbol.SymbolGet, new ArgExp[0], s ); Exp.ResolveExpAsRight(ref eResolved, s); return eResolved; }
// Internal helper. Since the left & right cases are close enough // we want to merge them into a function. private Exp ResolveInternal(ISemanticResolver s, bool fIsLeft) { ResolveExpAsRight(ref m_oeLeft, s); ResolveExpAsRight(ref m_expIndex, s); // @todo - check that m_expIndex is an integer // Check for indexers: // If the Left is not an array, then we must be an indexer. // Strip references, So T[]& --> T[] System.Type t = m_oeLeft.CLRType; if (t.IsByRef) t = t.GetElementType(); if (!t.IsArray) { m_fIsIndexer = true; // If we're the leftside, we have a problem. We don't know the exp on the RS, // so we don't have a full signature, so we don't know what we're supposed to // change too. So just leave it that we're an indexer and let our parent // in the AST resolve us. // But this also means that we don't have a good thing to set our CLR type too. // So we just don't call CalcCLRType(). That's ok since our parent will drop // this node immediately anyways. if (fIsLeft) { return this; } // Rightside: get_Item(idx); System.Type [] alParams = new Type [] { this.ExpIndex.CLRType }; TypeEntry tLeft = s.ResolveCLRTypeToBlueType(m_oeLeft.CLRType); MethodExpEntry m = tLeft.LookupIndexer(m_oeLeft.Location, s, alParams, fIsLeft); Exp e = new MethodCallExp( this.Left, m, new ArgExp[] { new ArgExp(EArgFlow.cIn, ExpIndex) }, s); Exp.ResolveExpAsRight(ref e, s); return e; } CalcCLRType(s); return this; }
// Semantic resolution protected override Exp ResolveExpAsRight(ISemanticResolver s) { // Only resolve once. if (m_symbol != null) return this; // First, resolve our parameters (because of overloading) // We need to know the URT types for our parameters // in order to resolve between overloaded operators Type [] alParamTypes = new Type[m_arParams.Length]; for(int i = 0; i < m_arParams.Length; i++) { Exp e = m_arParams[i]; ResolveExpAsRight(ref e, s); Debug.Assert(e == m_arParams[i]); Type tParam = e.CLRType; //if ((tParam !=null) && tParam.IsByRef) // tParam = tParam.GetElementType(); alParamTypes[i] = tParam; //Debug.Assert(alParamTypes[i] != null); } TypeEntry tCur = s.GetCurrentClass(); TypeEntry tLeft = null; // Type to lookup in // Is this a 'base' access? // Convert to the real type and set a non-virtual flag if (m_objExp is SimpleObjExp) { SimpleObjExp e = m_objExp as SimpleObjExp; if (e.Name.Text == "base") { // Set the scope that we lookup in. tLeft = tCur.Super; // Still need to resolve the expression. m_objExp = new SimpleObjExp("this"); m_fIsNotPolymorphic = true; } } #if true // See if we have a delegate here Exp eDelegate = null; if (m_objExp == null) { Exp e = new SimpleObjExp(m_idName); Exp.ResolveExpAsRight(ref e, s); if (!(e is SimpleObjExp)) eDelegate = e; } else { // If it's an interface, then we know we can't have a delegate field on it, // so short-circuit now. Exp.ResolveExpAsRight(ref m_objExp, s); if (!m_objExp.CLRType.IsInterface) { Exp e = new DotObjExp(m_objExp, m_idName); Exp.ResolveExpAsRight(ref e, s); if (!(e is DotObjExp)) eDelegate = e; } } if (eDelegate != null) { if (!DelegateDecl.IsDelegate(eDelegate.CLRType)) { //Debug.Assert(false, "@todo - " + m_strName + " is not a delegate or function"); // @todo - legit // Just fall through for now, method resolution will decide if this is a valid function } else { Exp e = new MethodCallExp( eDelegate, new Identifier("Invoke"), this.m_arParams ); Exp.ResolveExpAsRight(ref e, s); return e; } } #endif // No delegate, carry on with a normal function call // If there's no objexp, then the function is a method // of the current class. // make it either a 'this' or a static call if (m_objExp == null) { // Lookup bool fIsVarArgDummy; MethodExpEntry sym = tCur.LookupMethod(s, m_idName, alParamTypes, out fIsVarArgDummy); if (sym.IsStatic) { m_objExp = new TypeExp(tCur); } else { m_objExp = new SimpleObjExp("this"); } } // Need to Lookup m_strName in m_objExp's scope (inherited scope) Exp.ResolveExpAsRight(ref m_objExp, s); // Get type of of left side object // This call can either be a field on a variable // or a static method on a class bool fIsStaticMember = false; // If we don't yet know what TypeEntry this methodcall is on, then figure // it out based off the expression if (tLeft == null) { if (m_objExp is TypeExp) { fIsStaticMember = true; tLeft = ((TypeExp) m_objExp).Symbol; } else { fIsStaticMember = false; tLeft = s.ResolveCLRTypeToBlueType(m_objExp.CLRType); } } // Here's the big lookup. This will jump through all sorts of hoops to match // parameters, search base classes, do implied conversions, varargs, // deal with abstract, etc. bool fIsVarArg; m_symbol = tLeft.LookupMethod(s, m_idName, alParamTypes, out fIsVarArg); Debug.Assert(m_symbol != null); if (m_fIsNotPolymorphic) { // of the form 'base.X(....)' if (m_symbol.IsStatic) ThrowError(SymbolError.BaseAccessCantBeStatic(this.Location, m_symbol)); // @todo - PrintError? } else { // normal method call /* if (fIsStaticMember && !m_symbol.IsStatic) ThrowError(SymbolError.ExpectInstanceMember(this.Location)); // @todo - PrintError? else if (!fIsStaticMember && m_symbol.IsStatic) ThrowError(SymbolError.ExpectStaticMember(this.Location)); // @todo - PrintError? */ Debug.Assert(fIsStaticMember == m_symbol.IsStatic, "@todo - user error. Mismatch between static & instance members on line."); } // If we have a vararg, then transform it if (fIsVarArg) { // Create the array int cDecl = m_symbol.ParamCount; int cCall = this.ParamExps.Length; ArrayTypeSig tSig = new ArrayTypeSig(m_symbol.ParamCLRType(cDecl - 1), s); Node [] list = new Node[cCall - cDecl + 1]; for(int i = 0; i < list.Length; i++) { list[i] = this.ParamExps[i + cDecl - 1]; } Exp eArray = new NewArrayObjExp( tSig, new ArrayInitializer( list ) ); Exp.ResolveExpAsRight(ref eArray, s); // Change the parameters to use the array ArgExp [] arParams = new ArgExp[cDecl]; for(int i = 0; i < cDecl - 1; i++) arParams[i] = m_arParams[i]; arParams[cDecl - 1] = new ArgExp(EArgFlow.cIn, eArray); m_arParams = arParams; } // end vararg transformation this.CalcCLRType(s); return this; }