// Semantic resolution protected override Exp ResolveExpAsRight(ISemanticResolver s) { // We have a problem. If our arg is a property, then we need to // transform this x++ --> x = x + 1 // But we can't always transform since we can overload the ++, -- operators. // So resolve our arg, see if it's a property, and then transform if it is. // Since we update the Arg, we must resolve it as a left value Exp.ResolveExpAsLeft(ref this.m_exp, s); // First check if we have an overload // Check if we're a property if (m_exp is PropertyExp) { Exp e = null; int iDelta = IsInc ? 1 : -1; e = new AssignStmtExp( m_exp, new BinaryExp( m_exp, new IntExp(iDelta, this.m_filerange), BinaryExp.BinaryOp.cAdd ) ); Exp.ResolveExpAsRight(ref e, s); return e; } CalcCLRType(s); // Ensure type match, unless we have an overload. s.EnsureAssignable(Arg, typeof(int)); return this; }
// 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); }
// Group 0) assignment (=, +=, etc) & conditional ?: protected Exp ParseExp_Worker() { Exp T = ParseExp1(); Token t = m_lexer.PeekNextToken(); // Assignment if (t.TokenType == Token.Type.cAssign) { ConsumeNextToken(); Exp E = ParseExp(); AssignStmtExp s = new AssignStmtExp(T, E); return s; } // ?: operator --> E ? E : E if (t.TokenType == Token.Type.cQuestion) { ConsumeNextToken(); Exp eTrue = ParseExp(); ReadExpectedToken(Token.Type.cColon); Exp eFalse = ParseExp(); return new IfExp(T, eTrue, eFalse); } else { // Look for +=,-=,*=,/=,%= AST.BinaryExp.BinaryOp op = BinaryExp.BinaryOp.cEqu; // set to dummy switch(t.TokenType) { // Arithmetic case Token.Type.cPlusEqual: op = BinaryExp.BinaryOp.cAdd; break; case Token.Type.cMinusEqual: op = BinaryExp.BinaryOp.cSub; break; case Token.Type.cMulEqual: op = BinaryExp.BinaryOp.cMul; break; case Token.Type.cDivEqual: op = BinaryExp.BinaryOp.cDiv; break; case Token.Type.cModEqual: op = BinaryExp.BinaryOp.cMod; break; // Bitwise case Token.Type.cBitwiseAndEqual: op = BinaryExp.BinaryOp.cBitwiseAnd; break; case Token.Type.cBitwiseOrEqual: op = BinaryExp.BinaryOp.cBitwiseOr; break; case Token.Type.cBitwiseXorEqual: op = BinaryExp.BinaryOp.cBitwiseXor; break; // Shift case Token.Type.cShiftLeftEqual: op = BinaryExp.BinaryOp.cShiftLeft; break; case Token.Type.cShiftRightEqual: op = BinaryExp.BinaryOp.cShiftRight; break; } if (op != BinaryExp.BinaryOp.cEqu) { ConsumeNextToken(); Exp E = ParseExp(); //return new OpEqualStmtExp(T, E, op); // Transform 'a X= b' -> 'a = (a X b)' // This makes things much more consistent. AST.AssignStmtExp assign = new AssignStmtExp( T, new BinaryExp( T, E, op) ); return assign; } } Debug.Assert(T != null); return T; }
// Resolve protected override Exp ResolveExpAsRight(ISemanticResolver s) { if (m_symbol != null) return this; // Resolve the type we're allocating m_tFullType.ResolveType(s); Debug.Assert(this.ElemType != null); // Resolve the initializer list if (HasInitializerList) { // If we specified a length, it'd better match the intializer list length. if (DimensionExpList != null) { Debug.Assert(this.DimensionExpList.Length == 1, "@todo -multidimensional arrays"); Exp e = DimensionExpList[0]; // e must be a compile time constant who's value matches the ArrayInit length IntExp eInt = e as IntExp; if (eInt == null) ThrowError(SymbolError.MustBeCompileTimeConstant(e)); if (eInt.Value != m_ArrayInit.Length) ThrowError(SymbolError.NewArrayBoundsMismatch(this)); } m_ArrayInit.Resolve(s, this.ElemType); // The ability to not specifiy a dimension list is just syntactic sugar. // So if we still don't have it, we'd better fill it in based of the array-init list. if (DimensionExpList == null) { m_arExpList = new Exp[] { new IntExp(m_ArrayInit.Length, this.Location) }; } } Debug.Assert(DimensionExpList != null); for(int i = 0; i < this.m_arExpList.Length; i++) { ResolveExpAsRight(ref m_arExpList[i], s); } m_symbol = new ArrayTypeEntry(m_tFullType, s); CalcCLRType(s); // Transform an initializer list into an CompoundExpression: // new T[] { e0, e1, ... en} // <DeclareTemp(x), x = new T[], x[0]=e0, ... x[n]=en, x> if (HasInitializerList) { DeclareLocalStmtExp declare_x = new DeclareLocalStmtExp(this.ArraySymbol); LocalExp x = declare_x.GetLocal(); StatementExp [] list = new StatementExp[m_ArrayInit.Length + 2]; list[0] = declare_x; list[1] = new AssignStmtExp(x, this); for(int i = 0; i < m_ArrayInit.Length; i++) list[i + 2] = new AssignStmtExp( new ArrayAccessExp( x, new IntExp(i, null) ), m_ArrayInit.GetExpAt(i) ); // Strip the ArrayInitializer off this node. m_ArrayInit = null; StatementExp c = new CompoundStmtExp(list, x); StatementExp.ResolveExpAsRight(ref c, s); return c; } // end has Initializer return this; }