2009-08-18 Marek Habersack <mhabersack@novell.com>
[mono.git] / mcs / mcs / eval.cs
index 493a67552a7c205eb4c68fb7304ba85ad66b5d31..bc36a672b3f50a107c47347cd0f4cd8a33909aab 100644 (file)
@@ -37,6 +37,18 @@ namespace Mono.CSharp {
        /// </remarks>
        public class Evaluator {
 
+               enum ParseMode {
+                       // Parse silently, do not output any error messages
+                       Silent,
+
+                       // Report errors during parse
+                       ReportErrors,
+
+                       // Auto-complete, means that the tokenizer will start producing
+                       // GETCOMPLETIONS tokens when it reaches a certain point.
+                       GetCompletions
+               }
+
                static object evaluator_lock = new object ();
                
                static string current_debug_name;
@@ -44,7 +56,7 @@ namespace Mono.CSharp {
                static Thread invoke_thread;
                
                static ArrayList using_alias_list = new ArrayList ();
-               static ArrayList using_list = new ArrayList ();
+               internal static ArrayList using_list = new ArrayList ();
                static Hashtable fields = new Hashtable ();
 
                static Type   interactive_base_class = typeof (InteractiveBase);
@@ -65,21 +77,50 @@ namespace Mono.CSharp {
                ///  interface.
                /// </remarks>
                public static void Init (string [] args)
+               {
+                       InitAndGetStartupFiles (args);
+               }
+
+
+               /// <summary>
+               ///   Optional initialization for the Evaluator.
+               /// </summary>
+               /// <remarks>
+               ///  Initializes the Evaluator with the command line
+               ///  options that would be processed by the command
+               ///  line compiler.  Only the first call to
+               ///  InitAndGetStartupFiles or Init will work, any future
+               ///  invocations are ignored.
+               ///
+               ///  You can safely avoid calling this method if your application
+               ///  does not need any of the features exposed by the command line
+               ///  interface.
+               ///
+               ///  This method return an array of strings that contains any
+               ///  files that were specified in `args'.
+               /// </remarks>
+               public static string [] InitAndGetStartupFiles (string [] args)
                {
                        lock (evaluator_lock){
                                if (inited)
-                                       return;
+                                       return new string [0];
                                
-                               RootContext.Version = LanguageVersion.Default;
                                driver = Driver.Create (args, false);
                                if (driver == null)
                                        throw new Exception ("Failed to create compiler driver with the given arguments");
                                
                                driver.ProcessDefaultConfig ();
+
+                               ArrayList startup_files = new ArrayList ();
+                               foreach (CompilationUnit file in Location.SourceFiles)
+                                       startup_files.Add (file.Path);
+                               
                                CompilerCallableEntryPoint.Reset ();
-                               Driver.LoadReferences ();
+                               driver.LoadReferences ();
                                RootContext.EvalMode = true;
                                inited = true;
+
+                               return (string []) startup_files.ToArray (typeof (string));
                        }
                }
 
@@ -99,7 +140,7 @@ namespace Mono.CSharp {
                                throw new Exception ("Failed to InitCoreTypes");
                        TypeManager.InitOptionalCoreTypes ();
                        
-                       Location.AddFile ("<interactive>");
+                       Location.AddFile ("{interactive}");
                        Location.Initialize ();
 
                        current_debug_name = "interactive" + (count++) + ".dll";
@@ -190,20 +231,25 @@ namespace Mono.CSharp {
                                        Init ();
                                
                                bool partial_input;
-                               CSharpParser parser = ParseString (true, input, out partial_input);
+                               CSharpParser parser = ParseString (ParseMode.Silent, input, out partial_input);
                                if (parser == null){
                                        compiled = null;
                                        if (partial_input)
                                                return input;
                                        
-                                       ParseString (false, input, out partial_input);
+                                       ParseString (ParseMode.ReportErrors, input, out partial_input);
                                        return null;
                                }
                                
                                object parser_result = parser.InteractiveResult;
                                
-                               if (!(parser_result is Class))
-                                       parser.CurrentNamespace.Extract (using_alias_list, using_list);
+                               if (!(parser_result is Class)){
+                                       int errors = Report.Errors;
+                                       
+                                       NamespaceEntry.VerifyAllUsing ();
+                                       if (errors == Report.Errors)
+                                               parser.CurrentNamespace.Extract (using_alias_list, using_list);
+                               }
 
                                compiled = CompileBlock (parser_result as Class, parser.undo);
                        }
@@ -312,7 +358,69 @@ namespace Mono.CSharp {
 
                        return null;
                }
+
+               public static string [] GetCompletions (string input, out string prefix)
+               {
+                       prefix = "";
+                       if (input == null || input.Length == 0)
+                               return null;
                        
+                       lock (evaluator_lock){
+                               if (!inited)
+                                       Init ();
+                               
+                               bool partial_input;
+                               CSharpParser parser = ParseString (ParseMode.GetCompletions, input, out partial_input);
+                               if (parser == null){
+                                       if (CSharpParser.yacc_verbose_flag != 0)
+                                               Console.WriteLine ("DEBUG: No completions available");
+                                       return null;
+                               }
+                               
+                               Class parser_result = parser.InteractiveResult as Class;
+                               
+                               if (parser_result == null){
+                                       if (CSharpParser.yacc_verbose_flag != 0)
+                                               Console.WriteLine ("Do not know how to cope with !Class yet");
+                                       return null;
+                               }
+
+                               try {
+                                       RootContext.ResolveTree ();
+                                       if (Report.Errors != 0)
+                                               return null;
+                                       
+                                       RootContext.PopulateTypes ();
+                                       if (Report.Errors != 0)
+                                               return null;
+
+                                       MethodOrOperator method = null;
+                                       foreach (MemberCore member in parser_result.Methods){
+                                               if (member.Name != "Host")
+                                                       continue;
+                                               
+                                               method = (MethodOrOperator) member;
+                                               break;
+                                       }
+                                       if (method == null)
+                                               throw new InternalErrorException ("did not find the the Host method");
+
+                                       EmitContext ec = method.CreateEmitContext (null);
+
+                                       try {
+                                               method.Block.Resolve (null, ec, method.ParameterInfo, method);
+                                       } catch (CompletionResult cr){
+                                               prefix = cr.BaseText;
+                                               return cr.Result;
+                                       }
+                               } finally {
+                                       parser.undo.ExecuteUndo ();
+                               }
+                               
+                       }
+                       return null;
+               }
+               
                /// <summary>
                ///   Executes the given expression or statement.
                /// </summary>
@@ -359,7 +467,7 @@ namespace Mono.CSharp {
 
                        return result;
                }
-               
+       
                enum InputKind {
                        EOF,
                        StatementOrExpression,
@@ -379,7 +487,7 @@ namespace Mono.CSharp {
                //
                static InputKind ToplevelOrStatement (SeekableStreamReader seekable)
                {
-                       Tokenizer tokenizer = new Tokenizer (seekable, Location.SourceFiles [0]);
+                       Tokenizer tokenizer = new Tokenizer (seekable, (CompilationUnit) Location.SourceFiles [0]);
                        
                        int t = tokenizer.token ();
                        switch (t){
@@ -481,7 +589,7 @@ namespace Mono.CSharp {
                // @partial_input: if @silent is true, then it returns whether the
                // parsed expression was partial, and more data is needed
                //
-               static CSharpParser ParseString (bool silent, string input, out bool partial_input)
+               static CSharpParser ParseString (ParseMode mode, string input, out bool partial_input)
                {
                        partial_input = false;
                        Reset ();
@@ -492,14 +600,14 @@ namespace Mono.CSharp {
 
                        InputKind kind = ToplevelOrStatement (seekable);
                        if (kind == InputKind.Error){
-                               if (!silent)
+                               if (mode == ParseMode.ReportErrors)
                                        Report.Error (-25, "Detection Parsing Error");
                                partial_input = false;
                                return null;
                        }
 
                        if (kind == InputKind.EOF){
-                               if (silent == false)
+                               if (mode == ParseMode.ReportErrors)
                                        Console.Error.WriteLine ("Internal error: EOF condition should have been detected in a previous call with silent=true");
                                partial_input = true;
                                return null;
@@ -507,7 +615,7 @@ namespace Mono.CSharp {
                        }
                        seekable.Position = 0;
 
-                       CSharpParser parser = new CSharpParser (seekable, Location.SourceFiles [0]);
+                       CSharpParser parser = new CSharpParser (seekable, (CompilationUnit) Location.SourceFiles [0]);
                        parser.ErrorOutput = Report.Stderr;
 
                        if (kind == InputKind.StatementOrExpression){
@@ -524,39 +632,34 @@ namespace Mono.CSharp {
                                RootContext.StatementMode = false;
                        }
 
-                       if (silent)
+                       if (mode == ParseMode.GetCompletions)
+                               parser.Lexer.CompleteOnEOF = true;
+
+                       bool disable_error_reporting;
+                       if ((mode == ParseMode.Silent || mode == ParseMode.GetCompletions) && CSharpParser.yacc_verbose_flag == 0)
+                               disable_error_reporting = true;
+                       else
+                               disable_error_reporting = false;
+                       
+                       if (disable_error_reporting)
                                Report.DisableReporting ();
                        try {
                                parser.parse ();
                        } finally {
                                if (Report.Errors != 0){
-                                       if (silent && parser.UnexpectedEOF)
+                                       if (mode != ParseMode.ReportErrors  && parser.UnexpectedEOF)
                                                partial_input = true;
 
                                        parser.undo.ExecuteUndo ();
                                        parser = null;
                                }
 
-                               if (silent)
+                               if (disable_error_reporting)
                                        Report.EnableReporting ();
                        }
                        return parser;
                }
 
-               /// <summary>
-               ///   A delegate that can be used to invoke the
-               ///   compiled expression or statement.
-               /// </summary>
-               /// <remarks>
-               ///   Since the Compile methods will compile
-               ///   statements and expressions into the same
-               ///   delegate, you can tell if a value was returned
-               ///   by checking whether the returned value is of type
-               ///   NoValueSet.   
-               /// </remarks>
-               
-               public delegate void CompiledMethod (ref object retvalue);
-
                //
                // Queue all the fields that we use, as we need to then go from FieldBuilder to FieldInfo
                // or reflection gets confused (it basically gets confused, and variables override each
@@ -630,7 +733,7 @@ namespace Mono.CSharp {
                                // If a previous value was set, nullify it, so that we do
                                // not leak memory
                                if (old != null){
-                                       if (old.FieldType.IsValueType){
+                                       if (TypeManager.IsStruct (old.FieldType)){
                                                //
                                                // TODO: Clear fields for structs
                                                //
@@ -711,6 +814,21 @@ namespace Mono.CSharp {
                        }
                }
 
+               static internal ICollection GetUsingList ()
+               {
+                       ArrayList res = new ArrayList (using_list.Count);
+                       foreach (object ue in using_list)
+                               res.Add (ue.ToString ());
+                       return res;
+               }
+               
+               static internal string [] GetVarNames ()
+               {
+                       lock (evaluator_lock){
+                               return (string []) new ArrayList (fields.Keys).ToArray (typeof (string));
+                       }
+               }
+               
                static public string GetVars ()
                {
                        lock (evaluator_lock){
@@ -747,8 +865,8 @@ namespace Mono.CSharp {
                static public void LoadAssembly (string file)
                {
                        lock (evaluator_lock){
-                               Driver.LoadAssembly (file, true);
-                               RootNamespace.ComputeNamespaces ();
+                               Driver.LoadAssembly (file, false);
+                               GlobalRootNamespace.Instance.ComputeNamespaces ();
                        }
                }
 
@@ -758,28 +876,76 @@ namespace Mono.CSharp {
                static public void ReferenceAssembly (Assembly a)
                {
                        lock (evaluator_lock){
-                               RootNamespace.Global.AddAssemblyReference (a);
-                               RootNamespace.ComputeNamespaces ();
+                               GlobalRootNamespace.Instance.AddAssemblyReference (a);
+                               GlobalRootNamespace.Instance.ComputeNamespaces ();
                        }
                }
                
        }
 
+       
+       /// <summary>
+       ///   A delegate that can be used to invoke the
+       ///   compiled expression or statement.
+       /// </summary>
+       /// <remarks>
+       ///   Since the Compile methods will compile
+       ///   statements and expressions into the same
+       ///   delegate, you can tell if a value was returned
+       ///   by checking whether the returned value is of type
+       ///   NoValueSet.   
+       /// </remarks>
+       
+       public delegate void CompiledMethod (ref object retvalue);
+
        /// <summary>
        ///   The default base class for every interaction line
        /// </summary>
+       /// <remarks>
+       ///   The expressions and statements behave as if they were
+       ///   a static method of this class.   The InteractiveBase class
+       ///   contains a number of useful methods, but can be overwritten
+       ///   by setting the InteractiveBaseType property in the Evaluator
+       /// </remarks>
        public class InteractiveBase {
+               /// <summary>
+               ///   Determines where the standard output of methods in this class will go. 
+               /// </summary>
                public static TextWriter Output = Console.Out;
+
+               /// <summary>
+               ///   Determines where the standard error of methods in this class will go. 
+               /// </summary>
                public static TextWriter Error = Console.Error;
+
+               /// <summary>
+               ///   The primary prompt used for interactive use.
+               /// </summary>
                public static string Prompt             = "csharp> ";
+
+               /// <summary>
+               ///   The secondary prompt used for interactive use (used when
+               ///   an expression is incomplete).
+               /// </summary>
                public static string ContinuationPrompt = "      > ";
 
+               /// <summary>
+               ///   Used to signal that the user has invoked the  `quit' statement.
+               /// </summary>
+               public static bool QuitRequested;
+               
+               /// <summary>
+               ///   Shows all the variables defined so far.
+               /// </summary>
                static public void ShowVars ()
                {
                        Output.Write (Evaluator.GetVars ());
                        Output.Flush ();
                }
 
+               /// <summary>
+               ///   Displays the using statements in effect at this point. 
+               /// </summary>
                static public void ShowUsing ()
                {
                        Output.Write (Evaluator.GetUsing ());
@@ -788,6 +954,9 @@ namespace Mono.CSharp {
 
                public delegate void Simple ();
                
+               /// <summary>
+               ///   Times the execution of the given delegate
+               /// </summary>
                static public TimeSpan Time (Simple a)
                {
                        DateTime start = DateTime.Now;
@@ -796,6 +965,14 @@ namespace Mono.CSharp {
                }
                
 #if !SMCS_SOURCE
+               /// <summary>
+               ///   Loads the assemblies from a package
+               /// </summary>
+               /// <remarks>
+               ///   Loads the assemblies from a package.   This is equivalent
+               ///   to passing the -pkg: command line flag to the C# compiler
+               ///   on the command line. 
+               /// </remarks>
                static public void LoadPackage (string pkg)
                {
                        if (pkg == null){
@@ -821,14 +998,27 @@ namespace Mono.CSharp {
                }
 #endif
 
+               /// <summary>
+               ///   Loads the assembly
+               /// </summary>
+               /// <remarks>
+               ///   Loads the specified assembly and makes its types
+               ///   available to the evaluator.  This is equivalent
+               ///   to passing the -pkg: command line flag to the C#
+               ///   compiler on the command line.
+               /// </remarks>
                static public void LoadAssembly (string assembly)
                {
                        Evaluator.LoadAssembly (assembly);
                }
                
+               /// <summary>
+               ///   Returns a list of available static methods. 
+               /// </summary>
                static public string help {
                        get {
                                return  "Static methods:\n"+
+                                       "  Describe(obj)      - Describes the object's type\n" + 
                                        "  LoadPackage (pkg); - Loads the given Package (like -pkg:FILE)\n" +
                                        "  LoadAssembly (ass) - Loads the given assembly (like -r:ASS)\n" + 
                                        "  ShowVars ();       - Shows defined local variables.\n" +
@@ -841,12 +1031,41 @@ namespace Mono.CSharp {
                        }
                }
 
+               /// <summary>
+               ///   Indicates to the read-eval-print-loop that the interaction should be finished. 
+               /// </summary>
                static public object quit {
                        get {
-                               Environment.Exit (0);
+                               QuitRequested = true;
                                return null;
                        }
                }
+
+#if !NET_2_1
+               /// <summary>
+               ///   Describes an object or a type.
+               /// </summary>
+               /// <remarks>
+               ///   This method will show a textual representation
+               ///   of the object's type.  If the object is a
+               ///   System.Type it renders the type directly,
+               ///   otherwise it renders the type returned by
+               ///   invoking GetType on the object.
+               /// </remarks>
+               static public string Describe (object x)
+               {
+                       if (x == null)
+                               return "";
+                       
+                       Type t = x as Type;
+                       if (t == null)
+                               t = x.GetType ();
+
+                       StringWriter sw = new StringWriter ();
+                       new Outline (t, sw, true, false, false).OutlineType ();
+                       return sw.ToString ();
+               }
+#endif
        }
 
        //
@@ -861,8 +1080,8 @@ namespace Mono.CSharp {
                TypeContainer container;
                string name;
                
-               public LocalVariableReferenceWithClassSideEffect (TypeContainer container, string name, Block current_block, string local_variable_id, Location loc)
-                       : base (current_block, local_variable_id, loc)
+               public LocalVariableReferenceWithClassSideEffect (TypeContainer container, string name, Block current_block, string local_variable_id, LocalInfo li, Location loc)
+                       : base (current_block, local_variable_id, loc, li, false)
                {
                        this.container = container;
                        this.name = name;
@@ -893,7 +1112,7 @@ namespace Mono.CSharp {
 
                        Field f = new Field (container, new TypeExpression (ret.Type, Location),
                                             Modifiers.PUBLIC | Modifiers.STATIC,
-                                            name, null, Location);
+                                            new MemberName (name, Location), null);
                        container.AddField (f);
                        if (f.Define ())
                                Evaluator.QueueField (f);
@@ -987,4 +1206,4 @@ namespace Mono.CSharp {
        }
        
 }
-       
\ No newline at end of file
+