// !!!!!!!!!!!!!!!!! // !!! ATTENTION !!! // !!!!!!!!!!!!!!!!! // // Add the following method to your Scanner.frame file. // // // peek the next token, including pragmas // public Token PeekWithPragmas () { // if (pt.next == null) { // pt.next = NextToken(); // } // pt = pt.next; // return pt; // } // // Patch the method Get() in your Parser.frame to: // // void Get () { // for (;;) { // t = la; // la = scanner.Scan(); // if (la.kind <= maxT) { switchTokenContext(); ++errDist; break; } //-->pragmas // la = t; // } // } using System.Collections; COMPILER CS3 /*---------------------- conditional compilation symbols -----------------*/ ArrayList ccs = new ArrayList(); public void AddConditionalCompilationSymbols(string[] symbols) { if (symbols != null) { for (int i=0; i 0 && !ccs.Contains(symbols[i])) { ccs.Add(symbols[i]); } } } } // returns the end of the whitespaces in the given // string if whitespaces is true otherwise returns // the end of the non-whitespaces. int EndOf(string symbol, int start, bool whitespaces) { while ((start < symbol.Length) && (Char.IsWhiteSpace(symbol[start]) ^ !whitespaces)) { ++start; } return start; } // input: "#" {ws} directive ws {ws} {not-newline} {newline} // valid input: "#" {ws} directive ws {ws} {non-ws} {ws} {newline} // output: {non-ws} string RemPPDirective(string symbol) { int start = 1; int end; // skip {ws} start = EndOf(symbol, start, true); // skip directive start = EndOf(symbol, start, false); // skip ws {ws} start = EndOf(symbol, start, true); // search end of symbol end = EndOf(symbol, start, false); return symbol.Substring(start, end - start); } void AddCCS(string symbol) { symbol = RemPPDirective(symbol); if (!ccs.Contains(symbol)) { ccs.Add(symbol); } } void RemCCS(string symbol) { ccs.Remove(RemPPDirective(symbol)); } // preprocessor expression parser start bool PPOrExpression(PPScanner scan) { bool e1, e2; e1 = PPAndExpression(scan); while (scan.peek().getKind() == PPToken.eOr) { scan.next(); e2 = PPAndExpression(scan); e1 = e1 || e2; } return e1; } bool PPAndExpression(PPScanner scan) { bool e1, e2; e1 = PPEqualityExpression(scan); while (scan.peek().getKind() == PPToken.eAnd) { scan.next(); e2 = PPEqualityExpression(scan); e1 = e1 && e2; } return e1; } bool PPEqualityExpression(PPScanner scan) { bool e1, e2; e1 = PPUnaryExpression(scan); int tk = scan.peek().getKind(); while (tk == PPToken.eEQ || tk == PPToken.eNE) { scan.next(); e2 = PPUnaryExpression(scan); if (tk == PPToken.eEQ) { e1 = e1 == e2; } else { // tk == PPToken.eNE e1 = e1 != e2; } tk = scan.peek().getKind(); } return e1; } bool PPUnaryExpression(PPScanner scan) { bool not = false; PPToken ppt = scan.peek(); while (ppt.getKind() == PPToken.eNot) { scan.next(); not = !not; ppt = scan.peek(); } return not ^ PPPrimaryExpression(scan); } bool PPPrimaryExpression(PPScanner scan) { bool e = false; PPToken ppt = scan.next(); if (ppt.getKind() == PPToken.eTrue) { e = true; } else if (ppt.getKind() == PPToken.eFalse) { e = false; } else if (ppt.getKind() == PPToken.eLPar) { e = PPOrExpression(scan); ppt = scan.next(); if (ppt.getKind() != PPToken.eRPar) { Error("Corrupt preprocessor directive (')' expected), found: " + ppt.toString()); } } else if (ppt.getKind() == PPToken.eIdent) { e = ccs.Contains(ppt.getValue()); } else { Error("Corrupt preprocessor directive, unknown start of PPPrimaryExpression: " + ppt.toString()); } return e; } // preprocessor expression parser end // ccs.Contains(RemPPDirective(symbol)) bool IsCCS(string symbol) { // skip "#" "if", "#" "elif" PPScanner scan = new PPScanner(symbol); PPToken ppt = scan.next(); if (ppt.getKind() != PPToken.eHash) { Error("Corrupt preprocessor directive, # expected, but found: " + ppt.toString()); } ppt = scan.next(); if (ppt.getKind() != PPToken.eIdent) { Error("Corrupt preprocessor directive, identifier expected, but found: " + ppt.toString()); } return PPOrExpression(scan); } // search for the correct alternative and enter // drop everything before the correct alternative void IfPragma(string symbol) { if (!IsCCS(symbol)) { int state = 0; Token cur = ScanOrPeek(); for (;;) { switch (cur.kind) { case _ppIf: ++state; break; case _ppEndif: if (state == 0) { return; } --state; break; case _ppElif: if (state == 0 && IsCCS(cur.val)) { return; } break; case _ppElse: if (state == 0) { return; } break; case _EOF: Error("Incomplete file."); return; default: break; } cur = ScanOrPeek(); } } } // drop everything until the end of this if, elif, else directive void ElifOrElsePragma() { int state = 0; Token cur = ScanOrPeek(); for (;;) { switch (cur.kind) { case _ppIf: ++state; break; case _ppEndif: if (state == 0) { return; } --state; break; case _EOF: Error("Incomplete file."); return; default: break; } cur = ScanOrPeek(); } } public class PPScanner { private const int cEOF = -1; private char[] fChars; private int fPos; private PPToken fNext; private int fChar; // all start characters of symbol tokens (non ident tokens). private readonly char[] fSymbolTokenStart = { '#', '!', '=', '&', '|', '(', ')' }; public PPScanner(string str) { fChars = str.ToCharArray(); fPos = 0; fNext = null; nextCh(); } private void nextCh() { if (fPos < fChars.Length) { fChar = fChars[fPos]; ++fPos; } else { fChar = -1; } } private string readIdent() { System.Text.StringBuilder sb = new System.Text.StringBuilder(); do { sb.Append((char) fChar); nextCh(); } while (fChar != cEOF && !Char.IsWhiteSpace((char) fChar) && !isSymbolTokenStart((char) fChar)); return sb.ToString(); } private bool isSymbolTokenStart(char c) { for (int i = 0; i < fSymbolTokenStart.Length; ++i) { if (fSymbolTokenStart[i] == c) { return true; } } return false; } private PPToken scan() { PPToken t = new PPToken(); while (fChar != cEOF && Char.IsWhiteSpace((char) fChar)) { // Skip whitespaces nextCh(); } switch (fChar) { case '#': t.setKind(PPToken.eHash); nextCh(); break; case '!': t.setKind(PPToken.eNot); nextCh(); if (fChar == '=') { t.setKind(PPToken.eNE); nextCh(); } break; case '=': t.setKind(PPToken.eAssign); nextCh(); if (fChar == '=') { t.setKind(PPToken.eEQ); nextCh(); } break; case '&': nextCh(); if (fChar == '&') { nextCh(); t.setKind(PPToken.eAnd); } else { t.setKind(PPToken.eError); } break; case '|': nextCh(); if (fChar == '|') { nextCh(); t.setKind(PPToken.eOr); } else { t.setKind(PPToken.eError); } break; case '(': t.setKind(PPToken.eLPar); nextCh(); break; case ')': t.setKind(PPToken.eRPar); nextCh(); break; case cEOF: t.setKind(PPToken.eEOF); break; default: String val = readIdent(); if (val.Equals("true")) { t.setKind(PPToken.eTrue); } else if (val.Equals("false")) { t.setKind(PPToken.eFalse); } else { t.setKind(PPToken.eIdent); t.setValue(val); } break; } return t; } public PPToken peek() { if (fNext == null) { fNext = scan(); } return fNext; } public PPToken next() { PPToken t; if (fNext != null) { t = fNext; fNext = null; } else { t = scan(); } return t; } } public class PPToken { public const int eEOF = 0; public const int eHash = 1; public const int eNot = 2; public const int eAssign = 3; public const int eAnd = 4; public const int eOr = 5; public const int eLPar = 6; public const int eRPar = 7; public const int eTrue = 8; public const int eFalse = 9; public const int eIdent = 10; public const int eEQ = 11; public const int eNE = 12; public const int eError = 13; private readonly string[] sfTokenNames = {"EOF", "#", "!", "=", "&&", "||", "(", ")", "true", "false", "Identifier", "==", "!=", "Error"}; private const string cUnknownToken = "Unknown Token"; private int fKind; public string fVal; public void setKind(int kind) { fKind = kind; } public int getKind() { return fKind; } public void setValue(string value) { fVal = value; } public string getValue() { return fVal; } public string toString() { string s; if (fKind >= 0 && fKind < sfTokenNames.Length) { s = sfTokenNames[fKind]; if (fKind == eIdent) { s = s + ": " + fVal; } } else { s = cUnknownToken; } return s; } } /*----------------------------- token sets -------------------------------*/ const int maxTerminals = 160; // set size static BitArray NewSet(int[] values) { BitArray a = new BitArray(maxTerminals); foreach (int x in values) a[x] = true; return a; } static BitArray typeDeclStart= NewSet(new int[] {_class, _struct, _interface, _enum, _delegate}), typeKW = NewSet(new int[] {_char, _bool, _object, _string, _sbyte, _byte, _short, _ushort, _int, _uint, _long, _ulong, _float, _double, _decimal}), unaryHead = NewSet(new int[] {_plus, _minus, _not, _tilde, _times, _inc, _dec, _and}), assnStartOp = NewSet(new int[] {_plus, _minus, _not, _tilde, _times}), castFollower = NewSet(new int[] {_tilde, _not, _lpar, _ident, /* literals */ _intCon, _realCon, _charCon, _stringCon, /* any keyword expect as and is */ _abstract, _base, _bool, _break, _byte, _case, _catch, _char, _checked, _class, _const, _continue, _decimal, _default, _delegate, _do, _double, _else, _enum, _event, _explicit, _extern, _false, _finally, _fixed, _float, _for, _foreach, _goto, _if, _implicit, _in, _int, _interface, _internal, _lock, _long, _namespace, _new, _null, _object, _operator, _outKW, _override, _params, _private, _protected, _public, _readonly, _ref, _return, _sbyte, _sealed, _short, _sizeof, _stackalloc, _static, _string, _struct, _switch, _this, _throw, _true, _try, _typeof, _uint, _ulong, _unchecked, _unsafe, _ushort, _usingKW, _virtual, _void, _volatile, _while }), typArgLstFol = NewSet(new int[] {_lpar, _rpar, _rbrack, _colon, _scolon, _comma, _dot, _question, _eq, _neq, _gt, _nullCoal, _lt, _lteq, _gteq, _is, _as, _andand}), keyword = NewSet(new int[] {_abstract, _as, _base, _bool, _break, _byte, _case, _catch, _char, _checked, _class, _const, _continue, _decimal, _default, _delegate, _do, _double, _else, _enum, _event, _explicit, _extern, _false, _finally, _fixed, _float, _for, _foreach, _goto, _if, _implicit, _in, _int, _interface, _internal, _is, _lock, _long, _namespace, _new, _null, _object, _operator, _outKW, _override, _params, _private, _protected, _public, _readonly, _ref, _return, _sbyte, _sealed, _short, _sizeof, _stackalloc, _static, _string, _struct, _switch, _this, _throw, _true, _try, _typeof, _uint, _ulong, _unchecked, _unsafe, _ushort, _usingKW, _virtual, _void, _volatile, _while}), assgnOps = NewSet(new int[] {_assgn, _plusassgn, _minusassgn, _timesassgn, _divassgn, _modassgn, _andassgn, _orassgn, _xorassgn, _lshassgn}) /* rshassgn: ">" ">=" no whitespace allowed*/, firstExpr /* The first set of expressions are used to determine if a "?" symbol counts as a nullable qualifier or belogns to the conditional operator. This is a wild guess because there is no solution for this problem in the spec. */ = NewSet(new int[] { _ident, _intCon, _realCon, _charCon, _stringCon, _base, _bool, _byte, _char, _checked, _decimal, _default, _delegate, _double, _false, _float, _int, _long, _new, _null, _object, _sbyte, _short, _sizeof, _string, _this, _true, _typeof, _uint, _ulong, _unchecked, _ushort, _and, _dec, _inc, _lpar, _minus, _not, _plus, _tilde, _times }) ; /*---------------------------- auxiliary methods ------------------------*/ void Error (string s) { if (errDist >= minErrDist) errors.SemErr(la.line, la.col, s); errDist = 0; } // Return the n-th token after the current lookahead token Token Peek (int n) { scanner.ResetPeek(); Token _x = la; while (n > 0) { _x = PeekWithPragmas(); n--; } return _x; } Token Copy(Token t) { Token copy = new Token(); copy.kind = t.kind; copy.pos = t.pos; copy.col = t.col; copy.line = t.line; copy.val = t.val; return copy; } Token CopyTokens(Token start, Token end) { Token tokens = Copy(start); Token t = tokens; while(true) { start = start.next; if (start == end) break; if (start.kind > maxT) continue; t = t.next = Copy(start); } return tokens; } // A pragmas sensitive Peek mehtod. Token PeekWithPragmas() { Token _x = scanner.PeekWithPragmas(); while (_x.kind > maxT) { // Handle Pragma, this MUST stay in SYNC with the semantic actions of the pragmas! isScanMode = false; if (_x.kind == _ppDefine) { AddCCS(_x.val); } else if (_x.kind == _ppUndef) { RemCCS(_x.val); } else if (_x.kind == _ppIf) { IfPragma(_x.val); } else if (_x.kind == _ppElif) { ElifOrElsePragma(); } else if (_x.kind == _ppElse) { ElifOrElsePragma(); } isScanMode = true; _x = scanner.PeekWithPragmas(); } return _x; } /* This method will be used by the pragma handlers because we * have to decide if the pragma works in peek mode (in a resolver) * or in normal scan mode (called from get while parsing). */ private bool isScanMode = true; Token ScanOrPeek () { if (isScanMode) { return scanner.Scan(); } else { return scanner.PeekWithPragmas(); } } // ident "=" bool IsAssignment () { return kindInContext(la.kind) == _ident && Peek(1).kind == _assgn; } /* True, if the comma is not a trailing one, * * like the last one in: a, b, c, */ bool NotFinalComma () { int peek = Peek(1).kind; return la.kind == _comma && peek != _rbrace && peek != _rbrack; } /* Checks whether the next sequence of tokens is a qualident * * and returns the next token after the qualident * * !!! Proceeds from current peek position !!! */ Token IsQualident (Token pt) { if (kindInContext(pt.kind) == _ident) { pt = PeekWithPragmas(); while (pt.kind == _dot) { pt = PeekWithPragmas(); if (kindInContext(pt.kind) != _ident) return null; pt = PeekWithPragmas(); } return pt; } else return null; } bool IsGeneric() { scanner.ResetPeek(); Token pt = la; pt = IsTypeArgumentList(pt); return pt != null && typArgLstFol[kindInContext(pt.kind)]; } /* returns the token after the type argument list * * if type argument list could be recognized, * * otherwise null. */ Token IsTypeArgumentList(Token pt) { if (pt.kind == _lt) { pt = PeekWithPragmas(); while (true) { pt = IsType(pt); if (pt == null) { return null; } if (pt.kind == _gt) { // list recognized pt = PeekWithPragmas(); break; } else if (pt.kind == _comma) { // another argument pt = PeekWithPragmas(); } else { // error in type argument list return null; } } } else { return null; } return pt; } /* Stores if the last IsType recognized a pointer or array type. * * This flag does not mean anything if the last call of IsType * * did not recognize a type. */ bool wasPointerOrArrayType; /* returns the token after the type * * if a type could be recognized, otherwise null. */ Token IsType (Token pt) { wasPointerOrArrayType = false; if (typeKW[kindInContext(pt.kind)]) { pt = PeekWithPragmas(); } else if (pt.kind == _void) { pt = PeekWithPragmas(); if (pt.kind != _times) { return null; } wasPointerOrArrayType = true; pt = PeekWithPragmas(); } else if (kindInContext(pt.kind) == _ident) { pt = PeekWithPragmas(); if (pt.kind == _dblcolon || pt.kind == _dot) { // either namespace alias qualifier "::" or first // part of the qualident pt = PeekWithPragmas(); pt = IsQualident(pt); if (pt == null) { return null; } } if (pt.kind == _lt) { pt = IsTypeArgumentList(pt); if (pt == null) { return null; } if (pt.kind == _dot) { pt = PeekWithPragmas(); pt = IsType(pt); if (pt == null) { return null; } } } } else { return null; } if (pt.kind == _question) { pt = PeekWithPragmas(); } Token prePt = pt; pt = SkipPointerOrDims(pt); wasPointerOrArrayType |= prePt != pt; return pt; } // Type ident // (Type can be void*) bool IsLocalVarDecl() { Token pt = la; scanner.ResetPeek(); pt = IsType(pt); return pt != null && kindInContext(pt.kind) == _ident; } // "[" ("," | "]") bool IsDims () { int peek = Peek(1).kind; return la.kind == _lbrack && (peek == _comma || peek == _rbrack); } // "*" | "[" ("," | "]") bool IsPointerOrDims () { return la.kind == _times || IsDims(); } /* skip: { "[" { "," } "]" | "*" } */ /* returns the next token after the pointer of dims */ /* or null if not recognized */ /* !!! Proceeds from current peek position !!! */ Token SkipPointerOrDims (Token pt) { for (;;) { if (pt.kind == _lbrack) { do pt = PeekWithPragmas(); while (pt.kind == _comma); if (pt.kind != _rbrack) return null; } else if (pt.kind != _times) break; pt = PeekWithPragmas(); } return pt; } // Is attribute target specifier // (ident | keyword) ":" bool IsAttrTargSpec () { return (kindInContext(la.kind) == _ident || keyword[kindInContext(la.kind)]) && Peek(1).kind == _colon; } // ident ("," | "=" | ";") bool IsFieldDecl () { int peek = Peek(1).kind; return kindInContext(la.kind) == _ident && (peek == _comma || peek == _assgn || peek == _scolon); } bool IsTypeCast () { if (la.kind != _lpar) return false; if (IsSimpleTypeCast()) return true; return GuessTypeCast(); } // "(" typeKW ")" bool IsSimpleTypeCast () { // assert: la.kind == _lpar scanner.ResetPeek(); Token pt1 = PeekWithPragmas(); Token pt2 = PeekWithPragmas(); return typeKW[kindInContext(pt1.kind)] && (pt2.kind == _rpar || (pt2.kind == _question && PeekWithPragmas().kind == _rpar)); } // "(" Type ")" castFollower bool GuessTypeCast () { // assert: la.kind == _lpar Token pt = Peek(1); pt = IsType(pt); if (pt == null) { return false; } if (pt.kind != _rpar) { return false; } pt = PeekWithPragmas(); return castFollower[kindInContext(pt.kind)] || /* & for unsafe context */ (wasPointerOrArrayType && pt.kind == _and); } // "[" "assembly" bool IsGlobalAttrTarget () { Token pt = Peek(1); return la.kind == _lbrack && kindInContext(pt.kind) == _ident && (pt.val.Equals("assembly") || pt.val.Equals("module")); } // "extern" "alias" // where alias is an identifier, no keyword bool IsExternAliasDirective () { return la.kind == _extern && Peek(1).val.Equals("alias"); } // true: anyToken"<" // no whitespace between the token and the "<" allowed // anything else will return false. bool IsLtNoWs() { return (la.kind == _lt) && ((t.pos + t.val.Length) == la.pos); } bool IsNoSwitchLabelOrRBrace() { return (la.kind != _case && la.kind != _default && la.kind != _rbrace) || (la.kind == _default && Peek(1).kind != _colon); } bool IsShift() { Token pt = Peek(1); return (la.kind == _ltlt) || ( la.kind == _gt && pt.kind == _gt && (la.pos + la.val.Length == pt.pos) ); } bool IsTypeDeclaration() { if (typeDeclStart[kindInContext(la.kind)]) { return true; } Token pt = Peek(1); return kindInContext(la.kind) == _ident && "partial".Equals(la.val) && typeDeclStart[kindInContext(pt.kind)]; } // true: TypeArgumentList followed by anything but "(" bool IsPartOfMemberName() { scanner.ResetPeek(); Token pt = la; pt = IsTypeArgumentList(pt); return pt != null && pt.kind != _lpar; } bool IsImplicitTypedLambdaExpression() { Token pt = Peek(1); if (kindInContext(la.kind) == _ident && pt.kind == _arrow) { // x => return true; } if (la.kind != _lpar || kindInContext(pt.kind) != _ident) { // no start of implicit typed lambda expression return false; } pt = PeekWithPragmas(); // (x , y, z) => while (pt.kind == _comma) { pt = PeekWithPragmas(); if (kindInContext(pt.kind) != _ident) { // after a comma there must be an ident return false; } pt = PeekWithPragmas(); } return pt.kind == _rpar && PeekWithPragmas().kind == _arrow; } bool IsExplicitTypedLambdaExpression() { if (la.kind != _lpar) { return false; } // ( Token pt = Peek(0); int nPars = 1; // skip explicitly typed lambda parameterlist sloppy while (nPars > 0) { pt = PeekWithPragmas(); if (pt.kind == _EOF) break; if (pt.kind == _lpar) { ++nPars; } else if (pt.kind == _rpar) { --nPars; } } // ( formal parameters ) // check for "=>" return PeekWithPragmas().kind == _arrow; } bool IsYieldReturn() { if (la.kind != _ident) { return false; } Token pt = Peek(1); return pt.kind == _return || pt.kind == _break; } // TypeKind enum TypeKind {simple, array, pointer, @void} // Operator const int plusOp = 0x00000001, minusOp = 0x00000002, notOp = 0x00000004, tildeOp = 0x00000008, incOp = 0x00000010, decOp = 0x00000020, trueOp = 0x00000040, falseOp = 0x00000080, timesOp = 0x00000100, divOp = 0x00000200, modOp = 0x00000400, andOp = 0x00000800, orOp = 0x00001000, xorOp = 0x00002000, lshiftOp = 0x00004000, rshiftOp = 0x00008000, eqOp = 0x00010000, neqOp = 0x00020000, gtOp = 0x00040000, ltOp = 0x00080000, gteOp = 0x00100000, lteOp = 0x00200000, unaryOp = plusOp|minusOp|notOp|tildeOp|incOp|decOp|trueOp|falseOp, binaryOp = plusOp|minusOp|timesOp|divOp|modOp|andOp|orOp|xorOp|lshiftOp|rshiftOp|eqOp|neqOp|gtOp|ltOp|gteOp|lteOp; /*------------------------- modifier handling -----------------------------*/ // Modifier private const long newMod = 0x0001, publicMod = 0x0002, protectedMod= 0x0004, internalMod = 0x0008, privateMod = 0x0010, unsafeMod = 0x0020, staticMod = 0x0040, readonlyMod = 0x0080, volatileMod = 0x0100, virtualMod= 0x0200, sealedMod = 0x0400, overrideMod = 0x0800, abstractMod = 0x1000, externMod = 0x2000, /* sets of modifiers that can be attached to certain program elements * * e.g., "constants" marks all modifiers that may be used with constants */ noneMod = 0x0000, classesMod = newMod|publicMod|protectedMod|internalMod|privateMod|unsafeMod|abstractMod|sealedMod|staticMod, constantsMod = newMod|publicMod|protectedMod|internalMod|privateMod, fieldsMod = newMod|publicMod|protectedMod|internalMod|privateMod|unsafeMod|staticMod|readonlyMod|volatileMod, propEvntMethsMod = newMod|publicMod|protectedMod|internalMod|privateMod|unsafeMod|staticMod|virtualMod|sealedMod|overrideMod|abstractMod|externMod, accessorsPossib1Mod = privateMod, accessorsPossib2Mod = protectedMod|internalMod, indexersMod = newMod|publicMod|protectedMod|internalMod|privateMod|unsafeMod|virtualMod|sealedMod|overrideMod|abstractMod|externMod, operatorsMod = publicMod|unsafeMod|staticMod|externMod, operatorsMustMod = publicMod|staticMod, constructorsMod = publicMod|protectedMod|internalMod|privateMod|unsafeMod|externMod, staticConstrMod = externMod|staticMod, staticConstrMustMod = staticMod, nonClassTypesMod = newMod|publicMod|protectedMod|internalMod|privateMod|unsafeMod, destructorsMod = externMod|unsafeMod, allMod = 0x3fff; private class Modifiers { private long cur = noneMod; private Parser parser; public Modifiers(Parser parser) { this.parser = parser; } public void Add (long m) { if ((cur & m) == 0) cur |= m; else parser.Error("modifier " + m + " already defined"); } public void Add (Modifiers m) { Add(m.cur); } public bool IsNone() { return cur == noneMod; } public void Check (long allowed) { long wrong = cur & (allowed ^ allMod); if (wrong != noneMod) parser.Error("modifier(s) " + wrong + " not allowed here"); } public void Check (long allowEither, long allowOr) { long wrong = cur & ((allowEither|allowOr) ^ allMod); if ((allowEither&allowOr) != noneMod) { parser.Error("modifiers providerd must not overlap"); } else if (wrong != noneMod) { parser.Error("modifier(s) " + wrong + " not allowed here"); } else if (((cur&allowEither) != noneMod) && ((cur&allowOr) != noneMod)) { parser.Error("modifier(s) may either be " + allowEither + " or " + allowOr); } } public void CheckMust (long mustHave) { long missing = (cur&mustHave)^mustHave; if (missing != noneMod) { parser.Error("modifier(s) " + missing + " must be applied here"); } } public bool Has (long mod) { return (cur&mod) == mod; } } // LINQ Context switch Hashtable litInOtherContext = new Hashtable(); // will be used to restore the special context kind. int lastKind; // contextDepth stores the recursion depth of the context, if we are in the context // the value is > 0, outside it is 0 int contextDepth = 0; void fillContextLitMap() { litInOtherContext[_from] = _ident; litInOtherContext[_where] = _ident; litInOtherContext[_join] = _ident; litInOtherContext[_on] = _ident; litInOtherContext[_equals] = _ident; litInOtherContext[_into] = _ident; litInOtherContext[_let] = _ident; litInOtherContext[_orderby] = _ident; litInOtherContext[_ascending] = _ident; litInOtherContext[_descending] = _ident; litInOtherContext[_select] = _ident; litInOtherContext[_group] = _ident; litInOtherContext[_by] = _ident; } int kindInContext(int kind) { if (contextDepth == 0) { // subtract extra literals from the special context as we are in normal context if (litInOtherContext.Contains(kind)) { return (int) litInOtherContext[kind]; } } return kind; } void switchTokenContext() { // save the original kind of the token in case we have to restore it lastKind = la.kind; la.kind = kindInContext(la.kind); } void leaveContext() { // assert: contextDepth > 0; --contextDepth; if (contextDepth == 0) { switchTokenContext(); } } void enterContext() { ++contextDepth; } void checkForContextSwitch() { if (contextDepth > 0) { // nothing to do, we are already in context. return; } // check for context switch if (lastKind == _from && (Peek(1).kind == _ident || IsType(Peek(1)) != null) && Peek(2).kind != _scolon && Peek(2).kind != _comma && Peek(2).kind != _eq) { // restore la token if we need a token switch la.kind = lastKind; } } Stack tryResults = new Stack(); /*------------------------------------------------------------------------* *----- SCANNER DESCRIPTION ----------------------------------------------* *------------------------------------------------------------------------*/ CHARACTERS tab = '\u0009'. /* 9 = tabulator */ eol = '\u000a'. /* 10 = line feed */ cr = '\u000d'. /* 13 = carriage return */ newLine = cr + eol. /* Line separator character (U+2028) + Paragraph separator character (U+2029) */ startLetter = 'A' .. 'Z' + 'a' .. 'z' + '_' + '\u00aa' + '\u00b5' + '\u00ba' + '\u00c0' .. '\u00d6' + '\u00d8' .. '\u00f6' + '\u00f8' .. '\u00ff'. partLetter = '0' .. '9' + 'A' .. 'Z' + 'a' .. 'z' + '_' + '\u0080' + '\u00a0' .. '\u00b3' + '\u00b5' + '\u00ba' + '\u00c0' .. '\u00d6' + '\u00d8' .. '\u00f6' + '\u00f8' .. '\u00ff'. digit = "0123456789". hexDigit = digit + "ABCDEFabcdef". notDigit = ANY - digit. char = ANY - "'" - '\\' - newLine. verbatimStringChar = ANY - '"'. regularStringChar = ANY - '"' - '\\' - newLine. notNewLine = ANY - newLine . ws = " " + tab + '\u000b' + '\u000c'. /* Any character with Unicode class Zs */ blockComCh = ANY - '*' - '/'. TOKENS ident = ['@'] ( startLetter | '\\' ( 'u' hexDigit hexDigit hexDigit hexDigit | 'U' hexDigit hexDigit hexDigit hexDigit hexDigit hexDigit hexDigit hexDigit ) ) { partLetter | '\\' ( 'u' hexDigit hexDigit hexDigit hexDigit | 'U' hexDigit hexDigit hexDigit hexDigit hexDigit hexDigit hexDigit hexDigit ) }. /*--------------------------------------------------------------------------------*/ intCon = ( digit {digit} | digit {digit} CONTEXT ("." notDigit) | ("0x" | "0X") hexDigit {hexDigit} ) ["U" | "u" | "L" | "l" | "UL" | "Ul" | "uL" | "ul" | "LU" | "Lu" | "lU" | "lu"]. /*--------------------------------------------------------------------------------*/ realCon = "." digit {digit} [("e" | "E") ["+" | "-"] digit {digit}] ["F" | "f" | "D" | "d" | "M" | "m"] | digit {digit} ( "." digit {digit} [("e" | "E" ) ["+" | "-"] digit {digit} ] ["F" | "f" | "D" | "d" | "M" | "m"] | ("e" | "E") ["+" | "-"] digit {digit} ["F" | "f" | "D" | "d" | "M" | "m"] | "F" | "f" | "D" | "d" | "M" | "m" ). /*--------------------------------------------------------------------------------*/ charCon = "'" ( char | "\\\'" | "\\\"" | "\\\\" | "\\0" | "\\a" | "\\b" | "\\f" | "\\n" | "\\r" | "\\t" | "\\v" | "\\x" hexDigit [hexDigit] [hexDigit] [hexDigit] | "\\u" hexDigit hexDigit hexDigit hexDigit | "\\U" hexDigit hexDigit hexDigit hexDigit hexDigit hexDigit hexDigit hexDigit ) "'". /*--------------------------------------------------------------------------------*/ stringCon = "\"" { regularStringChar | "\\\'" | "\\\"" | "\\\\" | "\\0" | "\\a" | "\\b" | "\\f" | "\\n" | "\\r" | "\\t" | "\\v" | "\\x" hexDigit [hexDigit] [hexDigit] [hexDigit] | "\\u" hexDigit hexDigit hexDigit hexDigit | "\\U" hexDigit hexDigit hexDigit hexDigit hexDigit hexDigit hexDigit hexDigit } "\"" | "@\"" {verbatimStringChar | "\"\""} "\"". /*----- keyword names needed in LL(1) resolvers -----*/ abstract = "abstract". as = "as". base = "base". bool = "bool". break = "break". byte = "byte". case = "case". catch = "catch". char = "char". checked = "checked". class = "class". const = "const". continue = "continue". decimal = "decimal". default = "default". delegate = "delegate". do = "do". double = "double". else = "else". enum = "enum". event = "event". explicit = "explicit". extern = "extern". false = "false". finally = "finally". fixed = "fixed". float = "float". for = "for". foreach = "foreach". goto = "goto". if = "if". implicit = "implicit". in = "in". int = "int". interface = "interface". internal = "internal". is = "is". lock = "lock". long = "long". namespace = "namespace". new = "new". null = "null". object = "object". operator = "operator". outKW = "out". override = "override". params = "params". private = "private". protected = "protected". public = "public". readonly = "readonly". ref = "ref". return = "return". sbyte = "sbyte". sealed = "sealed". short = "short". sizeof = "sizeof". stackalloc = "stackalloc". static = "static". string = "string". struct = "struct". switch = "switch". this = "this". throw = "throw". true = "true". try = "try". typeof = "typeof". uint = "uint". ulong = "ulong". unchecked = "unchecked". unsafe = "unsafe". ushort = "ushort". usingKW = "using". virtual = "virtual". void = "void". volatile = "volatile". while = "while". /*----- operators and special characters needed in LL(1) resolvers --------------*/ and = "&". andassgn = "&=". arrow = "=>". assgn = "=". colon = ":". comma = ",". dec = "--". divassgn = "/=". dot = ".". dblcolon = "::". eq = "==". gt = ">". gteq = ">=". inc = "++". lbrace = "{". lbrack = "[". lpar = "(". lshassgn = "<<=". lt = "<". ltlt = "<<". minus = "-". minusassgn = "-=". modassgn = "%=". neq = "!=". not = "!". nullCoal = "??". orassgn = "|=". plus = "+". plusassgn = "+=". question = "?". rbrace = "}". rbrack = "]". rpar = ")". scolon = ";". tilde = "~". times = "*". timesassgn = "*=". xorassgn = "^=". andand = "&&". lteq = "<=". /*----- linq keywords --------------*/ from = "from". where = "where". join = "join". on = "on". equals = "equals". into = "into". let = "let". orderby = "orderby". ascending = "ascending". descending = "descending". select = "select". group = "group". by = "by". PRAGMAS /* Preprocessor directives. * The exact parsing of their syntax is left for later processing */ /* We removed the obligatory newLine at the end of each pragma, even it's * against the specification, because the csc is fine with it. This is * relevant if for example a ppEndif is at the end of a file without * the newLine. */ /* !!! ATTENTION !!! If you change the semantic actions of the pragmas you * MUST also change the PeekWithPragmas() method! */ ppDefine = "#" {ws} "define" {notNewLine} /* newLine */. (. AddCCS(la.val); .) ppUndef = "#" {ws} "undef" {notNewLine} /* newLine */. (. RemCCS(la.val); .) ppIf = "#" {ws} "if" {notNewLine} /* newLine */. (. IfPragma(la.val); .) ppElif = "#" {ws} "elif" {notNewLine} /* newLine */. (. ElifOrElsePragma(); .) ppElse = "#" {ws} "else" {notNewLine} /* newLine */. (. ElifOrElsePragma(); .) ppEndif = "#" {ws} "endif" {notNewLine} /* newLine */. ppLine = "#" {ws} "line" {notNewLine} /* newLine */. ppError = "#" {ws} "error" {notNewLine} /* newLine */. ppWarning = "#" {ws} "warning" {notNewLine} /* newLine */. ppRegion = "#" {ws} "region" {notNewLine} /* newLine */. ppEndReg = "#" {ws} "endregion" {notNewLine} /* newLine */. ppPragma = "#" {ws} "pragma" {notNewLine} /* newLine */. // **************************************************************************** // If you would like to use C# comments in your grammar, use // pragmas for that purpose and remove the COMMENTS. // // The pragma for the block comment looks like this: // cBlockCom = "/*" { "/" | blockComCh | "*"{"*"} blockComCh } "*"{"*"}"/". // where blockComCh is a character set (CHARACTERS section) defined as: // blockComCh = ANY - '*' - '/'. // // The line comment is simpler: // cLineCom = "//" { notNewLine } newLine. // where newLine and notNewLine are character sets (already defined in the // CHARACTERS section). // **************************************************************************** COMMENTS FROM "/*" TO "*/" COMMENTS FROM "//" TO eol IGNORE eol + cr + tab PRODUCTIONS /*------------------------------------------------------------------------* *--------------------------- Declarations -------------------------------* *------------------------------------------------------------------------*/ CS3 (. fillContextLitMap(); .) = {IF (IsExternAliasDirective()) ExternAliasDirective} {UsingDirective} {IF (IsGlobalAttrTarget()) GlobalAttributes} {NamespaceMemberDeclaration} . ExternAliasDirective = "extern" ident (. if (!t.val.Equals("alias")) { Error("alias expected"); } .) ident ";" . UsingDirective = "using" [ IF (IsAssignment()) ident "=" ] TypeName ";" . NamespaceMemberDeclaration (. Modifiers m = new Modifiers(this); .) = "namespace" ident { "." ident } "{" { IF (IsExternAliasDirective()) ExternAliasDirective } { UsingDirective } { NamespaceMemberDeclaration } "}" [ ";" ] | { Attributes } ModifierList TypeDeclaration . TypeDeclaration (. TypeKind dummy; .) = ( [ ident (. if (!"partial".Equals(t.val)) { Error("partial expected, found: " + t.val); } .) ] ( (. m.Check(classesMod); .) "class" ident [ TypeParameterList ] [ ClassBase ] { TypeParameterConstraintsClause } ClassBody [ ";" ] | (. m.Check(nonClassTypesMod); .) "struct" ident [ TypeParameterList ] [ ":" TypeName { "," TypeName } ] { TypeParameterConstraintsClause } StructBody [ ";" ] | (. m.Check(nonClassTypesMod); .) "interface" ident [ TypeParameterList ] [ ":" TypeName { "," TypeName } ] { TypeParameterConstraintsClause } "{" { InterfaceMemberDeclaration } "}" [ ";" ] ) | (. m.Check(nonClassTypesMod); .) "enum" ident [ ":" IntegralType ] EnumBody [ ";" ] | (. m.Check(nonClassTypesMod); .) "delegate" Type ident [ TypeParameterList ] "(" [ FormalParameterList ] ")" { TypeParameterConstraintsClause } ";" ) . ClassBase = ":" ClassType { "," TypeName } . ClassBody = "{" { { Attributes } (. Modifiers m = new Modifiers(this); .) ModifierList ClassMemberDeclaration } "}" . StructBody = "{" { { Attributes } (. Modifiers m = new Modifiers(this); .) ModifierList StructMemberDeclaration } "}" . EnumBody = "{" [ EnumMemberDeclaration {IF (NotFinalComma()) "," EnumMemberDeclaration } [ "," ] ] "}" . ClassMemberDeclaration = ( StructMemberDeclaration | "~" ident "(" ")" ( Block | ";" ) ) . StructMemberDeclaration (. TypeKind type; int op; .) = ( (. m.Check(constantsMod); .) "const" Type ident "=" Expression { "," ident "=" Expression } ";" | /* Event */ (. m.Check(propEvntMethsMod); .) "event" Type ( IF (IsFieldDecl()) VariableDeclarators ";" | TypeName "{" EventAccessorDeclarations "}" ) | /* Constructor, StaticConstructor */ IF (kindInContext(la.kind) == _ident && Peek(1).kind == _lpar) (. m.Check(constructorsMod|staticConstrMod); .) ident "(" [ (. m.Check(constructorsMod); .) FormalParameterList ] ")" ( (. m.Check(constructorsMod); .) ":" ( "base" | "this" ) "(" [ Argument { "," Argument } ] ")" | /* implicit base() call */ ) ( Block | ";") | /* Fixed buffer declaration, not in ECMA but in C# 2.0. This should only appear in unsafe struct declarations. */ "fixed" Type ident "[" Expression "]" ";" | IF(IsTypeDeclaration()) TypeDeclaration | [IF ("partial".Equals(la.val)) ident] Type ( /* Operator */ (. m.Check(operatorsMod); m.CheckMust(operatorsMustMod); if (type == TypeKind.@void) { Error("operator not allowed on void"); } .) "operator" OverloadableOp "(" Type ident ( "," Type (. if ((op & binaryOp) == 0) Error("too many operands for unary operator"); .) ident | (. if ((op & unaryOp) == 0) Error("too few operands for binary operator"); .) ) ")" ( Block | ";" ) | /* Field */ IF (IsFieldDecl()) (. m.Check(fieldsMod); if (type == TypeKind.@void) { Error("field type must not be void"); } .) VariableDeclarators ";" | /* Property | Indexer | Method */ MemberName ( /* Property */ (. m.Check(propEvntMethsMod); if (type == TypeKind.@void) { Error("property type must not be void"); } .) "{" AccessorDeclarations "}" | /* Indexer */ (. m.Check(indexersMod); if (type == TypeKind.@void) { Error("indexer type must not be void"); } .) "." "this" "[" FormalParameterList "]" "{" AccessorDeclarations "}" | /* Method */ (. m.Check(propEvntMethsMod); .) [TypeParameterList] "(" [ FormalParameterList ] ")" { TypeParameterConstraintsClause } ( Block | ";" ) ) | /* Indexer */ (. m.Check(indexersMod); if (type == TypeKind.@void) { Error("indexer type must not be void"); } .) "this" "[" FormalParameterList "]" "{" AccessorDeclarations "}" ) | /* Cast operator */ (. m.Check(operatorsMod); m.CheckMust(operatorsMustMod); .) ( "implicit" | "explicit" ) "operator" Type (. if (type == TypeKind.@void) { Error("cast type must not be void"); } .) "(" Type ident ")" ( Block | ";" ) ) . InterfaceMemberDeclaration (. Modifiers m = new Modifiers(this); // every interface member is public m.Add(Parser.publicMod); TypeKind dummy; bool newSet = false; .) = { Attributes } [ "new" (. m.Add(Parser.newMod); newSet = true; .) ] ( [ "unsafe" (. m.Add(Parser.unsafeMod); .) ] [ "new" (. if (newSet) Error("duplicate operand 'new'"); m.Add(Parser.newMod); .) ] Type ( ident ( [ TypeParameterList ] "(" [ FormalParameterList ] ")" { TypeParameterConstraintsClause } ";" | "{" InterfaceAccessors "}" ) | "this" "[" FormalParameterList "]" "{" InterfaceAccessors "}" ) | "event" Type ident ";" ) . EnumMemberDeclaration = { Attributes } ident [ "=" Expression ] . LocalVariableType = (. TypeKind dummy; .) Type . LocalVariableDeclaration = LocalVariableType LocalVariableDeclarator { "," LocalVariableDeclarator } . LocalVariableDeclarator (. TypeKind dummy; .) = ident ( | "=" ( VariableInitializer | "stackalloc" Type "[" Expression "]" ) ) . VariableInitializer = ( Expression | ArrayInitializer ) . ArrayInitializer = "{" [ VariableInitializer { IF (NotFinalComma()) "," VariableInitializer } [ "," ] ] "}" . FormalParameterList (. TypeKind type; .) = { Attributes } ( IF ("__arglist".Equals(la.val)) ident | /* we allow "this" in front of every Parameter not only the * first one, if you need to be stricter you have to check * it semantically. */ [ "ref" | "out" | "this" ] Type ident [ "," FormalParameterList ] | "params" Type (. if (type != TypeKind.array) { Error("params argument must be an array"); } .) ident ) . Argument = [ "ref" | "out" ] Expression . AccessorDeclarations (. Modifiers am = new Modifiers(this); bool getFound = false, setFound = false; .) = { Attributes } ModifierList (. am.Check(accessorsPossib1Mod, accessorsPossib2Mod); .) ( IF (la.val.Equals("get")) ident (. getFound = true; .) | IF (la.val.Equals("set")) ident (. setFound = true; .) | ident (. Error("set or get expected"); .) ) (Block | ";") [ (. am = new Modifiers(this); .) { Attributes } ModifierList (. am.Check(accessorsPossib1Mod, accessorsPossib2Mod); .) ( IF (la.val.Equals("get")) ident (. if (getFound) Error("get already declared"); getFound = true; .) | IF (la.val.Equals("set")) ident (. if (setFound) Error("set already declared"); setFound = true;.) | ident (. Error("set or get expected"); .) ) (Block | ";") ] . EventAccessorDeclarations (. bool addFound = false, remFound = false; .) = { Attributes } ( IF (la.val.Equals("add")) ident (. addFound = true; .) | IF (la.val.Equals("remove")) ident (. remFound = true; .) | ident (. Error("add or remove expected"); .) ) Block [ { Attributes } ( IF (la.val.Equals("add")) ident (. if (addFound) Error("add already declared"); else { addFound = true; remFound = false; } .) | IF (la.val.Equals("remove")) ident (. if (remFound) Error("remove already declared"); else { remFound = true; addFound = false; } .) | ident (. addFound = false; remFound = false; Error("add or remove expected"); .) ) Block ] . InterfaceAccessors (. bool getFound = false, setFound = false; .) = { Attributes } ( IF (la.val.Equals("get")) ident (. getFound = true; .) | IF (la.val.Equals("set")) ident (. setFound = true; .) | ident (. Error("set or get expected"); .) ) ";" [ { Attributes } ( IF (la.val.Equals("get")) ident (. if (getFound) Error("get already declared"); .) | IF (la.val.Equals("set")) ident (. if (setFound) Error("set already declared"); .) | ident (. Error("set or get expected"); .) ) ";" ] . GlobalAttributes = "[" ident (. // We accept module because csc does (even if it is against the specification). if (!t.val.Equals("assembly") && !t.val.Equals("module")) Error("global attribute target specifier \"assembly\" or \"module\" expected"); .) ":" Attribute {IF (NotFinalComma()) "," Attribute } [ "," ] "]" . Attributes = "[" [ IF (IsAttrTargSpec()) ( ident | Keyword ) ":" ] Attribute { IF (la.kind == _comma && Peek(1).kind != _rbrack) "," Attribute } [ "," ] "]" . Keyword = "abstract" | "as" | "base" | "bool" | "break" | "byte" | "case" | "catch" | "char" | "checked" | "class" | "const" | "continue" | "decimal" | "default" | "delegate" | "do" | "double" | "else" | "enum" | "event" | "explicit" | "extern" | "false" | "finally" | "fixed" | "float" | "for" | "foreach" | "goto" | "if" | "implicit" | "in" | "int" | "interface" | "internal" | "is" | "lock" | "long" | "namespace" | "new" | "null" | "object" | "operator" | "out" | "override" | "params" | "private" | "protected" | "public" | "readonly" | "ref" | "return" | "sbyte" | "sealed" | "short" | "sizeof" | "stackalloc" | "static" | "string" | "struct" | "switch" | "this" | "throw" | "true" | "try" | "typeof" | "uint" | "ulong" | "unchecked" | "unsafe" | "ushort" | "using" | "virtual" | "void" | "volatile" | "while" . Attribute = TypeName [ AttributeArguments ] . AttributeArguments (. bool nameFound = false; .) = "(" [ [ IF (IsAssignment()) (. nameFound = true; .) ident "=" ] Expression { "," ( IF (IsAssignment()) (. nameFound = true; .) ident "=" | (. if (nameFound) Error("no positional argument after named arguments"); .) ) Expression } ] ")" . ModifierList = { "new" (. m.Add(newMod); .) | "public" (. m.Add(publicMod); .) | "protected" (. m.Add(protectedMod); .) | "internal" (. m.Add(internalMod); .) | "private" (. m.Add(privateMod); .) | "unsafe" (. m.Add(unsafeMod); .) | "static" (. m.Add(staticMod); .) | "readonly" (. m.Add(readonlyMod); .) | "volatile" (. m.Add(volatileMod); .) | "virtual" (. m.Add(virtualMod); .) | "sealed" (. m.Add(sealedMod); .) | "override" (. m.Add(overrideMod); .) | "abstract" (. m.Add(abstractMod); .) | "extern" (. m.Add(externMod); .) } . /*------------------------------------------------------------------------* *-------------------------------- Types ---------------------------------* *------------------------------------------------------------------------*/ /* Attribute "type" is needed for error messages in EmbeddedStatement * * and for array creation expressions */ Type (. type = TypeKind.simple; .) = ( PrimitiveType | ClassType | "void" (. type = TypeKind.@void; .) ) [ "?" (. if (type == TypeKind.@void) { Error("Unexpected token ?, void must not be nullable."); } .) ] PointerOrArray (. if (type == TypeKind.@void && !voidAllowed) { Error("type expected, void found, maybe you mean void*"); } .) . ResolvedType (. TypeKind type = TypeKind.simple; .) = ( PrimitiveType /* ClassType */ | "object" | "string" | ident [ "::" ident ] [ IF (IsGeneric()) TypeArgumentList ] { "." ident [ IF (IsGeneric()) TypeArgumentList ] } | "void" (. type = TypeKind.@void; .) ) [ IF (la.kind == _question && !firstExpr[kindInContext(Peek(1).kind)]) "?" (. if (type == TypeKind.@void) { Error("Unexpected token ?, void must not be nullable."); } .) ] PointerOrArray (. if (type == TypeKind.@void) Error("type expected, void found, maybe you mean void*"); .) . PointerOrArray (. type = curType; int dims = 0; .) = { IF (IsPointerOrDims()) ( "*" (. type = TypeKind.pointer; .) | "[" (. dims = 1; type = TypeKind.array; .) { "," (. dims++; .) } "]" ) } . PrimitiveType = IntegralType | "float" | "double" | "decimal" | "bool" . IntegralType = "sbyte" | "byte" | "short" | "ushort" | "int" | "uint" | "long" | "ulong" | "char" . ClassType = TypeName | InternalClassType . InternalClassType = "object" | "string" . MemberName = ident [ "::" ident ] [ IF (la.kind == _lt && IsPartOfMemberName()) TypeArgumentList ] { IF (la.kind == _dot && kindInContext(Peek(1).kind) == _ident) "." ident [ IF (la.kind == _lt && IsPartOfMemberName()) TypeArgumentList ] } . // A local type name may be "var", see C# 3.0 specification 8.5.1 Local // variable declarations (implicitly typed local variable declaration). TypeName = ident [ "::" ident ] [ TypeArgumentList ] { "." ident [ TypeArgumentList ] } . /*------------------------------------------------------------------------* *------------------------------ Statements ------------------------------* *------------------------------------------------------------------------*/ Statement (. TypeKind dummy; .) = ( IF (kindInContext(la.kind) == _ident && Peek(1).kind == _colon) ident ":" Statement | "const" Type ident "=" Expression { "," ident "=" Expression } ";" | IF (la.kind == _void || IsLocalVarDecl()) LocalVariableDeclaration ";" | EmbeddedStatement ) . EmbeddedStatement (. TypeKind type;.) = Block | ";" | IF (la.kind == _checked && Peek(1).kind == _lbrace) "checked" Block | IF (la.kind == _unchecked && Peek(1).kind == _lbrace) "unchecked" Block | IF (IsYieldReturn()) ident /* yield is only a contextual keyword */ ( "return" Expression | "break" ) ";" | StatementExpression ";" | "if" "(" Expression ")" EmbeddedStatement [ "else" EmbeddedStatement ] | "switch" "(" Expression ")" "{" { SwitchSection } "}" | "while" "(" Expression ")" EmbeddedStatement | "do" EmbeddedStatement "while" "(" Expression ")" ";" | "for" "(" [ ForInitializer ] ";" [ Expression ] ";" [ ForIterator ] ")" EmbeddedStatement | "foreach" "(" LocalVariableType ident "in" Expression ")" EmbeddedStatement | "break" ";" | "continue" ";" | "goto" ( ident | "case" Expression | "default" ) ";" | "return" [ Expression ] ";" | "throw" [ Expression ] ";" | "try" Block ( CatchClauses [ "finally" Block ] | "finally" Block ) | "lock" "(" Expression ")" EmbeddedStatement | "using" "(" ResourceAcquisition ")" EmbeddedStatement | "unsafe" Block | "fixed" "(" Type (. if (type != TypeKind.pointer) Error("can only fix pointer types"); .) ident "=" Expression { "," ident "=" Expression } ")" EmbeddedStatement . Block = "{" { Statement } "}" . StatementExpression (. bool isAssignment = assnStartOp[kindInContext(la.kind)] || IsTypeCast(); .) = Unary ( AssignmentOperator Expression | (. if (isAssignment) Error("error in assignment."); .) ) . AssignmentOperator = ( "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "&=" | "|=" | "^=" | "<<=" | ">" (. int pos = t.pos; .) ">=" (. if (pos+1 < t.pos) Error("no whitespace allowed in right shift assignment"); .) ) . SwitchSection = SwitchLabel { IF (la.kind == _case || (la.kind == _default && Peek(1).kind == _colon)) SwitchLabel } Statement { IF (IsNoSwitchLabelOrRBrace()) Statement } . SwitchLabel = ( "case" Expression ":" | "default" ":" ) . ForInitializer = IF (IsLocalVarDecl()) LocalVariableDeclaration | StatementExpression { "," StatementExpression } . ForIterator = StatementExpression { "," StatementExpression } . CatchClauses = "catch" ( Block | "(" ClassType [ ident ] ")" Block [ CatchClauses ] ) . ResourceAcquisition = IF (IsLocalVarDecl()) LocalVariableDeclaration | Expression . /*------------------------------------------------------------------------* *----------------------------- Expressions ------------------------------* *------------------------------------------------------------------------*/ Expression = (. checkForContextSwitch(); .) ( QueryExpression | IF (IsImplicitTypedLambdaExpression()) ImplicitLambdaExpression | IF (IsExplicitTypedLambdaExpression()) "(" [ AnonymousMethodParameter { "," AnonymousMethodParameter } ] ")" "=>" ( Block | Expression ) | // Assignment, Non Assignment Unary ( IF (assgnOps[kindInContext(la.kind)] || (la.kind == _gt && Peek(1).kind == _gteq)) AssignmentOperator Expression | NullCoalescingExpr [ "?" Expression ":" Expression ] ) ) . NullCoalescingExpr = OrExpr { "??" Unary OrExpr } . OrExpr = AndExpr { "||" Unary AndExpr } . AndExpr = BitOrExpr { "&&" Unary BitOrExpr } . BitOrExpr = BitXorExpr { "|" Unary BitXorExpr } . BitXorExpr = BitAndExpr { "^" Unary BitAndExpr } . BitAndExpr = EqlExpr { "&" Unary EqlExpr } . EqlExpr = RelExpr { ( "!=" | "==" ) Unary RelExpr } . RelExpr = ShiftExpr { ( "<" | ">" | "<=" | ">=" ) Unary ShiftExpr | ( "is" | "as" ) ResolvedType } . ShiftExpr = AddExpr { IF (IsShift()) ( "<<" | ">" ">" ) Unary AddExpr } . AddExpr = MulExpr { ( "+" | "-" ) Unary MulExpr } . MulExpr = { ( "*" | "/" | "%" ) Unary } . Unary (. TypeKind dummy; .) = { IF (unaryHead[la.kind] || IsTypeCast()) ( "+" | "-" | "!" | "~" | "++" | "--" | "*" | "&" | "(" Type ")" /* Problem: "(" Type ")" from here and * * "(" Expr ")" from Primary * * are not distinguishable * * Solution: (in IsTypeCast()) */ ) } Primary . Primary (. TypeKind type; bool isArrayCreation = false; .) = ( Literal | "(" Expression ")" | ( "bool" | "byte" | "char" | "decimal" | "double" | "float" | "int" | "long" | "object" | "sbyte" | "short" | "string" | "uint" | "ulong" | "ushort" ) [ /* Here we are more generous and allow a single type name to be an expression to enable the parsing of the proprietary csc feature __refvalue, which expects a type as the second argument. e.g: int i = __refvalue(typedRef, int) Thus the option here not standard compatible but enables us to be csc compatible. Annotation: This leads to the LL1 warning "dot is start & successor of deletable structure" but this can be ignored because the greedy default behavior is correct. */ "." ident [ IF (IsGeneric()) TypeArgumentList ] ] | ident [ "::" ident [ TypeArgumentList ] "." ident ] [ IF (IsGeneric()) TypeArgumentList ] | "this" | "base" | "new" (Type ( /*--- delegate or object creation expression: * * Note: a delegate creation expression allows only a single Expr * * not an argument list, but this is not checked here */ "(" [ Argument { "," Argument } ] ")" [ ObjectOrCollectionInitializer ] | IF (la.kind == _lbrace && type != TypeKind.array) ObjectOrCollectionInitializer | (. int dims = 1; .) "[" Expression { "," Expression (. ++dims; .) } "]" { IF (IsDims()) "[" (. dims = 1; .) { "," (. ++dims; .) } "]" } (. isArrayCreation = true; .) [ ArrayInitializer (. isArrayCreation = false; /* From the specification point of view (14.5.6) this would be an array creation expression, but csc does allow element access on an initialized array creation expression so we allow it too. The same holds for the ArrayInitializer only alternative below. */ .) ] | ArrayInitializer (. if (type != TypeKind.array) Error("array type expected"); isArrayCreation = true; .) ) | AnonymousObjectInitializer | /* Implicitly typed array. */ (. int dims = 1; .) "[" { "," (. dims++; .) } "]" ArrayInitializer (. isArrayCreation = true; .) ) | "typeof" "(" Type ")" | "checked" "(" Expression ")" | "unchecked" "(" Expression ")" | "default" "(" Type ")" | "delegate" [ "(" [ AnonymousMethodParameter { "," AnonymousMethodParameter } ] ")" ] Block | "sizeof" "(" Type ")" ) { "++" | "--" | "->" ident [ IF (IsGeneric()) TypeArgumentList ] | "." ident [ IF (IsGeneric()) TypeArgumentList ] | "(" [ Argument { "," Argument } ] ")" | (. if (isArrayCreation) Error("element access not allowed on array creation"); .) "[" Expression { "," Expression } "]" } . AnonymousObjectInitializer = "{" [ MemberDeclaratorList ] "}" . MemberDeclaratorList = MemberDeclarator [ "," [ MemberDeclaratorList ] ] . /* This seems too generous, the spec (May 2006) allows only: * * - simple-name * * - member-access * * - identifier = expression * * Yet this should be good enough to skip over it, or to grep * * the significant parts out. */ MemberDeclarator = [ IF (IsAssignment()) ident "=" ] Expression . /* We are very generous here, we allow any expression as a member * * or element initializer. The spec only allows assignments of * * expressions and ObjectOrCollectionInitializers for * * MemberInitializers and non assignment expressions for * * ElementInitializers. See C# 3.0 Spec 26.4 from May 2006. */ ObjectOrCollectionInitializer = "{" [ MemberOrElementInitializer { IF (la.kind == _comma && Peek(1).kind != _rbrace) "," MemberOrElementInitializer } [ "," ] ] "}" . MemberOrElementInitializer = IF (IsAssignment()) ( ident "=" ( Expression | ObjectOrCollectionInitializer ) ) | ( Argument | "{" Argument { "," Argument } "}" ) . Literal = ( intCon | realCon | charCon | stringCon | "true" | "false" | "null" ) . AnonymousMethodParameter (. TypeKind dummy; .) = [ "ref" | "out" ] Type ident . ImplicitLambdaExpression = ( ImplicitAnonymousFunctionParameter | ( "(" [ ImplicitAnonymousFunctionParameter { "," ImplicitAnonymousFunctionParameter } ] ")" ) ) "=>" ImplicitTypedLambdaBody . ImplicitTypedLambdaBody = ( Block | Expression ) . ImplicitAnonymousFunctionParameter = ident . /*------------------------------------------------------------------------* *-------------------------------- Misc ---------------------------------* *------------------------------------------------------------------------*/ VariableDeclarators = ident [ "=" VariableInitializer ] { "," ident [ "=" VariableInitializer ] } . OverloadableOp (. op = plusOp; .) = /* unary operators */ "+" | "-" (. op = minusOp; .) | "!" (. op = notOp; .) | "~" (. op = tildeOp; .) | "++" (. op = incOp; .) | "--" (. op = decOp; .) | "true" (. op = trueOp; .) | "false" (. op = falseOp; .) | /* binary operators (plus +, -) */ "*" (. op = timesOp; .) | "/" (. op = divOp; .) | "%" (. op = modOp; .) | "&" (. op = andOp; .) | "|" (. op = orOp; .) | "^" (. op = xorOp; .) | "<<" (. op = lshiftOp; .) | "==" (. op = eqOp; .) | "!=" (. op = neqOp; .) | ">" (. op = gtOp; .) [ (. if (la.pos > t.pos+1) Error("no whitespace allowed in right shift operator"); .) ">" (. op = rshiftOp; .) ] | "<" (. op = ltOp; .) | ">=" (. op = gteOp; .) | "<=" (. op = lteOp; .) . TypeParameterList = "<" { Attributes } ident { "," { Attributes } ident } ">" . TypeArgumentList (. TypeKind dummy; .) = "<" ( Type | /* empty type argument */ ) { "," ( Type | /* empty type argument */ ) } ">" . TypeParameterConstraintsClause = ident (. if (!t.val.Equals("where")) { Error("type parameter constraints clause must start with: where"); } .) ident ":" ( ( "class" | "struct" | "object" | "string" | TypeName ) { IF (la.kind == _comma && Peek(1).kind != _new) "," TypeName } [ "," "new" "(" ")" ] | "new" "(" ")" ) . QueryExpression = (. enterContext(); .) FromClause QueryBody (. leaveContext(); .) . FromClause (. TypeKind type; .) = "from" [ IF (Peek(1).kind != _in) Type ] ident "in" Expression . QueryBody = { QueryBodyClause } ( SelectClause | GroupClause ) [ "into" ident QueryBody ] . QueryBodyClause = ( FromClause | LetClause | WhereClause | JoinClause | OrderByClause ) . LetClause = "let" ident "=" Expression . WhereClause = "where" Expression . JoinClause (. TypeKind type; .) = "join" [ IF (Peek(1).kind != _in) Type ] ident "in" Expression "on" Expression "equals" Expression [ "into" ident ] . OrderByClause = "orderby" Ordering {"," Ordering } . Ordering = Expression [ "ascending" | "descending" ] . SelectClause = "select" Expression . GroupClause = "group" Expression "by" Expression . END CS3.