Hello, I need developers that have generics code to try a new version of the C# compiler. I fixed bugs #72908 and #59289, but am afraid that my changes could have introduced regressions.
This compiler can compile our SVN tree, and passes all of our C# tests, but I would like to get more testing. A binary is available here: http://primates.ximian.com/~miguel/tmp/gmcs.exe To install, copy this gmcs.exe into your $prefix/lib/mono/2.0 directory and try building your generics code with it. If you rather use the source, I have attached the current patch. Miguel
Index: cs-tokenizer.cs =================================================================== --- cs-tokenizer.cs (revision 55012) +++ cs-tokenizer.cs (working copy) @@ -44,6 +44,7 @@ bool handle_assembly = false; bool handle_constraints = false; bool handle_typeof = false; + bool simple_name_deambiguation = false; Location current_location; Location current_comment_location = Location.Null; ArrayList escapedIdentifiers = new ArrayList (); @@ -172,6 +173,29 @@ } } + Stack state = new Stack (); + + // + // Sets the new state for the simple-name-deambiguation. + // + public void PushSND (bool value) + { + state.Push (simple_name_deambiguation); + simple_name_deambiguation = value; + if (debug) + Console.WriteLine ("PUSH: ambiguation IS: {0}", simple_name_deambiguation); + } + + // + // Restores the previous state for the simple-name-deambiguation. + // + public void PopSND () + { + simple_name_deambiguation = (bool) state.Pop (); + if (debug) + Console.WriteLine ("POP: ambiguation is: {0}", simple_name_deambiguation); + } + public XmlCommentState doc_state { get { return xmlDocState; } set { @@ -255,6 +279,59 @@ } } + // + // This is used when the tokenizer needs to save + // the current position as it needs to do some parsing + // on its own to deamiguate a token in behalf of the + // parser. + // + Stack position_stack = new Stack (); + class Position { + public int position; + public int ref_line; + public int col; + public int putback_char; + public int previous_col; + public int parsing_generic_less_than; + + public Position (Tokenizer t) + { + position = t.reader.Position; + ref_line = t.ref_line; + col = t.col; + putback_char = t.putback_char; + previous_col = t.previous_col; + parsing_generic_less_than = t.parsing_generic_less_than; + } + } + + public void PushPosition () + { + position_stack.Push (new Position (this)); + } + + public void PopPosition (bool restore_glt) + { + Position p = (Position) position_stack.Pop (); + + reader.Position = p.position; + ref_line = p.ref_line; + col = p.col; + putback_char = p.putback_char; + previous_col = p.previous_col; + + if (debug) + Console.WriteLine ("{3} PopPopsition with {0} {1}->{2}", restore_glt, parsing_generic_less_than, p.parsing_generic_less_than,Location); + if (restore_glt) + parsing_generic_less_than = p.parsing_generic_less_than; + } + + // Do not reset the position, ignore it. + public void DiscardPosition () + { + position_stack.Pop (); + } + static void AddKeyword (string kw, int token) { keywordStrings.Add (kw, kw); if (keywords [kw.Length] == null) { @@ -354,6 +431,8 @@ AddKeyword ("partial", Token.PARTIAL); } + static bool debug = false; + // // Class initializer // @@ -364,6 +443,9 @@ styles = NumberStyles.Float; string_builder = new System.Text.StringBuilder (); + + if (Environment.GetEnvironmentVariable ("D") != null) + debug = true; } int GetKeyword (char[] id, int id_len) @@ -478,7 +560,12 @@ return false; } - bool parse_less_than () + // + // The level indicates the nestedness level. This is required + // to perform the extra check in case that simple_name_deambiguation + // is set to true + // + bool parse_less_than (int level) { start: int the_token = token (); @@ -514,14 +601,27 @@ again: the_token = token (); - if (the_token == Token.OP_GENERICS_GT) + if (the_token == Token.OP_GENERICS_GT){ + // + // Check for the 9.2.3 special cases + // + if (debug) + Console.WriteLine ("__GT {0}" + Environment.StackTrace, simple_name_deambiguation); + if (level == 0 && simple_name_deambiguation){ + PushPosition (); + bool v = next_token_is_allowed_after_generic_closing (); + PopPosition (true); + return v; + } return true; + } + else if ((the_token == Token.COMMA) || (the_token == Token.DOT)) goto start; else if (the_token == Token.INTERR) goto again; else if (the_token == Token.OP_GENERICS_LT) { - if (!parse_less_than ()) + if (!parse_less_than (level+1)) return false; goto again; } else if (the_token == Token.OPEN_BRACKET) { @@ -537,8 +637,52 @@ return false; } - int parsing_generic_less_than = 0; + // + // This method is used to deamiguate the meaning of '>' + // while parsing a '<'...'>' structure. It implements the + // check specified in 9.2.3 "Grammar Ambiguities" on the C# 3rd + // edition + // + bool next_token_is_allowed_after_generic_closing () + { + PushPosition (); + int next_token = token (); + bool ret; + //Console.WriteLine ("NextToken used for deambiguation is: " + next_token); + if (next_token == Token.OPEN_PARENS || + next_token == Token.CLOSE_PARENS || + next_token == Token.CLOSE_PARENS_OPEN_PARENS || + next_token == Token.CLOSE_PARENS_MINUS || + next_token == Token.CLOSE_PARENS_CAST || + next_token == Token.CLOSE_PARENS_NO_CAST || + next_token == Token.CLOSE_PARENS || + next_token == Token.CLOSE_BRACKET || + next_token == Token.COLON || next_token == Token.SEMICOLON || + next_token == Token.COMMA || next_token == Token.DOT || + next_token == Token.INTERR || + next_token == Token.OP_NE || next_token == Token.OP_EQ) + ret = true; + else + ret = false; + + // + // The following line is a *hack*, it is here to parse: + // (Blah<a,b>[]) case. but it is likely going to show + // up as another bug elsewhere. + // + if (next_token == Token.OPEN_BRACKET) + ret = true; + + PopPosition (true); + + if (debug) + Console.WriteLine ("DETERMINED: Token {0} allowed state: {1}", CSharpParser.yyname (next_token), ret); + return ret; + } + + public int parsing_generic_less_than = 0; + int is_punct (char c, ref bool doread) { int d; @@ -568,16 +712,9 @@ --deambiguate_close_parens; - // Save current position and parse next token. - int old = reader.Position; - int old_ref_line = ref_line; - int old_col = col; - + PushPosition (); int new_token = token (); - reader.Position = old; - ref_line = old_ref_line; - col = old_col; - putback_char = -1; + PopPosition (true); if (new_token == Token.OPEN_PARENS) return Token.CLOSE_PARENS_OPEN_PARENS; @@ -605,22 +742,25 @@ if (parsing_generic_less_than++ > 0) return Token.OP_GENERICS_LT; - int old = reader.Position; if (handle_typeof) { int dimension; + PushPosition (); if (parse_generic_dimension (out dimension)) { val = dimension; + DiscardPosition (); return Token.GENERIC_DIMENSION; } - reader.Position = old; - putback_char = -1; + PopPosition (true); } // Save current position and parse next token. - old = reader.Position; - bool is_generic_lt = parse_less_than (); - reader.Position = old; - putback_char = -1; + PushPosition (); + if (debug) + Console.WriteLine ("Before parse_less_than: {0}", parsing_generic_less_than); + bool is_generic_lt = parse_less_than (0); + if (debug) + Console.WriteLine ("After parse_less_than: {0}", parsing_generic_less_than); + PopPosition (false); if (is_generic_lt) { parsing_generic_less_than++; @@ -1250,10 +1390,10 @@ { int x; if (putback_char != -1) { + //Console.WriteLine ("Using Putback_char: {0}", (char) putback_char); x = putback_char; putback_char = -1; - } - else + } else x = reader.Read (); if (x == '\n') { line++; @@ -1263,6 +1403,7 @@ } else col++; + //Console.WriteLine ("getChar: {0}", (char) x); return x; } @@ -1362,12 +1503,25 @@ } } + void r (int token) + { + if (debug) + Console.WriteLine ("{1}TOKEN: {0} {2}/{3}", CSharpParser.yyname (token), level, position_stack.Count, parsing_generic_less_than); + } + + int level = 0; public int token () { + level++; + //Console.WriteLine ("Starting read at {0}", reader.Position); + current_token = xtoken (); - if (current_token != Token.DEFAULT) + if (current_token != Token.DEFAULT){ + r (current_token); + level--; return current_token; + } int c = consume_whitespace (); if (c == -1) @@ -1377,6 +1531,10 @@ else putback (c); + if (current_token == Token.FOR) + parsing_generic_less_than = 0; + r (current_token); + level--; return current_token; } @@ -2015,23 +2173,15 @@ if (res == Token.PARTIAL) { // Save current position and parse next token. - int old = reader.Position; - int old_putback = putback_char; - int old_ref_line = ref_line; - int old_col = col; + PushPosition (); - putback_char = -1; - int next_token = token (); bool ok = (next_token == Token.CLASS) || (next_token == Token.STRUCT) || (next_token == Token.INTERFACE) || (next_token == Token.ENUM); // "partial" is a keyword in 'partial enum', even though it's not valid - reader.Position = old; - ref_line = old_ref_line; - col = old_col; - putback_char = old_putback; + PopPosition (true); if (ok) return res; Index: cs-parser.jay =================================================================== --- cs-parser.jay (revision 55012) +++ cs-parser.jay (working copy) @@ -104,6 +104,13 @@ bool global_attrs_enabled = true; bool has_get, has_set; + // + // Used to pass information down to member-name that we are + // parsing namespace_or_type_name and that it should not + // activate the grammar deambiguation. + // + bool in_namespace_or_typename; + %} %token EOF @@ -2695,15 +2702,31 @@ ; namespace_or_type_name - : member_name + : // + // inline member_name here + // + IDENTIFIER { + lexer.PushSND (false); + oob_stack.Push (in_namespace_or_typename); + in_namespace_or_typename = true; + } opt_type_argument_list { + lexer.PopSND (); + in_namespace_or_typename = (bool) oob_stack.Pop (); + LocatedToken lt = (LocatedToken) $1; + $$ = new MemberName (lt.Value, (TypeArguments) $3, lt.Location); + } | IDENTIFIER DOUBLE_COLON IDENTIFIER { LocatedToken lt1 = (LocatedToken) $1; LocatedToken lt2 = (LocatedToken) $3; $$ = new MemberName (lt1.Value, lt2.Value, lt2.Location); } - | namespace_or_type_name DOT IDENTIFIER opt_type_argument_list { - LocatedToken lt = (LocatedToken) $3; - $$ = new MemberName ((MemberName) $1, lt.Value, (TypeArguments) $4, lt.Location); + | namespace_or_type_name DOT { + lexer.PushSND (false); + //Console.WriteLine ("namespace_or_type_name: Setting deambiguation to {0}", lexer.SimpleNameDeambiguation); + } IDENTIFIER opt_type_argument_list { + lexer.PopSND (); + LocatedToken lt = (LocatedToken) $4; + $$ = new MemberName ((MemberName) $1, lt.Value, (TypeArguments) $5, lt.Location); } ; @@ -2722,6 +2745,7 @@ if (RootContext.Version == LanguageVersion.ISO_1) Report.FeatureIsNotStandardized (lexer.Location, "generics"); } + | GENERIC_DIMENSION { $$ = new TypeArguments ((int) $1, lexer.Location); @@ -2757,7 +2781,6 @@ } ; - /* * Before you think of adding a return_type, notice that we have been * using two rules in the places where it matters (one rule using type @@ -2892,7 +2915,51 @@ } ; +type_argument_list_or_qualified + : opt_type_argument_list + | DOUBLE_COLON IDENTIFIER { $$ = $2; } + ; + // +// This is used so we can push the SimpleNameDeambiguation state +// before we parse a member-base (which might need the extra token +// reading to deamiguate). +// +// To avoid a grammar conflict between a qualified-alias and a +// member-name, I merged both rules into this one and call +// PushSND before. It does not matter/hurt QualifiedAliasMember +// and helps our cause +// +// This production merges two old productions in primary-expression: +// | member_name +// | IDENTIFIER DOUBLE_COLON IDENTIFIER +// + +member_name_or_qualified_alias + : IDENTIFIER + { + lexer.PushSND (in_namespace_or_typename == false); + } + type_argument_list_or_qualified + { + lexer.PopSND (); + + // + // Its a member-name with or without type-arguments + // + if ($3 == null || $3 is TypeArguments){ + LocatedToken lt = (LocatedToken) $1; + MemberName mn = new MemberName (lt.Value, (TypeArguments) $3, lt.Location); + $$ = mn.GetTypeExpression (); + } else { + LocatedToken lt1 = (LocatedToken) $1; + LocatedToken lt2 = (LocatedToken) $3; + $$ = new QualifiedAliasMember (lt1.Value, lt2.Value, lt2.Location); + } + } + ; + +// // Expressions, section 7.5 // primary_expression @@ -2900,17 +2967,7 @@ { // 7.5.1: Literals } - | member_name - { - MemberName mn = (MemberName) $1; - $$ = mn.GetTypeExpression (); - } - | IDENTIFIER DOUBLE_COLON IDENTIFIER - { - LocatedToken lt1 = (LocatedToken) $1; - LocatedToken lt2 = (LocatedToken) $3; - $$ = new QualifiedAliasMember (lt1.Value, lt2.Value, lt2.Location); - } + | member_name_or_qualified_alias | parenthesized_expression | default_value_expression | member_access @@ -4016,6 +4073,7 @@ block : OPEN_BRACE { + //lexer.parsing_generic_less_than = 0; if (current_block == null){ current_block = new ToplevelBlock ((ToplevelBlock) top_current_block, current_local_parameters, (Location) $1); top_current_block = current_block; @@ -4041,7 +4099,7 @@ ; statement_list - : statement + : statement | statement_list statement ; @@ -4052,10 +4110,13 @@ current_block.AddStatement ((Statement) $1); current_block = (Block) $1; } + //lexer.parsing_generic_less_than = 0; + } | valid_declaration_statement { current_block.AddStatement ((Statement) $1); + //lexer.parsing_generic_less_than = 0; } | labeled_statement ;
_______________________________________________ Mono-devel-list mailing list Mono-devel-list@lists.ximian.com http://lists.ximian.com/mailman/listinfo/mono-devel-list