%{ // // cs-parser.jay: The Parser for the C# compiler // // Author: Miguel de Icaza (miguel@gnu.org) // // Licensed under the terms of the GNU GPL // // (C) 2001 Ximian, Inc (http://www.ximian.com) // // TODO: // (1) Get rid of the *Collections.cs, that is an idea I took from System.CodeDOM // And come to think of it, it is not that great, it duplicates a lot of code // for something which is not really needed. We still have piles of typecasts // anwyays (due to the nature of the stack being a collection of Objects). // // (2) Figure out why error productions dont work. `type-declaration' is a // great spot to put an `error' because you can reproduce it with this input: // "public X { }" // // (3) Move Modifier checking from each object into the parser itself, that will // get rid of the global "error" symbol that we use now to report errors. // We still need to pass a pointer to the tree.ErrorHandler, but that is a // separate problem // using System.Text; using CIR; using System; namespace CIR { using System.Collections; using Mono.Languages; /// /// The C# Parser /// public class CSharpParser : GenericParser { Namespace current_namespace; TypeContainer current_container; // // Current block is used to add statements as we find // them. // Block current_block; // // Current interface is used by the various declaration // productions in the interface declaration to "add" // the interfaces as we find them. // Interface current_interface; // // This is used by the unary_expression code to resolve // a name against a parameter. // Parameters current_local_parameters; // // Using during property parsing to describe the implicit // value parameter that is passed to the "set" accesor // method // Parameter [] implicit_value_parameters; // // Used to determine if we are parsing the get/set pair // of an indexer or a property // bool parsing_indexer; // // Used to record all types defined // Tree tree; RootContext rc; %} %token EOF %token NONE /* This token is never returned by our lexer */ %token ERROR // This is used not by the parser, but by the tokenizer. // do not remove. /* *These are the C# keywords */ %token ABSTRACT %token AS %token ADD %token BASE %token BOOL %token BREAK %token BYTE %token CASE %token CATCH %token CHAR %token CHECKED %token CLASS %token CONST %token CONTINUE %token DECIMAL %token DEFAULT %token DELEGATE %token DO %token DOUBLE %token ELSE %token ENUM %token EVENT %token EXPLICIT %token EXTERN %token FALSE %token FINALLY %token FIXED %token FLOAT %token FOR %token FOREACH %token GOTO %token IF %token IMPLICIT %token IN %token INT %token INTERFACE %token INTERNAL %token IS %token LOCK %token LONG %token NAMESPACE %token NEW %token NULL %token OBJECT %token OPERATOR %token OUT %token OVERRIDE %token PARAMS %token PRIVATE %token PROTECTED %token PUBLIC %token READONLY %token REF %token RETURN %token REMOVE %token SBYTE %token SEALED %token SHORT %token SIZEOF %token STATIC %token STRING %token STRUCT %token SWITCH %token THIS %token THROW %token TRUE %token TRY %token TYPEOF %token UINT %token ULONG %token UNCHECKED %token UNSAFE %token USHORT %token USING %token VIRTUAL %token VOID %token WHILE /* C# keywords which are not really keywords */ %token GET "get" %token SET "set" /* C# single character operators/punctuation. */ %token OPEN_BRACE "{" %token CLOSE_BRACE "}" %token OPEN_BRACKET "[" %token CLOSE_BRACKET "]" %token OPEN_PARENS "(" %token CLOSE_PARENS ")" %token DOT "." %token COMMA "," %token COLON ":" %token SEMICOLON ";" %token TILDE "~" %token PLUS "+" %token MINUS "-" %token BANG "!" %token ASSIGN "=" %token OP_LT "<" %token OP_GT ">" %token BITWISE_AND "&" %token BITWISE_OR "|" %token STAR "*" %token PERCENT "%" %token DIV "/" %token CARRET "^" %token INTERR "?" /* C# multi-character operators. */ %token OP_INC "++" %token OP_DEC "--" %token OP_SHIFT_LEFT "<<" %token OP_SHIFT_RIGHT ">>" %token OP_LE "<=" %token OP_GE ">=" %token OP_EQ "==" %token OP_NE "!=" %token OP_AND "&&" %token OP_OR "||" %token OP_MULT_ASSIGN "*=" %token OP_DIV_ASSIGN "/=" %token OP_MOD_ASSIGN "%=" %token OP_ADD_ASSIGN "+=" %token OP_SUB_ASSIGN "-=" %token OP_SHIFT_LEFT_ASSIGN "<<=" %token OP_SHIFT_RIGHT_ASSIGN ">>=" %token OP_AND_ASSIGN "&=" %token OP_XOR_ASSIGN "^=" %token OP_OR_ASSIGN "|=" %token OP_PTR "->" /* Numbers */ %token LITERAL_INTEGER "int literal" %token LITERAL_FLOAT "float literal" %token LITERAL_DOUBLE "double literal" %token LITERAL_DECIMAL "decimal literal" %token LITERAL_CHARACTER "character literal" %token LITERAL_STRING "string literal" %token IDENTIFIER /* Add precedence rules to solve dangling else s/r conflict */ %nonassoc LOWPREC %nonassoc IF %nonassoc ELSE %right ASSIGN %left OP_OR %left OP_AND %left BITWISE_OR %left BITWISE_AND %left OP_SHIFT_LEFT OP_SHIFT_RIGHT %left PLUS MINUS %left STAR DIV PERCENT %right BANG CARRET UMINUS %nonassoc OP_INC OP_DEC %left OPEN_PARENS %left OPEN_BRACKET OPEN_BRACE %left DOT %nonassoc HIGHPREC %start compilation_unit /*%start namespace_declaration */ %% compilation_unit : opt_using_directives opt_attributes opt_namespace_member_declarations EOF { // Check that using comes only before namespace elements } ; using_directives : using_directive | using_directives using_directive ; using_directive : using_alias_directive | using_namespace_directive ; using_alias_directive : USING IDENTIFIER ASSIGN namespace_or_type_name SEMICOLON { // FIXME : Need to implement actual action. } ; using_namespace_directive : USING namespace_name SEMICOLON { current_namespace.Using ((string) $2); } ; // namespace_declarations // : namespace_declaration // | namespace_declarations namespace_declaration namespace_declaration : NAMESPACE qualified_identifier { current_namespace = new Namespace (current_namespace, (string) $2); tree.RecordNamespace ((string) $2, current_namespace); } namespace_body opt_semicolon { current_namespace = current_namespace.Parent; } ; opt_semicolon : /* empty */ | SEMICOLON ; opt_comma : /* empty */ | COMMA ; qualified_identifier : IDENTIFIER | qualified_identifier DOT IDENTIFIER { $$ = (($1).ToString ()) + "." + ($3.ToString ()); } ; namespace_name : namespace_or_type_name ; namespace_body : OPEN_BRACE opt_using_directives opt_namespace_member_declarations CLOSE_BRACE { } ; opt_using_directives : /* empty */ | using_directives ; opt_namespace_member_declarations : /* empty */ | namespace_member_declarations ; namespace_member_declarations : namespace_member_declaration | namespace_member_declarations namespace_member_declaration ; namespace_member_declaration : type_declaration { int mod_flags = 0; string name = ""; if ($1 is Class){ Class c = (Class) $1; mod_flags = c.ModFlags; name = c.Name; } else if ($1 is Struct){ Struct s = (Struct) $1; mod_flags = s.ModFlags; name = s.Name; } else break; // // We remove this error until we can //if ((mod_flags & (Modifiers.PRIVATE|Modifiers.PROTECTED)) != 0){ // error (1527, "Namespace elements cant be explicitly " + // "declared private or protected in `" + name + "'"); //} } | namespace_declaration ; type_declaration : class_declaration | struct_declaration | interface_declaration | enum_declaration | delegate_declaration ; // // Attributes 17.2 // opt_attributes : /* empty */ { $$ = null; } | attribute_section opt_attributes { Attributes attrs; if ($2 != null) { attrs = (Attributes) $2; attrs.AddAttribute ((Attribute) $1); } else attrs = new Attributes ((Attribute) $1); $$ = attrs; } ; attribute_section : OPEN_BRACKET attribute_target_specifier attribute_list CLOSE_BRACKET { string target = null; if ($2 != null) target = (string) $2; $$ = new Attribute (target, (ArrayList) $3); } | OPEN_BRACKET attribute_list CLOSE_BRACKET { $$ = new Attribute (null, (ArrayList) $2); } ; attribute_target_specifier : attribute_target COLON { $$ = $1; } ; attribute_target : IDENTIFIER { CheckAttributeTarget ((string) $1); $$ = $1; } | EVENT { $$ = "event"; } | RETURN { $$ = "return"; } ; attribute_list : attribute { ArrayList attrs = new ArrayList (); attrs.Add ($1); $$ = attrs; } | attribute_list COMMA attribute { ArrayList attrs = (ArrayList) $1; attrs.Add ($3); $$ = attrs; } ; attribute : attribute_name opt_attribute_arguments { $$ = new DictionaryEntry ($1, $2); } ; attribute_name : type_name { /* reserved attribute name or identifier: 17.4 */ } ; opt_attribute_arguments : /* empty */ { $$ = null; } | OPEN_PARENS attribute_arguments CLOSE_PARENS { $$ = $2; } ; attribute_arguments : expression { ArrayList args = new ArrayList (); args.Add ($1); $$ = args; } | attribute_arguments COMMA expression { ArrayList args = (ArrayList) $1; args.Add ($3); $$ = args; } ; class_body : OPEN_BRACE opt_class_member_declarations CLOSE_BRACE ; opt_class_member_declarations : /* empty */ | class_member_declarations ; class_member_declarations : class_member_declaration | class_member_declarations class_member_declaration ; class_member_declaration : constant_declaration // done | field_declaration // done | method_declaration // done | property_declaration // done | event_declaration // done | indexer_declaration // done | operator_declaration // done | constructor_declaration // done | destructor_declaration // done | type_declaration ; struct_declaration : opt_attributes opt_modifiers STRUCT IDENTIFIER { Struct new_struct; string full_struct_name = MakeName ((string) $4); new_struct = new Struct (rc, current_container, full_struct_name, (int) $2, (Attributes) $1, lexer.Location); current_container = new_struct; current_container.Namespace = current_namespace; tree.RecordStruct (full_struct_name, new_struct); } opt_struct_interfaces struct_body opt_semicolon { Struct new_struct = (Struct) current_container; current_container = current_container.Parent; CheckDef (current_container.AddStruct (new_struct), new_struct.Name); $$ = new_struct; } ; opt_struct_interfaces : /* empty */ | struct_interfaces ; struct_interfaces : struct_interface | struct_interfaces struct_interface ; struct_interface : COLON type_list ; struct_body : OPEN_BRACE opt_struct_member_declarations CLOSE_BRACE ; opt_struct_member_declarations : /* empty */ | struct_member_declarations ; struct_member_declarations : struct_member_declaration | struct_member_declarations struct_member_declaration ; struct_member_declaration : constant_declaration | field_declaration | method_declaration | property_declaration | event_declaration | indexer_declaration | operator_declaration | constructor_declaration | type_declaration ; constant_declaration : opt_attributes opt_modifiers CONST type constant_declarators SEMICOLON { foreach (DictionaryEntry constant in (ArrayList) $5){ Constant c = new Constant ( (string) $4, (string) constant.Key, (Expression) constant.Value, (int) $2, (Attributes) $1); CheckDef (current_container.AddConstant (c), c.Name); } } ; constant_declarators : constant_declarator { ArrayList constants = new ArrayList (); constants.Add ($1); $$ = constants; } | constant_declarators COMMA constant_declarator { ArrayList constants = (ArrayList) $1; constants.Add ($3); } ; constant_declarator : IDENTIFIER ASSIGN constant_expression { $$ = new DictionaryEntry ($1, $3); } ; field_declaration : opt_attributes opt_modifiers type variable_declarators SEMICOLON { string type = (string) $3; int mod = (int) $2; foreach (VariableDeclaration var in (ArrayList) $4){ Field field = new Field (type, mod, var.identifier, var.expression_or_array_initializer, (Attributes) $1); CheckDef (current_container.AddField (field), field.Name); } } ; variable_declarators : variable_declarator { ArrayList decl = new ArrayList (); decl.Add ($1); $$ = decl; } | variable_declarators COMMA variable_declarator { ArrayList decls = (ArrayList) $1; decls.Add ($3); $$ = $1; } ; variable_declarator : IDENTIFIER ASSIGN variable_initializer { $$ = new VariableDeclaration ((string) $1, $3, lexer.Location); } | IDENTIFIER { $$ = new VariableDeclaration ((string) $1, null, lexer.Location); } ; variable_initializer : expression { $$ = $1; } | array_initializer { $$ = $1; } ; method_declaration : method_header method_body { Method method = (Method) $1; Block b = (Block) $2; if (b == null){ if ((method.ModFlags & (Modifiers.EXTERN | Modifiers.ABSTRACT)) == 0){ Report.Error ( 501, lexer.Location, "`" + current_container.Name + "." + method.Name + "'" + "must declare a body because it is not marked abstract or extern"); } } method.Block = (Block) $2; CheckDef (current_container.AddMethod (method), method.Name); current_local_parameters = null; } ; method_header : opt_attributes opt_modifiers type member_name OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS { Method method = new Method ((string) $3, (int) $2, (string) $4, (Parameters) $6, (Attributes) $1, lexer.Location); current_local_parameters = (Parameters) $6; $$ = method; } | opt_attributes opt_modifiers VOID member_name OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS { Method method = new Method ("System.Void", (int) $2, (string) $4, (Parameters) $6, (Attributes) $1, lexer.Location); current_local_parameters = (Parameters) $6; $$ = method; } ; method_body : block | SEMICOLON { $$ = null; } ; opt_formal_parameter_list : /* empty */ { $$ = new Parameters (null, null); } | formal_parameter_list ; formal_parameter_list : fixed_parameters { ArrayList pars_list = (ArrayList) $1; Parameter [] pars = new Parameter [pars_list.Count]; pars_list.CopyTo (pars); $$ = new Parameters (pars, null); } | fixed_parameters COMMA parameter_array { ArrayList pars_list = (ArrayList) $1; Parameter [] pars = new Parameter [pars_list.Count]; pars_list.CopyTo (pars); $$ = new Parameters (pars, (Parameter) $3); } | parameter_array { $$ = new Parameters (null, (Parameter) $1); } ; fixed_parameters : fixed_parameter { ArrayList pars = new ArrayList (); pars.Add ($1); $$ = pars; } | fixed_parameters COMMA fixed_parameter { ArrayList pars = (ArrayList) $1; pars.Add ($3); $$ = $1; } ; fixed_parameter : opt_attributes opt_parameter_modifier type IDENTIFIER { $$ = new Parameter ((string) $3, (string) $4, (Parameter.Modifier) $2, (Attributes) $1); } ; opt_parameter_modifier : /* empty */ { $$ = Parameter.Modifier.NONE; } | parameter_modifier ; parameter_modifier : REF { $$ = Parameter.Modifier.REF; } | OUT { $$ = Parameter.Modifier.OUT; } ; parameter_array : opt_attributes PARAMS type IDENTIFIER { $$ = new Parameter ((string) $3, (string) $4, Parameter.Modifier.PARAMS, (Attributes) $1); note ("type must be a single-dimension array type"); } ; member_name : IDENTIFIER { $$ = $1.ToString (); } | interface_type DOT IDENTIFIER { $$ = $1.ToString () + "." + $3.ToString (); } ; property_declaration : opt_attributes opt_modifiers type member_name OPEN_BRACE { Parameter implicit_value_parameter; implicit_value_parameter = new Parameter ((string) $3, "value", Parameter.Modifier.NONE, null); lexer.properties = true; implicit_value_parameters = new Parameter [1]; implicit_value_parameters [0] = implicit_value_parameter; } accessor_declarations { lexer.properties = false; } CLOSE_BRACE { Property prop; DictionaryEntry pair = (DictionaryEntry) $7; Block get_block = null; Block set_block = null; if (pair.Key != null) get_block = (Block) pair.Key; if (pair.Value != null) set_block = (Block) pair.Value; prop = new Property ((string) $3, (string) $4, (int) $2, get_block, set_block, (Attributes) $1); CheckDef (current_container.AddProperty (prop), prop.Name); implicit_value_parameters = null; } ; accessor_declarations : get_accessor_declaration opt_set_accessor_declaration { $$ = new DictionaryEntry ($1, $2); } | set_accessor_declaration opt_get_accessor_declaration { $$ = new DictionaryEntry ($2, $1); } ; opt_get_accessor_declaration : /* empty */ { $$ = null; } | get_accessor_declaration ; opt_set_accessor_declaration : /* empty */ { $$ = null; } | set_accessor_declaration ; get_accessor_declaration : opt_attributes GET { // If this is not the case, then current_local_parameters has already // been set in indexer_declaration if (parsing_indexer == false) current_local_parameters = new Parameters (implicit_value_parameters, null); } accessor_body { $$ = $4; current_local_parameters = null; } ; set_accessor_declaration : opt_attributes SET { if (parsing_indexer == false) current_local_parameters = new Parameters (implicit_value_parameters, null); } accessor_body { $$ = $4; current_local_parameters = null; } ; accessor_body : block | SEMICOLON { $$ = new Block (null); } ; interface_declaration : opt_attributes opt_modifiers INTERFACE IDENTIFIER { Interface new_interface; string full_interface_name = MakeName ((string) $4); new_interface = new Interface (current_container, full_interface_name, (int) $2, (Attributes) $1, lexer.Location); if (current_interface != null) { Location l = lexer.Location; Report.Error (-2, l, "Internal compiler error: interface inside interface"); } current_interface = new_interface; tree.RecordInterface (full_interface_name, new_interface); } opt_interface_base interface_body { Interface new_interface = (Interface) current_interface; if ($6 != null) new_interface.Bases = (ArrayList) $6; current_interface = null; CheckDef (current_container.AddInterface (new_interface), new_interface.Name); } ; opt_interface_base : /* empty */ { $$ = null; } | interface_base ; interface_base : COLON interface_type_list { $$ = $2; } ; interface_type_list : interface_type { ArrayList interfaces = new ArrayList (); interfaces.Add ($1); $$ = interfaces; } | interface_type_list COMMA interface_type { ArrayList interfaces = (ArrayList) $1; interfaces.Add ($3); $$ = interfaces; } ; interface_body : OPEN_BRACE opt_interface_member_declarations CLOSE_BRACE ; opt_interface_member_declarations : /* empty */ | interface_member_declarations ; interface_member_declarations : interface_member_declaration | interface_member_declarations interface_member_declaration ; interface_member_declaration : interface_method_declaration { InterfaceMethod m = (InterfaceMethod) $1; CheckDef (current_interface.AddMethod (m), m.Name); } | interface_property_declaration { InterfaceProperty p = (InterfaceProperty) $1; CheckDef (current_interface.AddProperty (p), p.Name); } | interface_event_declaration { InterfaceEvent e = (InterfaceEvent) $1; CheckDef (current_interface.AddEvent (e), e.Name); } | interface_indexer_declaration { InterfaceIndexer i = (InterfaceIndexer) $1; CheckDef (current_interface.AddIndexer (i), "indexer"); } ; opt_new : /* empty */ { $$ = false; } | NEW { $$ = true; } ; interface_method_declaration : opt_attributes opt_new type IDENTIFIER OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS SEMICOLON { $$ = new InterfaceMethod ((string) $3, (string) $4, (bool) $2, (Parameters) $6, (Attributes) $1); } | opt_attributes opt_new VOID IDENTIFIER OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS SEMICOLON { $$ = new InterfaceMethod ("System.Void", (string) $4, (bool) $2, (Parameters) $6, (Attributes) $1); } ; interface_property_declaration : opt_attributes opt_new type IDENTIFIER OPEN_BRACE { lexer.properties = true; } interface_accesors { lexer.properties = false; } CLOSE_BRACE { int gs = (int) $7; $$ = new InterfaceProperty ((string) $3, (string) $4, (bool) $2, (gs & 1) == 1, (gs & 2) == 2, (Attributes) $1); } ; interface_accesors : opt_attributes GET SEMICOLON { $$ = 1; } | opt_attributes SET SEMICOLON { $$ = 2; } | opt_attributes GET SEMICOLON opt_attributes SET SEMICOLON { $$ = 3; } | opt_attributes SET SEMICOLON opt_attributes GET SEMICOLON { $$ = 3; } ; interface_event_declaration : opt_attributes opt_new EVENT type IDENTIFIER SEMICOLON { $$ = new InterfaceEvent ((string) $4, (string) $5, (bool) $2, (Attributes) $1); } ; interface_indexer_declaration : opt_attributes opt_new type THIS OPEN_BRACKET formal_parameter_list CLOSE_BRACKET OPEN_BRACE { lexer.properties = true; } interface_accesors { lexer.properties = false; } CLOSE_BRACE { int a_flags = (int) $10; bool do_get = (a_flags & 1) == 1; bool do_set = (a_flags & 2) == 2; $$ = new InterfaceIndexer ((string) $3, (Parameters) $6, do_get, do_set, (bool) $2, (Attributes) $1); } ; operator_declaration : opt_attributes opt_modifiers operator_declarator block { OperatorDeclaration decl = (OperatorDeclaration) $3; Operator op = new Operator (decl.optype, decl.ret_type, (int) $2, decl.arg1type, decl.arg1name, decl.arg2type, decl.arg2name, (Block) $4, (Attributes) $1, decl.location); // Note again, checking is done in semantic analysis current_container.AddOperator (op); } ; operator_declarator : type OPERATOR overloadable_operator OPEN_PARENS type IDENTIFIER CLOSE_PARENS { CheckUnaryOperator ((Operator.OpType) $3); $$ = new OperatorDeclaration ((Operator.OpType) $3, (string) $1, (string) $5, (string) $6, null, null, lexer.Location); } | type OPERATOR overloadable_operator OPEN_PARENS type IDENTIFIER COMMA type IDENTIFIER CLOSE_PARENS { CheckBinaryOperator ((Operator.OpType) $3); $$ = new OperatorDeclaration ((Operator.OpType) $3, (string) $1, (string) $5, (string) $6, (string) $8, (string) $9, lexer.Location); } | conversion_operator_declarator ; overloadable_operator // Unary operators: : BANG { $$ = Operator.OpType.Negate; } | TILDE { $$ = Operator.OpType.BitComplement; } | OP_INC { $$ = Operator.OpType.Increment; } | OP_DEC { $$ = Operator.OpType.Decrement; } | TRUE { $$ = Operator.OpType.True; } | FALSE { $$ = Operator.OpType.False; } // Unary and binary: | PLUS { $$ = Operator.OpType.Addition; } | MINUS { $$ = Operator.OpType.Subtraction; } // Binary: | STAR { $$ = Operator.OpType.Multiply; } | DIV { $$ = Operator.OpType.Division; } | PERCENT { $$ = Operator.OpType.Modulus; } | BITWISE_AND { $$ = Operator.OpType.BitwiseAnd; } | BITWISE_OR { $$ = Operator.OpType.BitwiseOr; } | CARRET { $$ = Operator.OpType.ExclusiveOr; } | OP_SHIFT_LEFT { $$ = Operator.OpType.LeftShift; } | OP_SHIFT_RIGHT { $$ = Operator.OpType.RightShift; } | OP_EQ { $$ = Operator.OpType.Equality; } | OP_NE { $$ = Operator.OpType.Inequality; } | OP_GT { $$ = Operator.OpType.GreaterThan; } | OP_LT { $$ = Operator.OpType.LessThan; } | OP_GE { $$ = Operator.OpType.GreaterThanOrEqual; } | OP_LE { $$ = Operator.OpType.LessThanOrEqual; } ; conversion_operator_declarator : IMPLICIT OPERATOR type OPEN_PARENS type IDENTIFIER CLOSE_PARENS { $$ = new OperatorDeclaration (Operator.OpType.Implicit, (string) $3, (string) $5, (string) $6, null, null, lexer.Location); } | EXPLICIT OPERATOR type OPEN_PARENS type IDENTIFIER CLOSE_PARENS { $$ = new OperatorDeclaration (Operator.OpType.Explicit, (string) $3, (string) $5, (string) $6, null, null, lexer.Location); } ; constructor_declaration : opt_attributes opt_modifiers constructor_declarator block { Constructor c = (Constructor) $3; c.Block = (Block) $4; c.ModFlags = (int) $2; if ((c.ModFlags & Modifiers.STATIC) != 0){ if ((c.ModFlags & Modifiers.Accessibility) != 0) { Location l = lexer.Location; Report.Error (515, l, "Access modifiers are not allowed on static constructors"); } if (c.Initializer != null){ Location l = lexer.Location; Report.Error (514, l, "Static constructors can not have an explicit this or base constructor invocations"); } if (!c.Parameters.Empty){ Location l = lexer.Location; Report.Error (103, l, "Static constructors should not have parameters"); } } CheckDef (current_container.AddConstructor (c), c.Name); current_local_parameters = null; } ; constructor_declarator : IDENTIFIER OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS opt_constructor_initializer { Location l = lexer.Location; $$ = new Constructor ((string) $1, (Parameters) $3, (ConstructorInitializer) $5, l); current_local_parameters = (Parameters) $3; } ; opt_constructor_initializer : /* empty */ { $$ = null; } | constructor_initializer ; constructor_initializer : COLON BASE OPEN_PARENS opt_argument_list CLOSE_PARENS { $$ = new ConstructorBaseInitializer ((ArrayList) $4, lexer.Location); } | COLON THIS OPEN_PARENS opt_argument_list CLOSE_PARENS { $$ = new ConstructorThisInitializer ((ArrayList) $4, lexer.Location); } ; destructor_declaration : opt_attributes TILDE IDENTIFIER OPEN_PARENS CLOSE_PARENS block { Method d = new Method ("System.Void", 0, "Finalize", new Parameters (null, null), (Attributes) $1, lexer.Location); d.Block = (Block) $6; CheckDef (current_container.AddMethod (d), d.Name); } ; event_declaration : opt_attributes opt_modifiers EVENT type variable_declarators SEMICOLON { foreach (VariableDeclaration var in (ArrayList) $5) { // FIXME : Is this right ? Event e = new Event ((string) $4, var.identifier, var.expression_or_array_initializer, (int) $2, null, null, (Attributes) $1); CheckDef (current_container.AddEvent (e), e.Name); } } | opt_attributes opt_modifiers EVENT type member_name OPEN_BRACE event_accessor_declarations CLOSE_BRACE { DictionaryEntry pair = (DictionaryEntry) $7; Block add_block = null; Block rem_block = null; if (pair.Key != null) add_block = (Block) pair.Key; if (pair.Value != null) rem_block = (Block) pair.Value; Event e = new Event ((string) $4, (string) $5, null, (int) $2, add_block, rem_block, (Attributes) $1); CheckDef (current_container.AddEvent (e), e.Name); } ; event_accessor_declarations : add_accessor_declaration remove_accessor_declaration { $$ = new DictionaryEntry ($1, $2); } | remove_accessor_declaration add_accessor_declaration { $$ = new DictionaryEntry ($2, $1); } ; add_accessor_declaration : opt_attributes ADD block { $$ = $3; } ; remove_accessor_declaration : opt_attributes REMOVE block { $$ = $3; } ; indexer_declaration : opt_attributes opt_modifiers indexer_declarator OPEN_BRACE { IndexerDeclaration decl = (IndexerDeclaration) $3; lexer.properties = true; parsing_indexer = true; current_local_parameters = decl.param_list; } accessor_declarations { lexer.properties = false; parsing_indexer = false; } CLOSE_BRACE { // The signature is computed from the signature of the indexer. Look // at section 3.6 on the spec Indexer indexer; IndexerDeclaration decl = (IndexerDeclaration) $3; DictionaryEntry pair = (DictionaryEntry) $6; Block get_block = null; Block set_block = null; if (pair.Key != null) get_block = (Block) pair.Key; if (pair.Value != null) set_block = (Block) pair.Value; indexer = new Indexer (decl.type, decl.interface_type, (int) $2, decl.param_list, get_block, set_block, (Attributes) $1); // Note that there is no equivalent of CheckDef for this case // We shall handle this in semantic analysis current_container.AddIndexer (indexer); current_local_parameters = null; } ; indexer_declarator : type THIS OPEN_BRACKET formal_parameter_list CLOSE_BRACKET { $$ = new IndexerDeclaration ((string) $1, null, (Parameters) $4); } | type interface_type DOT THIS OPEN_BRACKET formal_parameter_list CLOSE_BRACKET { $$ = new IndexerDeclaration ((string) $1, (string) $2, (Parameters) $6); } ; enum_declaration : opt_attributes opt_modifiers ENUM IDENTIFIER opt_enum_base enum_body opt_semicolon { string name = (string) $4; Enum e = new Enum ((string) $5, (int) $2, name, (Attributes) $1, lexer.Location); foreach (VariableDeclaration ev in (ArrayList) $6){ CheckDef (e.AddEnumMember (ev.identifier, (Expression) ev.expression_or_array_initializer), ev.identifier); } CheckDef (current_container.AddEnum (e), name); } ; opt_enum_base : /* empty */ { $$ = "System.Int32"; } | COLON integral_type { $$ = $2; } ; enum_body : OPEN_BRACE opt_enum_member_declarations CLOSE_BRACE { $$ = $2; } ; opt_enum_member_declarations : /* empty */ { $$ = new ArrayList (); } | enum_member_declarations opt_comma { $$ = $1; } ; enum_member_declarations : enum_member_declaration { ArrayList l = new ArrayList (); l.Add ($1); $$ = l; } | enum_member_declarations COMMA enum_member_declaration { ArrayList l = (ArrayList) $1; l.Add ($3); $$ = l; } ; enum_member_declaration : opt_attributes IDENTIFIER { $$ = new VariableDeclaration ((string) $2, null, lexer.Location); } | opt_attributes IDENTIFIER ASSIGN expression { $$ = new VariableDeclaration ((string) $2, $4, lexer.Location); } ; delegate_declaration : opt_attributes opt_modifiers DELEGATE type IDENTIFIER OPEN_PARENS formal_parameter_list CLOSE_PARENS SEMICOLON { Delegate del = new Delegate ((string) $4, (int) $2, (string) $5, (Parameters) $7, (Attributes) $1, lexer.Location); CheckDef (current_container.AddDelegate (del), del.Name); } | opt_attributes opt_modifiers DELEGATE VOID IDENTIFIER OPEN_PARENS formal_parameter_list CLOSE_PARENS SEMICOLON { Delegate del = new Delegate (null, (int) $2, (string) $5, (Parameters) $7, (Attributes) $1, lexer.Location); CheckDef (current_container.AddDelegate (del), del.Name); } ; type_name : namespace_or_type_name ; namespace_or_type_name : qualified_identifier ; /* * 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 * and another identical one that uses VOID as the return type). This * gets rid of a shift/reduce couple */ type : type_name { /* class_type */ /* This does interfaces, delegates, struct_types, class_types, parent classes, and more! 4.2 */ $$ = $1; } | builtin_types | array_type ; type_list : type { ArrayList types = new ArrayList (); types.Add ($1); $$ = types; } | type_list COMMA type { ArrayList types = (ArrayList) $1; types.Add ($3); $$ = types; } ; /* * replaces all the productions for isolating the various * simple types, but we need this to reuse it easily in local_variable_type */ builtin_types : OBJECT { $$ = "System.Object"; } | STRING { $$ = "System.String"; } | BOOL { $$ = "System.Boolean"; } | DECIMAL { $$ = "System.Decimal"; } | FLOAT { $$ = "System.Single"; } | DOUBLE { $$ = "System.Double"; } | integral_type ; integral_type : SBYTE { $$ = "System.SByte"; } | BYTE { $$ = "System.Byte"; } | SHORT { $$ = "System.Int16"; } | USHORT { $$ = "System.UInt16"; } | INT { $$ = "System.Int32"; } | UINT { $$ = "System.UInt32"; } | LONG { $$ = "System.Int64"; } | ULONG { $$ = "System.UInt64"; } | CHAR { $$ = "System.Char"; } ; interface_type : type_name ; array_type : type rank_specifiers { $$ = (string) $1 + (string) $2; } ; // // Expressions, section 7.5 // primary_expression : literal { // 7.5.1: Literals } | qualified_identifier { string name = (string) $1; $$ = null; $$ = QualifiedIdentifier (name, lexer.Location); } | parenthesized_expression | member_access | invocation_expression | element_access | this_access | base_access | post_increment_expression | post_decrement_expression | new_expression | typeof_expression | sizeof_expression | checked_expression | unchecked_expression ; literal : boolean_literal | integer_literal | real_literal | LITERAL_CHARACTER { $$ = new CharLiteral ((char) lexer.Value); } | LITERAL_STRING { $$ = new StringLiteral ((string) lexer.Value); } | NULL { $$ = new NullLiteral (); } ; real_literal : LITERAL_FLOAT { $$ = new FloatLiteral ((float) lexer.Value); } | LITERAL_DOUBLE { $$ = new DoubleLiteral ((double) lexer.Value); } | LITERAL_DECIMAL { $$ = new DecimalLiteral ((decimal) lexer.Value); } ; integer_literal : LITERAL_INTEGER { object v = lexer.Value; // // FIXME: Possible optimization would be to // compute the *Literal objects directly in the scanner // if (v is int) $$ = new IntLiteral ((Int32) v); else if (v is uint) $$ = new UIntLiteral ((UInt32) v); else if (v is long) $$ = new LongLiteral ((Int64) v); else if (v is ulong) $$ = new ULongLiteral ((UInt64) v); else Console.WriteLine ("OOPS. Unexpected result from scanner"); } ; boolean_literal : TRUE { $$ = new BoolLiteral (true); } | FALSE { $$ = new BoolLiteral (false); } ; parenthesized_expression : OPEN_PARENS expression CLOSE_PARENS { $$ = $2; } ; member_access : primary_expression DOT IDENTIFIER { $$ = new MemberAccess ((Expression) $1, (string) $3); } | predefined_type DOT IDENTIFIER { $$ = new SimpleName ((string) $1 + "." + (string) $3, lexer.Location); } ; predefined_type : builtin_types ; invocation_expression : primary_expression OPEN_PARENS opt_argument_list CLOSE_PARENS { // FIXME: // if $1 is MethodGroup // $$ = new Call ($1, $3); // else // $$ = new DelegateCall ($1, $3); if ($1 == null) { Location l = lexer.Location; Report.Error (1, l, "THIS IS CRAZY"); } $$ = new Invocation ((Expression) $1, (ArrayList) $3, lexer.Location); } ; opt_argument_list : /* empty */ { $$ = null; } | argument_list ; argument_list : argument { ArrayList list = new ArrayList (); list.Add ($1); $$ = list; } | argument_list COMMA argument { ArrayList list = (ArrayList) $1; list.Add ($3); $$ = list; } ; argument : expression { $$ = new Argument ((Expression) $1, Argument.AType.Expression); } | REF variable_reference { $$ = new Argument ((Expression) $2, Argument.AType.Ref); } | OUT variable_reference { $$ = new Argument ((Expression) $2, Argument.AType.Out); } ; variable_reference : expression { note ("section 5.4"); $$ = $1; } ; element_access : primary_expression OPEN_BRACKET expression_list CLOSE_BRACKET { $$ = new ElementAccess ((Expression) $1, (ArrayList) $3); } ; expression_list : expression { ArrayList list = new ArrayList (); list.Add ($1); $$ = list; } | expression_list COMMA expression { ArrayList list = (ArrayList) $1; list.Add ($3); $$ = list; } ; this_access : THIS { $$ = new This (); } ; base_access : BASE DOT IDENTIFIER { $$ = new BaseAccess (BaseAccess.BaseAccessType.Member, (string) $3, null); } | BASE OPEN_BRACKET expression_list CLOSE_BRACKET { $$ = new BaseAccess (BaseAccess.BaseAccessType.Indexer, null, (ArrayList) $3); } ; post_increment_expression : primary_expression OP_INC { $$ = new Unary (Unary.Operator.PostIncrement, (Expression) $1, lexer.Location); } ; post_decrement_expression : primary_expression OP_DEC { $$ = new Unary (Unary.Operator.PostDecrement, (Expression) $1, lexer.Location); } ; new_expression : object_or_delegate_creation_expression | array_creation_expression ; object_or_delegate_creation_expression : NEW type OPEN_PARENS opt_argument_list CLOSE_PARENS { $$ = new New ((string) $2, (ArrayList) $4, lexer.Location); } ; array_creation_expression : NEW type OPEN_BRACKET expression_list CLOSE_BRACKET opt_rank_specifier opt_array_initializer { $$ = new New ((string) $2, (ArrayList) $4, (string) $6, (ArrayList) $7, lexer.Location); } ; opt_rank_specifier : /* empty */ { $$ = ""; } | rank_specifiers { $$ = $1; } ; rank_specifiers : rank_specifier { $$ = $1; } | rank_specifier rank_specifiers { $$ = (string) $1 + (string) $2; } ; rank_specifier : OPEN_BRACKET opt_dim_separators CLOSE_BRACKET { $$ = "[" + (string) $2 + "]"; } ; opt_dim_separators : /* empty */ { $$ = ""; } | dim_separators { $$ = $1; } ; dim_separators : COMMA { $$ = ","; } | dim_separators COMMA { $$ =(string) $1 + ","; } ; opt_array_initializer : /* empty */ { $$ = null; } | array_initializer { $$ = $1; } ; array_initializer : OPEN_BRACE CLOSE_BRACE { ArrayList list = new ArrayList (); $$ = list; } | OPEN_BRACE variable_initializer_list opt_comma CLOSE_BRACE { $$ = (ArrayList) $2; } ; variable_initializer_list : variable_initializer { ArrayList list = new ArrayList (); list.Add ($1); $$ = list; } | variable_initializer_list COMMA variable_initializer { ArrayList list = (ArrayList) $1; list.Add ($3); $$ = list; } ; typeof_expression : TYPEOF OPEN_PARENS type CLOSE_PARENS { $$ = new TypeOf ((string) $3); } ; sizeof_expression : SIZEOF OPEN_PARENS type CLOSE_PARENS { $$ = new SizeOf ((string) $3); note ("Verify type is unmanaged"); note ("if (5.8) builtin, yield constant expression"); } ; checked_expression : CHECKED OPEN_PARENS expression CLOSE_PARENS { $$ = new CheckedExpr ((Expression) $3); } ; unchecked_expression : UNCHECKED OPEN_PARENS expression CLOSE_PARENS { $$ = new UnCheckedExpr ((Expression) $3); } ; unary_expression : primary_expression | PLUS unary_expression { $$ = new Unary (Unary.Operator.Addition, (Expression) $2, lexer.Location); } | MINUS unary_expression { $$ = new Unary (Unary.Operator.Subtraction, (Expression) $2, lexer.Location); } | BANG unary_expression { $$ = new Unary (Unary.Operator.Negate, (Expression) $2, lexer.Location); } | TILDE unary_expression { $$ = new Unary (Unary.Operator.BitComplement, (Expression) $2, lexer.Location); } | STAR unary_expression { $$ = new Unary (Unary.Operator.Indirection, (Expression) $2, lexer.Location); } | BITWISE_AND unary_expression { $$ = new Unary (Unary.Operator.AddressOf, (Expression) $2, lexer.Location); } | OP_INC unary_expression { $$ = new Unary (Unary.Operator.PreIncrement, (Expression) $2, lexer.Location); } | OP_DEC unary_expression { $$ = new Unary (Unary.Operator.PreDecrement, (Expression) $2, lexer.Location); } | cast_expression /* we can not do cast expressions at this level, as there is an ambiguity. Check "Cast Expressions" 7.6.8 for the recipe to handle this. */ ; pre_increment_expression : OP_INC unary_expression { $$ = new Unary (Unary.Operator.PreIncrement, (Expression) $2, lexer.Location); } ; pre_decrement_expression : OP_DEC unary_expression { $$ = new Unary (Unary.Operator.PreDecrement, (Expression) $2, lexer.Location); } ; cast_expression /* * FIXME: This is actually wrong, it should be `type' but that * introduces a lot of {shift,reduce}/reduces * * This is really really wrong. We need to track down * the source of problems with QIs because expressions like: * foreach (string s in (string []) object) wont be parsed. */ : OPEN_PARENS qualified_identifier CLOSE_PARENS unary_expression { $$ = new Cast ((string) $2, (Expression) $4); } | OPEN_PARENS builtin_types CLOSE_PARENS unary_expression { $$ = new Cast ((string) $2, (Expression) $4); } ; multiplicative_expression : unary_expression | multiplicative_expression STAR unary_expression { $$ = new Binary (Binary.Operator.Multiply, (Expression) $1, (Expression) $3, lexer.Location); } | multiplicative_expression DIV unary_expression { $$ = new Binary (Binary.Operator.Division, (Expression) $1, (Expression) $3, lexer.Location); } | multiplicative_expression PERCENT unary_expression { $$ = new Binary (Binary.Operator.Modulus, (Expression) $1, (Expression) $3, lexer.Location); } ; additive_expression : multiplicative_expression | additive_expression PLUS multiplicative_expression { $$ = new Binary (Binary.Operator.Addition, (Expression) $1, (Expression) $3, lexer.Location); } | additive_expression MINUS multiplicative_expression { $$ = new Binary (Binary.Operator.Subtraction, (Expression) $1, (Expression) $3, lexer.Location); } ; shift_expression : additive_expression | shift_expression OP_SHIFT_LEFT additive_expression { $$ = new Binary (Binary.Operator.LeftShift, (Expression) $1, (Expression) $3, lexer.Location); } | shift_expression OP_SHIFT_RIGHT additive_expression { $$ = new Binary (Binary.Operator.RightShift, (Expression) $1, (Expression) $3, lexer.Location); } ; relational_expression : shift_expression | relational_expression OP_LT shift_expression { $$ = new Binary (Binary.Operator.LessThan, (Expression) $1, (Expression) $3, lexer.Location); } | relational_expression OP_GT shift_expression { $$ = new Binary (Binary.Operator.GreaterThan, (Expression) $1, (Expression) $3, lexer.Location); } | relational_expression OP_LE shift_expression { $$ = new Binary (Binary.Operator.LessThanOrEqual, (Expression) $1, (Expression) $3, lexer.Location); } | relational_expression OP_GE shift_expression { $$ = new Binary (Binary.Operator.GreaterThanOrEqual, (Expression) $1, (Expression) $3, lexer.Location); } | relational_expression IS type { $$ = new Probe (Probe.Operator.Is, (Expression) $1, (string) $3); } | relational_expression AS type { $$ = new Probe (Probe.Operator.As, (Expression) $1, (string) $3); } ; equality_expression : relational_expression | equality_expression OP_EQ relational_expression { $$ = new Binary (Binary.Operator.Equality, (Expression) $1, (Expression) $3, lexer.Location); } | equality_expression OP_NE relational_expression { $$ = new Binary (Binary.Operator.Inequality, (Expression) $1, (Expression) $3, lexer.Location); } ; and_expression : equality_expression | and_expression BITWISE_AND equality_expression { $$ = new Binary (Binary.Operator.BitwiseAnd, (Expression) $1, (Expression) $3, lexer.Location); } ; exclusive_or_expression : and_expression | exclusive_or_expression CARRET and_expression { $$ = new Binary (Binary.Operator.ExclusiveOr, (Expression) $1, (Expression) $3, lexer.Location); } ; inclusive_or_expression : exclusive_or_expression | inclusive_or_expression BITWISE_OR exclusive_or_expression { $$ = new Binary (Binary.Operator.BitwiseOr, (Expression) $1, (Expression) $3, lexer.Location); } ; conditional_and_expression : inclusive_or_expression | conditional_and_expression OP_AND inclusive_or_expression { $$ = new Binary (Binary.Operator.LogicalAnd, (Expression) $1, (Expression) $3, lexer.Location); } ; conditional_or_expression : conditional_and_expression | conditional_or_expression OP_OR conditional_and_expression { $$ = new Binary (Binary.Operator.LogicalOr, (Expression) $1, (Expression) $3, lexer.Location); } ; conditional_expression : conditional_or_expression | conditional_or_expression INTERR expression COLON expression { $$ = new Conditional ((Expression) $1, (Expression) $3, (Expression) $5, lexer.Location); } ; assignment_expression : unary_expression ASSIGN expression { $$ = new Assign ((Expression) $1, (Expression) $3, lexer.Location); } | unary_expression OP_MULT_ASSIGN expression { Location l = lexer.Location; $$ = new Assign ((Expression) $1, new Binary (Binary.Operator.Multiply, (Expression) $1, (Expression) $3, l), l); } | unary_expression OP_DIV_ASSIGN expression { Location l = lexer.Location; $$ = new Assign ((Expression) $1, new Binary (Binary.Operator.Division, (Expression) $1, (Expression) $3, l), l); } | unary_expression OP_MOD_ASSIGN expression { Location l = lexer.Location; $$ = new Assign ((Expression) $1, new Binary (Binary.Operator.Modulus, (Expression) $1, (Expression) $3, l), l); } | unary_expression OP_ADD_ASSIGN expression { Location l = lexer.Location; $$ = new Assign ((Expression) $1, new Binary (Binary.Operator.Addition, (Expression) $1, (Expression) $3, l), l); } | unary_expression OP_SUB_ASSIGN expression { Location l = lexer.Location; $$ = new Assign ((Expression) $1, new Binary (Binary.Operator.Subtraction, (Expression) $1, (Expression) $3, l), l); } | unary_expression OP_SHIFT_LEFT_ASSIGN expression { Location l = lexer.Location; $$ = new Assign ((Expression) $1, new Binary (Binary.Operator.LeftShift, (Expression) $1, (Expression) $3, l), l); } | unary_expression OP_SHIFT_RIGHT_ASSIGN expression { Location l = lexer.Location; $$ = new Assign ((Expression) $1, new Binary (Binary.Operator.RightShift, (Expression) $1, (Expression) $3, l), l); } | unary_expression OP_AND_ASSIGN expression { Location l = lexer.Location; $$ = new Assign ((Expression) $1, new Binary (Binary.Operator.BitwiseAnd, (Expression) $1, (Expression) $3, l), l); } | unary_expression OP_OR_ASSIGN expression { Location l = lexer.Location; $$ = new Assign ((Expression) $1, new Binary (Binary.Operator.BitwiseOr, (Expression) $1, (Expression) $3, l), l); } | unary_expression OP_XOR_ASSIGN expression { Location l = lexer.Location; $$ = new Assign ((Expression) $1, new Binary (Binary.Operator.ExclusiveOr, (Expression) $1, (Expression) $3, l), l); } ; expression : conditional_expression | assignment_expression ; constant_expression : expression ; boolean_expression : expression { CheckBoolean ((Expression) $1); $$ = $1; } ; // // 10 classes // class_declaration : opt_attributes opt_modifiers CLASS IDENTIFIER { Class new_class; string full_class_name = MakeName ((string) $4); new_class = new Class (rc, current_container, full_class_name, (int) $2, (Attributes) $1, lexer.Location); current_container = new_class; current_container.Namespace = current_namespace; tree.RecordClass (full_class_name, new_class); } opt_class_base class_body opt_semicolon { Class new_class = (Class) current_container; if ($6 != null) new_class.Bases = (ArrayList) $6; current_container = current_container.Parent; CheckDef (current_container.AddClass (new_class), new_class.Name); $$ = new_class; } ; opt_modifiers : /* empty */ { $$ = (int) 0; } | modifiers ; modifiers : modifier | modifiers modifier { int m1 = (int) $1; int m2 = (int) $2; if ((m1 & m2) != 0) { Location l = lexer.Location; Report.Error (1002, l, "Duplicate modifier: `" + Modifiers.Name (m2) + "'"); } $$ = (int) (m1 | m2); } ; modifier : NEW { $$ = Modifiers.NEW; } | PUBLIC { $$ = Modifiers.PUBLIC; } | PROTECTED { $$ = Modifiers.PROTECTED; } | INTERNAL { $$ = Modifiers.INTERNAL; } | PRIVATE { $$ = Modifiers.PRIVATE; } | ABSTRACT { $$ = Modifiers.ABSTRACT; } | SEALED { $$ = Modifiers.SEALED; } | STATIC { $$ = Modifiers.STATIC; } | READONLY { $$ = Modifiers.READONLY; } | VIRTUAL { $$ = Modifiers.VIRTUAL; } | OVERRIDE { $$ = Modifiers.OVERRIDE; } | EXTERN { $$ = Modifiers.EXTERN; } ; opt_class_base : /* empty */ { $$ = null; } | class_base { $$ = $1; } ; class_base : COLON type_list { $$ = $2; } ; // // Statements (8.2) // // // A block is "contained" on the following places: // method_body // property_declaration as part of the accessor body (get/set) // operator_declaration // constructor_declaration // destructor_declaration // event_declaration as part of add_accessor_declaration or remove_accessor_declaration // block : OPEN_BRACE { current_block = new Block (current_block); } opt_statement_list CLOSE_BRACE { while (current_block.Implicit) current_block = current_block.Parent; $$ = current_block; current_block = current_block.Parent; } ; opt_statement_list : /* empty */ | statement_list ; statement_list : statement | statement_list statement ; statement : declaration_statement { if ((Block) $1 != current_block){ current_block.AddStatement ((Statement) $1); current_block = (Block) $1; } } | embedded_statement { current_block.AddStatement ((Statement) $1); } | labeled_statement { current_block.AddStatement ((Statement) $1); } ; embedded_statement : block | empty_statement | expression_statement | selection_statement | iteration_statement | jump_statement | try_statement | checked_statement | unchecked_statement | lock_statement | using_statement ; empty_statement : SEMICOLON { $$ = new EmptyStatement (); } ; labeled_statement : IDENTIFIER COLON statement { string lab = (String) $1; Block block; block = new Block (current_block, lab); block.AddStatement ((Statement) $3); $$ = block; if (!current_block.AddLabel (lab, block)){ Location l = lexer.Location; Report.Error (140, l, "The label '" + lab + "' is a duplicate"); $$ = $3; } } ; declaration_statement : local_variable_declaration SEMICOLON // done | local_constant_declaration SEMICOLON // finishme ; /* * The following is from Rhys' grammar: * > Types in local variable declarations must be recognized as * > expressions to prevent reduce/reduce errors in the grammar. * > The expressions are converted into types during semantic analysis. */ local_variable_type : primary_expression type_suffixes { // FIXME: Do something smart here regarding the composition of the type. // Ok, the above "primary_expression" is there to get rid of // both reduce/reduce and shift/reduces in the grammar, it should // really just be "type_name". If you use type_name, a reduce/reduce // creeps up. If you use qualified_identifier (which is all we need // really) two shift/reduces appear. // // So, instead we do a super trick: we just allow ($1) to be a // SimpleName Expression. // if (((Expression) $1) is SimpleName) $$ = ((SimpleName) $1).Name; else { Location l = lexer.Location; Report.Error (-1, l, "Invalid Type definition"); $$ = "System.Object"; } } | builtin_types type_suffixes { $$ = (string) $1 + (string) $2; } ; // FIXME : How can the type of a local variable be void ? I don't quite see ;-) // | VOID // { // // FIXME: this is a string that represents the type // // Figure out something to make this work. // $$ = "void"; // } // ; type_suffixes : /* empty */ { $$ = ""; } | type_suffix_list ; type_suffix_list : type_suffix | type_suffix_list type_suffix { $$ = (string) $1 + (string) $2; } ; type_suffix : OPEN_BRACKET opt_dim_separators CLOSE_BRACKET { $$ = "[" + (string) $2 + "]"; } ; local_variable_declaration : local_variable_type variable_declarators { $$ = declare_local_variables ((string) $1, (ArrayList) $2); } ; local_constant_declaration : CONST type constant_declarator // finishme ; expression_statement : statement_expression SEMICOLON { $$ = $1; } ; // // We have to do the wrapping here and not in the case above, // because statement_expression is used for example in for_statement // statement_expression : invocation_expression { $$ = new StatementExpression ((ExpressionStatement) $1); } | object_creation_expression { $$ = new StatementExpression ((ExpressionStatement) $1); } | assignment_expression { $$ = new StatementExpression ((ExpressionStatement) $1); } | post_increment_expression { $$ = new StatementExpression ((ExpressionStatement) $1); } | post_decrement_expression { $$ = new StatementExpression ((ExpressionStatement) $1); } | pre_increment_expression { $$ = new StatementExpression ((ExpressionStatement) $1); } | pre_decrement_expression { $$ = new StatementExpression ((ExpressionStatement) $1); } ; object_creation_expression : object_or_delegate_creation_expression { note ("complain if this is a delegate maybe?"); } ; selection_statement : if_statement | switch_statement ; if_statement : IF OPEN_PARENS boolean_expression CLOSE_PARENS embedded_statement { $$ = new If ((Expression) $3, (Statement) $5); } | IF OPEN_PARENS boolean_expression CLOSE_PARENS embedded_statement ELSE embedded_statement { $$ = new If ((Expression) $3, (Statement) $5, (Statement) $7); } ; switch_statement : SWITCH OPEN_PARENS expression CLOSE_PARENS switch_block { $$ = new Switch ((Expression) $3, (ArrayList) $5); } ; switch_block : OPEN_BRACE opt_switch_sections CLOSE_BRACE { $$ = $2; } ; opt_switch_sections : /* empty */ { $$ = new ArrayList (); } | switch_sections ; switch_sections : switch_section { ArrayList sections = new ArrayList (); sections.Add ($1); $$ = sections; } | switch_sections switch_section { ArrayList sections = (ArrayList) $1; sections.Add ($2); $$ = sections; } ; switch_section : switch_labels { current_block = new Block (current_block); } statement_list { while (current_block.Implicit) current_block = current_block.Parent; $$ = new SwitchSection ((ArrayList) $1, current_block); current_block = current_block.Parent; } ; switch_labels : switch_label { ArrayList labels = new ArrayList (); labels.Add ($1); $$ = labels; } | switch_labels switch_label { ArrayList labels = (ArrayList) ($1); labels.Add ($2); $$ = labels; } ; switch_label : CASE constant_expression COLON { $$ = new SwitchLabel ((Expression) $2); } | DEFAULT COLON { $$ = new SwitchLabel (null); } ; iteration_statement : while_statement | do_statement | for_statement | foreach_statement ; while_statement : WHILE OPEN_PARENS boolean_expression CLOSE_PARENS embedded_statement { $$ = new While ((Expression) $3, (Statement) $5); } ; do_statement : DO embedded_statement WHILE OPEN_PARENS boolean_expression CLOSE_PARENS SEMICOLON { $$ = new Do ((Statement) $2, (Expression) $5); } ; for_statement : FOR OPEN_PARENS opt_for_initializer SEMICOLON opt_for_condition SEMICOLON opt_for_iterator CLOSE_PARENS embedded_statement { $$ = new For ((Statement) $3, (Expression) $5, (Statement) $7, (Statement) $9); } ; opt_for_initializer : /* empty */ { $$ = new EmptyStatement (); } | for_initializer ; for_initializer : local_variable_declaration | statement_expression_list ; opt_for_condition : /* empty */ { $$ = new BoolLiteral (true); } | boolean_expression ; opt_for_iterator : /* empty */ { $$ = new EmptyStatement (); } | for_iterator ; for_iterator : statement_expression_list ; statement_expression_list : statement_expression { Block b = new Block (null, true); b.AddStatement ((Statement) $1); $$ = b; } | statement_expression_list COMMA statement_expression { Block b = (Block) $1; b.AddStatement ((Statement) $3); $$ = $1; } ; foreach_statement : FOREACH OPEN_PARENS type IDENTIFIER IN expression CLOSE_PARENS embedded_statement { string temp_id = current_block.MakeInternalID (); ExpressionStatement assign_e; Expression ma; Statement getcurrent; Block foreach_block, child_block; Location l = lexer.Location; foreach_block = new Block (current_block, true); foreach_block.AddVariable ("System.IEnumerator", temp_id, l); foreach_block.AddVariable ((string) $3, (string) $4, l); assign_e = new Assign (new LocalVariableReference (foreach_block, temp_id), new Invocation ( new MemberAccess ((Expression) $6, "GetEnumerator"), null, lexer.Location), lexer.Location); current_block.AddStatement (new StatementExpression (assign_e)); ma = new MemberAccess (new LocalVariableReference (foreach_block, temp_id), "MoveNext"); child_block = new Block (current_block); getcurrent = new StatementExpression ( new Assign ( new LocalVariableReference (foreach_block, (string) $4), new Cast ( (string) $3, new MemberAccess ( new LocalVariableReference (foreach_block, temp_id), "Current")), lexer.Location)); child_block.AddStatement (getcurrent); child_block.AddStatement ((Statement) $8); foreach_block.AddStatement (new While (ma, (Statement) child_block)); $$ = foreach_block; } ; jump_statement : break_statement | continue_statement | goto_statement | return_statement | throw_statement ; break_statement : BREAK SEMICOLON { $$ = new Break (); } ; continue_statement : CONTINUE SEMICOLON { $$ = new Continue (); } ; goto_statement : GOTO IDENTIFIER SEMICOLON { $$ = new Goto ((string) $2); } | GOTO CASE constant_expression SEMICOLON | GOTO DEFAULT SEMICOLON ; return_statement : RETURN opt_expression SEMICOLON { $$ = new Return ((Expression) $2); } ; throw_statement : THROW opt_expression SEMICOLON { $$ = new Throw ((Expression) $2); } ; opt_expression : /* empty */ | expression ; try_statement : TRY block catch_clauses { Catch g = null; ArrayList s = new ArrayList (); foreach (Catch cc in (ArrayList) $3) { if (cc.Type == null) g = cc; else s.Add (cc); } // Now s contains the list of specific catch clauses // and g contains the general one. $$ = new Try ((Block) $2, s, g, null); } | TRY block opt_catch_clauses FINALLY block { Catch g = null; ArrayList s = new ArrayList (); foreach (Catch cc in (ArrayList) $3) { if (cc.Type == null) g = cc; else s.Add (cc); } $$ = new Try ((Block) $2, s, g, (Block) $5); } ; opt_catch_clauses : /* empty */ { $$ = null; } | catch_clauses ; catch_clauses : catch_clause { ArrayList l = new ArrayList (); l.Add ($1); $$ = l; } | catch_clauses catch_clause { ArrayList l = (ArrayList) $1; l.Add ($2); $$ = l; } ; opt_identifier : /* empty */ { $$ = null; } | IDENTIFIER ; catch_clause : CATCH opt_catch_args block { string type = null; string id = null; if ($2 != null) { DictionaryEntry cc = (DictionaryEntry) $2; type = (string) cc.Key; id = (string) cc.Value; } $$ = new Catch (type, id, (Block) $3); } ; opt_catch_args : /* empty */ { $$ = null; } | catch_args ; catch_args : OPEN_PARENS type opt_identifier CLOSE_PARENS { $$ = new DictionaryEntry ($2, $3); } ; checked_statement : CHECKED block { $$ = new Checked ((Block) $2); } ; unchecked_statement : UNCHECKED block { $$ = new Unchecked ((Block) $2); } ; lock_statement : LOCK OPEN_PARENS expression CLOSE_PARENS embedded_statement { $$ = new Lock ((Expression) $3, (Statement) $5); } ; using_statement : USING OPEN_PARENS resource_acquisition CLOSE_PARENS embedded_statement // finishme ; resource_acquisition : local_variable_declaration expression // finishme ; %% // // A class used to pass around variable declarations and constants // public class VariableDeclaration { public string identifier; public object expression_or_array_initializer; public Location Location; public VariableDeclaration (string id, object eoai, Location l){ this.identifier = id; this.expression_or_array_initializer = eoai; this.Location = l; } } // // A class used to hold info about an indexer declarator // public class IndexerDeclaration { public string type; public string interface_type; public Parameters param_list; public IndexerDeclaration (string type, string interface_type, Parameters param_list) { this.type = type; this.interface_type = interface_type; this.param_list = param_list; } } // // A class used to hold info about an operator declarator // public class OperatorDeclaration { public Operator.OpType optype; public string ret_type; public string arg1type; public string arg1name; public string arg2type; public string arg2name; public Location location; public OperatorDeclaration (Operator.OpType op, string ret_type, string arg1type, string arg1name, string arg2type, string arg2name, Location location) { optype = op; this.ret_type = ret_type; this.arg1type = arg1type; this.arg1name = arg1name; this.arg2type = arg2type; this.arg2name = arg2name; this.location = location; } } // // Given the @class_name name, it creates a fully qualified name // based on the containing declaration space // string MakeName (string class_name) { string ns = current_namespace.Name; string container_name = current_container.Name; if (container_name == ""){ if (ns != "") return ns + "." + class_name; else return class_name; } else return container_name + "." + class_name; } // // Used to report back to the user the result of a declaration // in the current declaration space // void CheckDef (DeclSpace.AdditionResult result, string name) { if (result == DeclSpace.AdditionResult.Success) return; Location l = lexer.Location; switch (result){ case DeclSpace.AdditionResult.NameExists: Report.Error (102, l, "The namespace `" + current_container.Name + "' already contains a definition for `"+ name + "'"); break; // NEED TO HANDLE THIS IN SEMANTIC ANALYSIS: // // case DeclSpace.AdditionResult.MethodDuplicated: // error (111, "Class `"+current_container.Name+ // "' already defines a member called '" + // name + "' with the same parameter types"); // break; case DeclSpace.AdditionResult.EnclosingClash: Report.Error (542, l, "Member names cannot be the same as their enclosing type"); break; case DeclSpace.AdditionResult.NotAConstructor: Report.Error (1520, l, "Class, struct, or interface method must have a return type"); break; } } void CheckDef (bool result, string name) { if (result) return; CheckDef (DeclSpace.AdditionResult.NameExists, name); } object SimpleLookup (string name) { // // we need to check against current_block not being null // as `expression' is allowed in argument_lists, which // do not exist inside a block. // if (current_block != null){ if (current_block.IsVariableDefined (name)) return new LocalVariableReference (current_block, name); } if (current_local_parameters != null){ int idx; Parameter par = current_local_parameters.GetParameterByName (name, out idx); if (par != null) return new ParameterReference (current_local_parameters, idx, name); } return null; } // // Assumes that the name contains a `.' and tries to perform a simple lookup // and shape the result to be a MemberAccess on a Local/Parameter // object CompositeLookup (string name) { int pos = name.IndexOf ("."); string left = name.Substring (0, pos); object o; o = SimpleLookup (left); if (o != null){ string right = name.Substring (pos + 1); return new MemberAccess ((Expression) o, right); } return null; } object QualifiedIdentifier (string name, Location l) { object o; if (name.IndexOf ('.') == -1) o = SimpleLookup (name); else o = CompositeLookup (name); if (o == null) o = new SimpleName (name, l); return o; } Block declare_local_variables (string type, ArrayList variable_declarators) { Block implicit_block; ArrayList inits = null; // // We use the `Used' property to check whether statements // have been added to the current block. If so, we need // to create another block to contain the new declaration // otherwise, as an optimization, we use the same block to // add the declaration. // // FIXME: A further optimization is to check if the statements // that were added were added as part of the initialization // below. In which case, no other statements have been executed // and we might be able to reduce the number of blocks for // situations like this: // // int j = 1; int k = j + 1; // if (current_block.Used) implicit_block = new Block (current_block, true); else implicit_block = new Block (current_block, true); foreach (VariableDeclaration decl in variable_declarators){ if (implicit_block.AddVariable (type, decl.identifier, decl.Location)){ if (decl.expression_or_array_initializer != null){ if (inits == null) inits = new ArrayList (); inits.Add (decl); } } else { Location l = lexer.Location; Report.Error (128, l, "A local variable `" + decl.identifier + "' is already defined in this scope"); } } if (inits == null) return implicit_block; foreach (VariableDeclaration decl in inits){ if (decl.expression_or_array_initializer is Expression){ Expression expr = (Expression) decl.expression_or_array_initializer; Assign assign; assign = new Assign (new LocalVariableReference ( implicit_block, decl.identifier), expr, lexer.Location); implicit_block.AddStatement (new StatementExpression (assign)); } else { Console.WriteLine ("Not handling Array initializers yet"); } } return implicit_block; } void CheckConstant (Expression expr) { // finishme } void CheckBoolean (Expression expr) { // finishme } void CheckAttributeTarget (string a) { switch (a) { case "assembly" : case "field" : case "method" : case "param" : case "property" : case "type" : return; default : Location l = lexer.Location; Report.Error (658, l, "Invalid attribute target"); break; } } void CheckUnaryOperator (Operator.OpType op) { switch (op) { case Operator.OpType.Negate: case Operator.OpType.BitComplement: case Operator.OpType.Increment: case Operator.OpType.Decrement: case Operator.OpType.True: case Operator.OpType.False: case Operator.OpType.Addition: case Operator.OpType.Subtraction: break; default : Location l = lexer.Location; Report.Error (1019, l, "Overloadable unary operator expected"); break; } } void CheckBinaryOperator (Operator.OpType op) { switch (op) { case Operator.OpType.Addition: case Operator.OpType.Subtraction: case Operator.OpType.Multiply: case Operator.OpType.Division: case Operator.OpType.Modulus: case Operator.OpType.BitwiseAnd: case Operator.OpType.BitwiseOr: case Operator.OpType.ExclusiveOr: case Operator.OpType.LeftShift: case Operator.OpType.RightShift: case Operator.OpType.Equality: case Operator.OpType.Inequality: case Operator.OpType.GreaterThan: case Operator.OpType.LessThan: case Operator.OpType.GreaterThanOrEqual: case Operator.OpType.LessThanOrEqual: break; default : Location l = lexer.Location; Report.Error (1020, l, "Overloadable binary operator expected"); break; } } void output (string s) { Console.WriteLine (s); } void note (string s) { // Used to put annotations } Tokenizer lexer; public Tokenizer Lexer { get { return lexer; } } public CSharpParser(RootContext rc, string name, System.IO.Stream input) { current_namespace = new Namespace (null, ""); this.rc = rc; this.tree = rc.Tree; this.name = name; this.input = input; current_container = tree.Types; current_container.Namespace = current_namespace; lexer = new Tokenizer (input, name); } public override int parse () { StringBuilder value = new StringBuilder (); global_errors = 0; try { if (yacc_verbose_flag) yyparse (lexer, new yydebug.yyDebugSimple ()); else yyparse (lexer); } catch (Exception e){ // Console.WriteLine ("Fatal error: " + name); // Console.WriteLine (lexer.location); Console.WriteLine (lexer.location + " : Parsing error"); Console.WriteLine (e); global_errors++; } return global_errors; } /* end end end */ }