//----------------------------------------------------------------------------- // Parse a Type Sig // // ** rules ** // TypeSig -> id ('.' id)* '[ ','* ]'* //----------------------------------------------------------------------------- protected NonRefTypeSig ParseTypeSig() { // Currently, we implement this by parsing ObjExpressions. That's easier // for us, but may let us parse illegal things. That's ok. The TypeSig // container class along with semantic checking will still give us the // expected error control. NonRefTypeSig sig = null; Identifier stId = ReadExpectedIdentifier(); Exp o = new SimpleObjExp(stId); Token t = m_lexer.PeekNextToken(); while (t.TokenType == Token.Type.cDot) { ConsumeNextToken(); stId = ReadExpectedIdentifier(); o = new DotObjExp(o, stId); t = m_lexer.PeekNextToken(); } sig = new SimpleTypeSig(o); // Check for arrays while (t.TokenType == Token.Type.cLRSquare) { sig = new ArrayTypeSig(sig, 1); ConsumeNextToken(); t = m_lexer.PeekNextToken(); } return sig; }
//----------------------------------------------------------------------------- // Parse a list of identifiers separated by dots // // ** rules ** // id ( '.' id )* //----------------------------------------------------------------------------- protected Exp ParseDottedIdList() { Identifier stId = ReadExpectedIdentifier(); Exp o = new SimpleObjExp(stId); Token t = m_lexer.PeekNextToken(); while (t.TokenType == Token.Type.cDot) { ConsumeNextToken(); stId = ReadExpectedIdentifier(); o = new DotObjExp(o, stId); t = m_lexer.PeekNextToken(); } return o; }
// An ObjExp is just a temporary node. But that's the best a Context-Free parse can // do. So now that we're building a symbol table, we can do a Context-Sensitive resolution // and figure out what type of node this really is. public Exp GetResolvedNode(ISemanticResolver s, bool fRight) { Exp eResolved = null; // Lookup the symbol and determine what we are //SymEntry sym = s.LookupSymbol(this.m_strId, true); string stName = this.m_strId.Text; SymEntry sym = s.LookupSymbolWithContext(this.m_strId, false); // allow methods // Namespace if (sym is NamespaceEntry) { eResolved = new NamespaceExp(sym as NamespaceEntry); } // Local Variable else if (sym is LocalVarExpEntry) { eResolved = new LocalExp(sym as LocalVarExpEntry); } // Parameter else if (sym is ParamVarExpEntry) { eResolved = new ParamExp(sym as ParamVarExpEntry); } // A type name else if (sym is TypeEntry) { eResolved = new TypeExp(sym as TypeEntry); } // A field (w/ an implied 'this' pointer) else if (sym is FieldExpEntry) { // When a single identifier resolves to a field, it can be either // an instance field with an implied 'this' ref, or a static field of this class. FieldExpEntry f = sym as FieldExpEntry; Exp expInstance = null; if (!f.IsStatic) { expInstance = new SimpleObjExp("this"); Exp.ResolveExpAsRight(ref expInstance, s); } eResolved = new FieldExp(f, expInstance); } // An event (w/ an implied 'this' ptr) else if (sym is EventExpEntry) { EventExpEntry e = (EventExpEntry) sym; Exp expInstance = null; if (!e.Mods.IsStatic) { expInstance = new SimpleObjExp("this"); Exp.ResolveExpAsRight(ref expInstance, s); } eResolved = new EventExp(e, expInstance); } // A property (w/ an implied 'this' ptr). // Properties will eventually be converted into method calls. else if (sym is PropertyExpEntry) { PropertyExpEntry p = (PropertyExpEntry) sym; Exp expInstance = null; if (!p.IsStatic) { expInstance = new SimpleObjExp("this"); Exp.ResolveExpAsRight(ref expInstance, s); } eResolved = new PropertyExp(p, expInstance); } // Not recognized. else { if (stName == "this") // check a common error case... Debug.Assert(false, "Can't access 'this'. Are we in a static?"); if (sym == null) { MethodHeaderEntry h = s.GetCurrentClass().LookupMethodHeader(this.m_strId.Text); if (h != null) { return this; } ThrowError(SymbolError.UndefinedSymbol(m_strId)); //Debug.Assert(false, "Unknown name in SimpleObjExp:" + stName); } Debug.Assert(false, "Unknown symbol type:" + ((sym == null) ? "null" : sym.ToString())); } Debug.Assert(eResolved != null); return eResolved; }
// Do the real work protected void ParseStatementOrLocal_Helper(out Statement s, out LocalVarDecl v) { s = null; v = null; // For each statement, we know which type based off the first token. // Expect for an identifier, in which case it could be a few things. Token t = m_lexer.PeekNextToken(); #if false // Skip past any ';' (as empty statements) while(t.TokenType == Token.Type.cSemi) { ConsumeNextToken(); t = m_lexer.PeekNextToken(); } #endif if (IsStartOfExp(t)) { FileRange f = BeginRange(); // This could be either an expression or a type Exp e = ParseExp(); t = m_lexer.PeekNextToken(); // Case 1 - Var declaration: // If an identifier follows, then we just read a type and this is // a var declaration: // Type id ';' // Type id '=' exp ';' if (t.TokenType == Token.Type.cId) { TypeSig tSig = this.ConvertExpToType(e); Identifier id = ReadExpectedIdentifier(); v = new LocalVarDecl(id, tSig); // Check for optional assignment (if there's an '=' after the name) Token t3 = m_lexer.PeekNextToken(); if (t3.TokenType == Token.Type.cAssign) { ConsumeNextToken(); // '=' Exp eRHS = ParseExp(); // exp ReadExpectedToken(Token.Type.cSemi); // ';' SimpleObjExp oleft = new SimpleObjExp(id); StatementExp se = new AssignStmtExp(oleft, eRHS); s = new ExpStatement(se); se.SetLocation(EndRange(f)); } else { ReadExpectedToken(Token.Type.cSemi); // ';' } return; } // end decl case // Case 2 - label declaration else if (t.TokenType == Token.Type.cColon) { SimpleObjExp o2 = e as SimpleObjExp; if (o2 != null) { ConsumeNextToken(); // ':' s = new LabelStatement(o2.Name); return; // skip reading a ';' } ThrowError(new ParserErrorException(Code.cBadLabelDef, t.Location, "Bad label definition (labels must be a single identifier)")); } // end case for label decls // Expect a StatementExp else if (t.TokenType == Token.Type.cSemi) { ReadExpectedToken(Token.Type.cSemi); // Else we must be a StatementExp StatementExp se = e as StatementExp; if (se == null) //this.ThrowError_ExpectedStatementExp(e.Location); ThrowError(E_ExpectedStatementExp(e.Location)); se.SetLocation(EndRange(f)); s = new ExpStatement(se); return; } ThrowError(E_UnexpectedToken(t)); } // end start of expressions switch(t.TokenType) { // Empty statement case Token.Type.cSemi: ConsumeNextToken(); s = new EmptyStatement(); break; // Return -> 'return' ';' // | 'return' exp ';' case Token.Type.cReturn: { ConsumeNextToken(); t = m_lexer.PeekNextToken(); Exp e = null; if (t.TokenType != Token.Type.cSemi) { e = ParseExp(); } ReadExpectedToken(Token.Type.cSemi); s = new ReturnStatement(e); } break; // Note that the semi colons are included inthe stmt // IfSmt -> 'if' '(' exp ')' stmt:then // IfSmt -> 'if' '(' exp ')' stmt:then 'else' stmt:else case Token.Type.cIf: { ConsumeNextToken(); // 'if' ReadExpectedToken(Token.Type.cLParen); Exp exp = ParseExp(); ReadExpectedToken(Token.Type.cRParen); Statement sThen = ParseStatement(); Statement sElse = null; Token t2 = m_lexer.PeekNextToken(); if (t2.TokenType == Token.Type.cElse) { ConsumeNextToken(); // 'else' sElse = ParseStatement(); } s = new IfStatement(exp, sThen, sElse); } break; case Token.Type.cSwitch: s = ParseSwitchStatement(); break; // Throw an expression // ThrowStmt -> 'throw' objexp case Token.Type.cThrow: { ConsumeNextToken(); // 'throw' Exp oe = null; if (m_lexer.PeekNextToken().TokenType != Token.Type.cSemi) { oe = ParseExp(); } ReadExpectedToken(Token.Type.cSemi); s = new ThrowStatement(oe); } break; // try-catch-finally case Token.Type.cTry: s = ParseTryCatchFinallyStatement(); break; // while loop // 'while' '(' exp ')' stmt case Token.Type.cWhile: { ConsumeNextToken(); // 'while' ReadExpectedToken(Token.Type.cLParen); Exp e = ParseExp(); ReadExpectedToken(Token.Type.cRParen); Statement body = ParseStatement(); s = new WhileStatement(e, body); } break; // do loop // 'do' stmt 'while' '(' exp ')' ';' case Token.Type.cDo: { ConsumeNextToken(); // 'do' Statement body = ParseStatement(); ReadExpectedToken(Token.Type.cWhile); ReadExpectedToken(Token.Type.cLParen); Exp e = ParseExp(); ReadExpectedToken(Token.Type.cRParen); ReadExpectedToken(Token.Type.cSemi); s = new DoStatement(e, body); } break; // goto // 'goto' id:label ';' case Token.Type.cGoto: { ConsumeNextToken(); // 'goto' Identifier id = ReadExpectedIdentifier(); // id:label ReadExpectedToken(Token.Type.cSemi); // ';' s = new GotoStatement(id); } break; // break // 'break' ';' case Token.Type.cBreak: ConsumeNextToken(); ReadExpectedToken(Token.Type.cSemi); s = new BreakStatement(); break; // Continue // 'continue' ';' case Token.Type.cContinue: ConsumeNextToken(); ReadExpectedToken(Token.Type.cSemi); s = new ContinueStatement(); break; // For-loop case Token.Type.cFor: s = ParseForStatement(); break; // For-each // -> 'foreach' '(' Type id 'in' exp:collection ')' stmt case Token.Type.cForEach: s = ParseForeachStatement(); break; // BlockStatement - can be nested inside each other // start with a '{', no terminating semicolon case Token.Type.cLCurly: { s = ParseStatementBlock(); } break; default: ThrowError(E_UnexpectedToken(t)); // unrecognized statement break; } // end switch // Must have come up with something Debug.Assert(s != null || v != null); }
// 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; }