+2008-09-10 Miguel de Icaza <miguel@novell.com>
+
+ * driver.cs: Drop --shell argument, the compiler is no longer a
+ REPL.
+
+ * eval.cs: Move most of the code that deals with evaluation into
+ this file and document the public API from repl.cs
+
+ * repl.cs: Remove from here.
+
2008-09-10 Marek Safar <marek.safar@gmail.com>
A fix for bug #424684
@ cp $(COMPILER_NAME).exe* $(topdir)/class/lib/$(PROFILE)/
q: cs-parser.cs qh
- echo 'System.Console.WriteLine ("Hello");' | mono --debug gmcs.exe -v --shell
- echo -e 'using System;\nConsole.WriteLine ("hello");' | mono --debug gmcs.exe -v --shell
- echo -e '"foo" == "bar";' | mono --debug gmcs.exe -v --shell
- echo -e 'var a = 1;\na + 2;' | mono --debug gmcs.exe -v --shell
- echo -e 'int j;\nj = 1;' | mono --debug gmcs.exe -v --shell
- echo -e 'var a = new int[]{1,2,3};\nfrom x in a select x;' | mono --debug gmcs.exe -v --shell
- echo -e 'var a = from f in System.IO.Directory.GetFiles ("/tmp") where f == "passwd" select f;' | mono gmcs.exe --shell
+ echo 'System.Console.WriteLine ("Hello");' | csharp
+ echo -e 'using System;\nConsole.WriteLine ("hello");' | csharp
+ echo -e '"foo" == "bar";' | csharp
+ echo -e 'var a = 1;\na + 2;' | csharp
+ echo -e 'int j;\nj = 1;' | csharp
+ echo -e 'var a = new int[]{1,2,3};\nfrom x in a select x;' | csharp
+ echo -e 'var a = from f in System.IO.Directory.GetFiles ("/tmp") where f == "passwd" select f;' | csharp
# read-eval-print-loop test target, for quickly hackign
repl:
: EVAL_STATEMENT_PARSER EOF
| EVAL_USING_DECLARATIONS_UNIT_PARSER using_directives
| EVAL_STATEMENT_PARSER {
- InteractiveShell.LoadAliases (current_namespace);
+ CSharpEvaluator.LoadAliases (current_namespace);
push_current_class (new Class (current_namespace, current_class, new MemberName ("Class" + class_count++),
Modifiers.PUBLIC, null), null);
ArrayList baseclass_list = new ArrayList ();
- baseclass_list.Add (new TypeExpression (typeof (InteractiveBase), lexer.Location));
+ baseclass_list.Add (new TypeExpression (CSharpEvaluator.InteractiveBaseClass, lexer.Location));
current_container.AddBasesForPart (current_class, baseclass_list);
// (ref object retval)
current_local_parameters = null;
}
| EVAL_COMPILATION_UNIT_PARSER {
- InteractiveShell.LoadAliases (current_namespace);
+ CSharpEvaluator.LoadAliases (current_namespace);
}
interactive_compilation_unit
;
current_container.AddField (f);
// Register the field to be visible later as a global variable
- InteractiveShell.QueueField (f);
+ CSharpEvaluator.QueueField (f);
}
}
//
bool load_default_config = true;
- //
- // Whether this is going to be an interactive C-Sharp session
- public bool Shell = false;
-
//
// A list of resource files
//
encoding = Encoding.Default;
}
- public static Driver Create (string [] args)
+ public static Driver Create (string [] args, bool require_files)
{
Driver d = new Driver ();
- if (!d.ParseArguments (args))
+ if (!d.ParseArguments (args, require_files))
return null;
return d;
Location.InEmacs = Environment.GetEnvironmentVariable ("EMACS") == "t";
- Driver d = Driver.Create (args);
+ Driver d = Driver.Create (args, true);
if (d == null)
return 1;
- if (d.Shell)
- return d.StartInteractiveShell ();
-
else if (d.Compile () && Report.Errors == 0) {
if (Report.Warnings > 0) {
Console.WriteLine ("Compilation succeeded - {0} warning(s)", Report.Warnings);
Location.AddFile (f);
}
- bool ParseArguments (string[] args)
+ bool ParseArguments (string[] args, bool require_files)
{
references = new ArrayList ();
external_aliases = new Hashtable ();
ProcessSourceFiles (arg, false);
}
- //
- // For now, very lax error checking
- //
- if (Shell)
+ if (require_files == false)
return true;
//
Report.Warning (-29, 1, "Compatibility: Use -noconfig option instead of --noconfig");
load_default_config = false;
return true;
-
- case "--shell":
- Shell = true;
- return true;
}
return false;
return true;
}
- int StartInteractiveShell ()
- {
- ProcessDefaultConfig ();
- return InteractiveShell.ReadEvalPrintLoop ();
- }
-
//
// Main compilation method
//
{
Report.Stderr = error;
try {
- Driver d = Driver.Create (args);
+ Driver d = Driver.Create (args, true);
if (d == null)
return false;
Expression e;
int errors = Report.Errors;
-
e = MemberLookup (ec.ContainerType, qualifier_type, queried_type, name, mt, bf, loc);
if (e != null || errors != Report.Errors)
}
if (RootContext.EvalMode){
- FieldInfo fi = InteractiveShell.LookupField (Name);
+ FieldInfo fi = CSharpEvaluator.LookupField (Name);
if (fi != null)
return new FieldExpr (fi, loc);
}
--- /dev/null
+//
+// eval.cs: Evaluation and Hosting API for the C# compiler
+//
+// Authors:
+// Miguel de Icaza (miguel@gnome.org)
+//
+// Dual licensed under the terms of the MIT X11 or GNU GPL
+//
+// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
+// Copyright 2004, 2005, 2006, 2007, 2008 Novell, Inc
+//
+using System;
+using System.Threading;
+using System.Collections;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.IO;
+using System.Globalization;
+using System.Text;
+
+namespace Mono.CSharp {
+
+ /// <summary>
+ /// CSharpEvaluator: provides an API to evaluate C# statements and
+ /// expressions dynamically.
+ /// </summary>
+ /// <remarks>
+ /// This class exposes static methods to evaluate expressions in the
+ /// current program.
+ ///
+ /// To initialize the evaluator with a number of compiler
+ /// options call the Init(string[]args) method with a set of
+ /// command line options that the compiler recognizes.
+ ///
+ /// To interrupt execution of a statement, you can invoke the
+ /// CSharpEvaluator.Interrupt method.
+ /// </remarks>
+ public class CSharpEvaluator {
+ static CSharpEvaluator evaluator;
+ static string current_debug_name;
+ static int count;
+ static Thread invoke_thread;
+
+ static public ArrayList using_alias_list = new ArrayList ();
+ static public ArrayList using_list = new ArrayList ();
+ static public Hashtable fields = new Hashtable ();
+
+ static Type interactive_base_class = typeof (InteractiveBase);
+ static Driver driver;
+ static bool inited;
+
+ /// <summary>
+ /// Optioanl initialization for the CSharpEvaluator.
+ /// </summary>
+ /// <remarks>
+ /// Initializes the CSharpEvaluator with the command line options
+ /// that would be processed by the command line compiler. Only
+ /// the first call to 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.
+ /// </remarks>
+ public static void Init (string [] args)
+ {
+ if (inited)
+ return;
+
+ RootContext.Version = LanguageVersion.Default;
+ Driver driver = Driver.Create (args, false);
+ if (driver == null)
+ throw new Exception ("Failed to create compiler driver with the given arguments");
+
+ driver.ProcessDefaultConfig ();
+ CompilerCallableEntryPoint.Reset ();
+ Driver.LoadReferences ();
+ RootContext.EvalMode = true;
+ inited = true;
+ }
+
+ static void Init ()
+ {
+ Init (new string [0]);
+ }
+
+ static void Reset ()
+ {
+ CompilerCallableEntryPoint.PartialReset ();
+
+ //
+ // PartialReset should not reset the core types, this is very redundant.
+ //
+ if (!TypeManager.InitCoreTypes ())
+ throw new Exception ("Failed to InitCoreTypes");
+ TypeManager.InitOptionalCoreTypes ();
+
+ Location.AddFile ("<interactive>");
+ Location.Initialize ();
+
+ current_debug_name = "interactive" + (count++) + ".dll";
+ if (Environment.GetEnvironmentVariable ("SAVE") != null){
+ CodeGen.Init (current_debug_name, current_debug_name, false);
+ } else
+ CodeGen.InitDynamic (current_debug_name);
+ }
+
+ /// <summary>
+ /// The base class for the classes that host the user generated code
+ /// </summary>
+ /// <remarks>
+ ///
+ /// This is the base class that will host the code
+ /// executed by the CSharpEvaluator. By default
+ /// this is the Mono.CSharp.InteractiveBase class
+ /// which is useful for interactive use.
+ ///
+ /// By changing this property you can control the
+ /// base class and the static members that are
+ /// available to your evaluated code.
+ /// </remarks>
+ static public Type InteractiveBaseClass {
+ get {
+ return interactive_base_class;
+ }
+
+ set {
+ if (value == null)
+ throw new ArgumentNullException ();
+
+ interactive_base_class = value;
+ }
+ }
+
+ /// <summary>
+ /// Interrupts the evaluation of an expression.
+ /// </summary>
+ /// <remarks>
+ /// Use this method to interrupt long-running invocations.
+ /// </remarks>
+ public static void Interrupt ()
+ {
+ if (!inited || !invoking)
+ return;
+
+ if (invoke_thread != null)
+ invoke_thread.Abort ();
+ }
+
+ //
+ // Todo: Should we handle errors, or expect the calling code to setup
+ // the recording themselves?
+ //
+
+ /// <summary>
+ /// Evaluates and expression or statement and returns any result values.
+ /// </summary>
+ /// <remarks>
+ /// Evaluates the input string as a C# expression or
+ /// statement. If the input string is an expression
+ /// the result will be stored in the result variable
+ /// and the result_set variable will be set to true.
+ ///
+ /// It is necessary to use the result/result_set
+ /// pair to identify when a result was set (for
+ /// example, execution of user-provided input can be
+ /// an expression, a statement or others, and
+ /// result_set would only be set if the input was an
+ /// expression.
+ ///
+ /// If the return value of this function is null,
+ /// this indicates that the parsing was complete.
+ /// If the return value is a string, it indicates
+ /// that the input is partial and that the user
+ /// should provide an updated string.
+ /// </remarks>
+ public static string Evaluate (string input, out object result, out bool result_set)
+ {
+ result_set = false;
+ result = null;
+
+ if (input == null || input.Length == 0)
+ return null;
+
+ if (!inited)
+ Init ();
+
+ bool partial_input;
+ CSharpParser parser = ParseString (true, input, out partial_input);
+ if (parser == null){
+ if (partial_input)
+ return input;
+
+ ParseString (false, input, out partial_input);
+ return null;
+ }
+
+ object parser_result = parser.InteractiveResult;
+
+ if (!(parser_result is Class))
+ parser.CurrentNamespace.Extract (using_alias_list, using_list);
+
+ result = ExecuteBlock (parser_result as Class, parser.undo);
+ //
+ // We use a reference to a compiler type, in this case
+ // Driver as a flag to indicate that this was a statement
+ //
+ if (result != typeof (NoValueSet))
+ result_set = true;
+
+ return null;
+ }
+
+ /// <summary>
+ /// Executes the given expression or statement.
+ /// </summary>
+ /// <remarks>
+ /// Executes the provided statement, returns true
+ /// on success, false on parsing errors. Exceptions
+ /// might be thrown by the called code.
+ /// </remarks>
+ public static bool Run (string statement)
+ {
+ if (!inited)
+ Init ();
+
+ object result;
+ bool result_set;
+
+ bool ok = Evaluate (statement, out result, out result_set) == null;
+
+ return ok;
+ }
+
+ /// <summary>
+ /// Evaluates and expression or statement and returns the result.
+ /// </summary>
+ /// <remarks>
+ /// Evaluates the input string as a C# expression or
+ /// statement and returns the value.
+ ///
+ /// This method will throw an exception if there is a syntax error,
+ /// of if the provided input is not an expression but a statement.
+ /// </remarks>
+ public static object Evaluate (string input)
+ {
+ object result;
+ bool result_set;
+
+ string r = Evaluate (input, out result, out result_set);
+
+ if (r != null || result_set == false)
+ throw new ArgumentException ("Syntax error on input");
+
+ return result;
+ }
+
+ enum InputKind {
+ EOF,
+ StatementOrExpression,
+ CompilationUnit,
+ Error
+ }
+
+ //
+ // Deambiguates the input string to determine if we
+ // want to process a statement or if we want to
+ // process a compilation unit.
+ //
+ // This is done using a top-down predictive parser,
+ // since the yacc/jay parser can not deambiguage this
+ // without more than one lookahead token. There are very
+ // few ambiguities.
+ //
+ static InputKind ToplevelOrStatement (SeekableStreamReader seekable)
+ {
+ Tokenizer tokenizer = new Tokenizer (seekable, Location.SourceFiles [0]);
+
+ int t = tokenizer.token ();
+ switch (t){
+ case Token.EOF:
+ return InputKind.EOF;
+
+ // These are toplevels
+ case Token.EXTERN:
+ case Token.OPEN_BRACKET:
+ case Token.ABSTRACT:
+ case Token.CLASS:
+ case Token.ENUM:
+ case Token.INTERFACE:
+ case Token.INTERNAL:
+ case Token.NAMESPACE:
+ case Token.PRIVATE:
+ case Token.PROTECTED:
+ case Token.PUBLIC:
+ case Token.SEALED:
+ case Token.STATIC:
+ case Token.STRUCT:
+ return InputKind.CompilationUnit;
+
+ // Definitely expression
+ case Token.FIXED:
+ case Token.BOOL:
+ case Token.BYTE:
+ case Token.CHAR:
+ case Token.DECIMAL:
+ case Token.DOUBLE:
+ case Token.FLOAT:
+ case Token.INT:
+ case Token.LONG:
+ case Token.NEW:
+ case Token.OBJECT:
+ case Token.SBYTE:
+ case Token.SHORT:
+ case Token.STRING:
+ case Token.UINT:
+ case Token.ULONG:
+ return InputKind.StatementOrExpression;
+
+ // These need deambiguation help
+ case Token.USING:
+ t = tokenizer.token ();
+ if (t == Token.EOF)
+ return InputKind.EOF;
+
+ if (t == Token.IDENTIFIER)
+ return InputKind.CompilationUnit;
+ return InputKind.StatementOrExpression;
+
+
+ // Distinguish between:
+ // delegate opt_anonymous_method_signature block
+ // delegate type
+ case Token.DELEGATE:
+ t = tokenizer.token ();
+ if (t == Token.EOF)
+ return InputKind.EOF;
+ if (t == Token.OPEN_PARENS || t == Token.OPEN_BRACE)
+ return InputKind.StatementOrExpression;
+ return InputKind.CompilationUnit;
+
+ // Distinguih between:
+ // unsafe block
+ // unsafe as modifier of a type declaration
+ case Token.UNSAFE:
+ t = tokenizer.token ();
+ if (t == Token.EOF)
+ return InputKind.EOF;
+ if (t == Token.OPEN_PARENS)
+ return InputKind.StatementOrExpression;
+ return InputKind.CompilationUnit;
+
+ // These are errors: we list explicitly what we had
+ // from the grammar, ERROR and then everything else
+
+ case Token.READONLY:
+ case Token.OVERRIDE:
+ case Token.ERROR:
+ return InputKind.Error;
+
+ // This catches everything else allowed by
+ // expressions. We could add one-by-one use cases
+ // if needed.
+ default:
+ return InputKind.StatementOrExpression;
+ }
+ }
+
+ //
+ // Parses the string @input and returns a CSharpParser if succeeful.
+ //
+ // if @silent is set to true then no errors are
+ // reported to the user. This is used to do various calls to the
+ // parser and check if the expression is parsable.
+ //
+ // @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)
+ {
+ partial_input = false;
+ Reset ();
+ queued_fields.Clear ();
+
+ Stream s = new MemoryStream (Encoding.Default.GetBytes (input));
+ SeekableStreamReader seekable = new SeekableStreamReader (s, Encoding.Default);
+
+ InputKind kind = ToplevelOrStatement (seekable);
+ if (kind == InputKind.Error){
+ if (!silent)
+ Report.Error (-25, "Detection Parsing Error");
+ partial_input = false;
+ return null;
+ }
+
+ if (kind == InputKind.EOF){
+ if (silent == false)
+ Console.Error.WriteLine ("Internal error: EOF condition should have been detected in a previous call with silent=true");
+ partial_input = true;
+ return null;
+
+ }
+ seekable.Position = 0;
+
+ CSharpParser parser = new CSharpParser (seekable, Location.SourceFiles [0]);
+ parser.ErrorOutput = Report.Stderr;
+
+ if (kind == InputKind.StatementOrExpression){
+ parser.Lexer.putback_char = Tokenizer.EvalStatementParserCharacter;
+ RootContext.StatementMode = true;
+ } else {
+ //
+ // Do not activate EvalCompilationUnitParserCharacter until
+ // I have figured out all the limitations to invoke methods
+ // in the generated classes. See repl.txt
+ //
+ parser.Lexer.putback_char = Tokenizer.EvalUsingDeclarationsParserCharacter;
+ //parser.Lexer.putback_char = Tokenizer.EvalCompilationUnitParserCharacter;
+ RootContext.StatementMode = false;
+ }
+
+ if (silent)
+ Report.DisableReporting ();
+ try {
+ parser.parse ();
+ } finally {
+ if (Report.Errors != 0){
+ if (silent && parser.UnexpectedEOF)
+ partial_input = true;
+
+ parser.undo.ExecuteUndo ();
+ parser = null;
+ }
+
+ if (silent)
+ Report.EnableReporting ();
+ }
+ return parser;
+ }
+
+ delegate void HostSignature (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
+ // other).
+ //
+ static ArrayList queued_fields = new ArrayList ();
+
+ //static ArrayList types = new ArrayList ();
+
+ static volatile bool invoking;
+
+ static object ExecuteBlock (Class host, Undo undo)
+ {
+ RootContext.ResolveTree ();
+ if (Report.Errors != 0){
+ undo.ExecuteUndo ();
+ return typeof (NoValueSet);
+ }
+
+ RootContext.PopulateTypes ();
+ RootContext.DefineTypes ();
+
+ if (Report.Errors != 0){
+ undo.ExecuteUndo ();
+ return typeof (NoValueSet);
+ }
+
+ TypeBuilder tb = null;
+ MethodBuilder mb = null;
+
+ if (host != null){
+ tb = host.TypeBuilder;
+ mb = null;
+ foreach (MemberCore member in host.Methods){
+ if (member.Name != "Host")
+ continue;
+
+ MethodOrOperator method = (MethodOrOperator) member;
+ mb = method.MethodBuilder;
+ break;
+ }
+
+ if (mb == null)
+ throw new Exception ("Internal error: did not find the method builder for the generated method");
+ }
+
+ RootContext.EmitCode ();
+ if (Report.Errors != 0)
+ return typeof (NoValueSet);
+
+ RootContext.CloseTypes ();
+
+ if (Environment.GetEnvironmentVariable ("SAVE") != null)
+ CodeGen.Save (current_debug_name, false);
+
+ if (host == null)
+ return typeof (NoValueSet);
+
+ //
+ // Unlike Mono, .NET requires that the MethodInfo is fetched, it cant
+ // work from MethodBuilders. Retarded, I know.
+ //
+ Type tt = CodeGen.Assembly.Builder.GetType (tb.Name);
+ MethodInfo mi = tt.GetMethod (mb.Name);
+
+ // Pull the FieldInfos from the type, and keep track of them
+ foreach (Field field in queued_fields){
+ FieldInfo fi = tt.GetField (field.Name);
+
+ FieldInfo old = (FieldInfo) fields [field.Name];
+
+ // If a previous value was set, nullify it, so that we do
+ // not leak memory
+ if (old != null){
+ if (old.FieldType.IsValueType){
+ //
+ // TODO: Clear fields for structs
+ //
+ } else {
+ try {
+ old.SetValue (null, null);
+ } catch {
+ }
+ }
+ }
+
+ fields [field.Name] = fi;
+ }
+ //types.Add (tb);
+
+ queued_fields.Clear ();
+
+ HostSignature invoker = (HostSignature) System.Delegate.CreateDelegate (typeof (HostSignature), mi);
+ object retval = typeof (NoValueSet);
+
+ try {
+ invoke_thread = System.Threading.Thread.CurrentThread;
+ invoking = true;
+ invoker (ref retval);
+ } catch (ThreadAbortException e){
+ Thread.ResetAbort ();
+ Console.WriteLine ("Interrupted!\n{0}", e);
+ } finally {
+ invoking = false;
+ }
+
+ // d.DynamicInvoke (new object [] { retval });
+
+ return retval;
+ }
+
+ static internal void LoadAliases (NamespaceEntry ns)
+ {
+ ns.Populate (using_alias_list, using_list);
+ }
+
+ //
+ // Just a placeholder class, used as a sentinel to determine that the
+ // generated code did not set a value
+ class NoValueSet {
+ }
+
+ static internal FieldInfo LookupField (string name)
+ {
+ FieldInfo fi = (FieldInfo) fields [name];
+
+ return fi;
+ }
+
+ //
+ // Puts the FieldBuilder into a queue of names that will be
+ // registered. We can not register FieldBuilders directly
+ // we need to fetch the FieldInfo after Reflection cooks the
+ // types, or bad things happen (bad means: FieldBuilders behave
+ // incorrectly across multiple assemblies, causing assignments to
+ // invalid areas
+ //
+ // This also serves for the parser to register Field classes
+ // that should be exposed as global variables
+ //
+ static internal void QueueField (Field f)
+ {
+ queued_fields.Add (f);
+ }
+
+ static string Quote (string s)
+ {
+ if (s.IndexOf ('"') != -1)
+ s = s.Replace ("\"", "\\\"");
+
+ return "\"" + s + "\"";
+ }
+
+ static public string GetUsing ()
+ {
+ StringBuilder sb = new StringBuilder ();
+
+ foreach (object x in using_alias_list)
+ sb.Append (String.Format ("using {0};\n", x));
+
+ foreach (object x in using_list)
+ sb.Append (String.Format ("using {0};\n", x));
+
+ return sb.ToString ();
+ }
+
+ static public string GetVars ()
+ {
+ StringBuilder sb = new StringBuilder ();
+
+ foreach (DictionaryEntry de in fields){
+ FieldInfo fi = LookupField ((string) de.Key);
+ object value = null;
+ bool error = false;
+
+ try {
+ if (value == null)
+ value = "null";
+ value = fi.GetValue (null);
+ if (value is string)
+ value = Quote ((string)value);
+ } catch {
+ error = true;
+ }
+
+ if (error)
+ sb.Append (String.Format ("{0} {1} <error reading value>", TypeManager.CSharpName(fi.FieldType), de.Key));
+ else
+ sb.Append (String.Format ("{0} {1} = {2}", TypeManager.CSharpName(fi.FieldType), de.Key, value));
+ }
+
+ return sb.ToString ();
+ }
+
+ /// <summary>
+ /// Loads the given assembly and exposes the API to the user.
+ /// </summary>
+ static public void LoadAssembly (string file)
+ {
+ Driver.LoadAssembly (file, true);
+ }
+
+ /// <summary>
+ /// Exposes the API of the given assembly to the CSharpEvaluator
+ /// </summary>
+ static public void ReferenceAssembly (Assembly a)
+ {
+ RootNamespace.Global.AddAssemblyReference (a);
+ }
+
+ }
+
+ /// <summary>
+ /// The default base class for every interaction line
+ /// </summary>
+ public class InteractiveBase {
+ public static TextWriter Output = Console.Out;
+ public static TextWriter Error = Console.Error;
+ public static string Prompt = "csharp> ";
+ public static string ContinuationPrompt = " > ";
+
+ static public void ShowVars ()
+ {
+ Output.Write (CSharpEvaluator.GetVars ());
+ Output.Flush ();
+ }
+
+ static public void ShowUsing ()
+ {
+ Output.Write (CSharpEvaluator.GetUsing ());
+ Output.Flush ();
+ }
+
+ public delegate void Simple ();
+
+ static public TimeSpan Time (Simple a)
+ {
+ DateTime start = DateTime.Now;
+ a ();
+ return DateTime.Now - start;
+ }
+
+#if !SMCS_SOURCE
+ static public void LoadPackage (string pkg)
+ {
+ if (pkg == null){
+ Error.WriteLine ("Invalid package specified");
+ return;
+ }
+
+ string pkgout = Driver.GetPackageFlags (pkg, false);
+ if (pkgout == null)
+ return;
+
+ string [] xargs = pkgout.Trim (new Char [] {' ', '\n', '\r', '\t'}).
+ Split (new Char [] { ' ', '\t'});
+
+ foreach (string s in xargs){
+ if (s.StartsWith ("-r:") || s.StartsWith ("/r:") || s.StartsWith ("/reference:")){
+ string lib = s.Substring (s.IndexOf (':')+1);
+
+ Driver.LoadAssembly (lib, true);
+ continue;
+ }
+ }
+ }
+#endif
+
+ static public void LoadAssembly (string assembly)
+ {
+ Driver.LoadAssembly (assembly, true);
+ }
+
+ static public string help {
+ get {
+ return "Static methods:\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" +
+ " ShowUsing (); - Show active using decltions.\n" +
+ " Prompt - The prompt used by the C# shell\n" +
+ " ContinuationPrompt - The prompt for partial input\n" +
+ " Time(() -> { }) - Times the specified code\n" +
+ " quit;\n" +
+ " help;\n";
+ }
+ }
+
+ static public object quit {
+ get {
+ Environment.Exit (0);
+ return null;
+ }
+ }
+ }
+
+ //
+ // A local variable reference that will create a Field in a
+ // Class with the resolved type. This is necessary so we can
+ // support "var" as a field type in a class declaration.
+ //
+ // We allow LocalVariableReferece to do the heavy lifting, and
+ // then we insert the field with the resolved type
+ //
+ public class LocalVariableReferenceWithClassSideEffect : LocalVariableReference {
+ 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)
+ {
+ this.container = container;
+ this.name = name;
+ }
+
+ public override bool Equals (object obj)
+ {
+ LocalVariableReferenceWithClassSideEffect lvr = obj as LocalVariableReferenceWithClassSideEffect;
+ if (lvr == null)
+ return false;
+
+ if (lvr.name != name || lvr.container != container)
+ return false;
+
+ return base.Equals (obj);
+ }
+
+ public override int GetHashCode ()
+ {
+ return name.GetHashCode ();
+ }
+
+ override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
+ {
+ Expression ret = base.DoResolveLValue (ec, right_side);
+ if (ret == null)
+ return null;
+
+ Field f = new Field (container, new TypeExpression (ret.Type, Location),
+ Modifiers.PUBLIC | Modifiers.STATIC,
+ name, null, Location);
+ container.AddField (f);
+ if (f.Define ())
+ CSharpEvaluator.QueueField (f);
+
+ return ret;
+ }
+ }
+
+ /// <summary>
+ /// A class used to assign values if the source expression is not void
+ ///
+ /// Used by the interactive shell to allow it to call this code to set
+ /// the return value for an invocation.
+ /// </summary>
+ class OptionalAssign : SimpleAssign {
+ public OptionalAssign (Expression t, Expression s, Location loc)
+ : base (t, s, loc)
+ {
+ }
+
+ public override Expression DoResolve (EmitContext ec)
+ {
+ CloneContext cc = new CloneContext ();
+ Expression clone = source.Clone (cc);
+
+ clone = clone.Resolve (ec);
+ if (clone == null)
+ return null;
+
+ // This means its really a statement.
+ if (clone.Type == TypeManager.void_type){
+ source = source.Resolve (ec);
+ target = null;
+ type = TypeManager.void_type;
+ eclass = ExprClass.Value;
+ return this;
+ }
+
+ return base.DoResolve (ec);
+ }
+
+ public override void Emit (EmitContext ec)
+ {
+ if (target == null)
+ source.Emit (ec);
+ else
+ base.Emit (ec);
+ }
+
+ public override void EmitStatement (EmitContext ec)
+ {
+ if (target == null)
+ source.Emit (ec);
+ else
+ base.EmitStatement (ec);
+ }
+ }
+
+ public class Undo {
+ ArrayList undo_types;
+
+ public Undo ()
+ {
+ undo_types = new ArrayList ();
+ }
+
+ public void AddTypeContainer (TypeContainer current_container, TypeContainer tc)
+ {
+ if (current_container == tc){
+ Console.Error.WriteLine ("Internal error: inserting container into itself");
+ return;
+ }
+
+ if (undo_types == null)
+ undo_types = new ArrayList ();
+ undo_types.Add (new Pair (current_container, tc));
+ }
+
+ public void ExecuteUndo ()
+ {
+ if (undo_types == null)
+ return;
+
+ foreach (Pair p in undo_types){
+ TypeContainer current_container = (TypeContainer) p.First;
+
+ current_container.RemoveTypeContainer ((TypeContainer) p.Second);
+ }
+ undo_types = null;
+ }
+ }
+
+}
+
\ No newline at end of file
+++ /dev/null
-//
-// getline.cs: A command line editor
-//
-// Authors:
-// Miguel de Icaza (miguel@novell.com)
-//
-// Copyright 2008 Novell, Inc.
-//
-// Dual-licensed under the terms of the MIT X11 license or the
-// Apache License 2.0
-//
-// USE -define:DEMO to build this as a standalone file and test it
-//
-// TODO:
-// Enter an error (a = 1); Notice how the prompt is in the wrong line
-// This is caused by Stderr not being tracked by System.Console.
-// Completion support
-// Why is Thread.Interrupt not working? Currently I resort to Abort which is too much.
-//
-// Limitations in System.Console:
-// Console needs SIGWINCH support of some sort
-// Console needs a way of updating its position after things have been written
-// behind its back (P/Invoke puts for example).
-// System.Console needs to get the DELETE character, and report accordingly.
-// Typing before the program start causes the cursor position to be wrong
-// This is caused by Console not reading all the available data
-// before sending the report-position sequence and reading it back.
-//
-#if NET_2_0 || NET_1_1
-#define IN_MCS_BUILD
-#endif
-
-// Only compile this code in the 2.0 profile, but not in the Moonlight one
-#if (IN_MCS_BUILD && NET_2_0 && !SMCS_SOURCE) || !IN_MCS_BUILD
-using System;
-using System.Text;
-using System.IO;
-using System.Threading;
-using System.Reflection;
-
-namespace Mono.Terminal {
-
- public class LineEditor {
- //static StreamWriter log;
-
- // The text being edited.
- StringBuilder text;
-
- // The text as it is rendered (replaces (char)1 with ^A on display for example).
- StringBuilder rendered_text;
-
- // The prompt specified, and the prompt shown to the user.
- string prompt;
- string shown_prompt;
-
- // The current cursor position, indexes into "text", for an index
- // into rendered_text, use TextToRenderPos
- int cursor;
-
- // The row where we started displaying data.
- int home_row;
-
- // The maximum length that has been displayed on the screen
- int max_rendered;
-
- // If we are done editing, this breaks the interactive loop
- bool done = false;
-
- // The thread where the Editing started taking place
- Thread edit_thread;
-
- // Our object that tracks history
- History history;
-
- // The contents of the kill buffer (cut/paste in Emacs parlance)
- string kill_buffer = "";
-
- // The string being searched for
- string search;
- string last_search;
-
- // whether we are searching (-1= reverse; 0 = no; 1 = forward)
- int searching;
-
- // The position where we found the match.
- int match_at;
-
- // Used to implement the Kill semantics (multiple Alt-Ds accumulate)
- KeyHandler last_handler;
-
- delegate void KeyHandler ();
-
- struct Handler {
- public ConsoleKeyInfo CKI;
- public KeyHandler KeyHandler;
-
- public Handler (ConsoleKey key, KeyHandler h)
- {
- CKI = new ConsoleKeyInfo ((char) 0, key, false, false, false);
- KeyHandler = h;
- }
-
- public Handler (char c, KeyHandler h)
- {
- KeyHandler = h;
- // Use the "Zoom" as a flag that we only have a character.
- CKI = new ConsoleKeyInfo (c, ConsoleKey.Zoom, false, false, false);
- }
-
- public Handler (ConsoleKeyInfo cki, KeyHandler h)
- {
- CKI = cki;
- KeyHandler = h;
- }
-
- public static Handler Control (char c, KeyHandler h)
- {
- return new Handler ((char) (c - 'A' + 1), h);
- }
-
- public static Handler Alt (char c, ConsoleKey k, KeyHandler h)
- {
- ConsoleKeyInfo cki = new ConsoleKeyInfo ((char) c, k, false, true, false);
- return new Handler (cki, h);
- }
- }
-
- static Handler [] handlers;
-
- public LineEditor (string name) : this (name, 10) { }
-
- public LineEditor (string name, int histsize)
- {
- handlers = new Handler [] {
- new Handler (ConsoleKey.Home, CmdHome),
- new Handler (ConsoleKey.End, CmdEnd),
- new Handler (ConsoleKey.LeftArrow, CmdLeft),
- new Handler (ConsoleKey.RightArrow, CmdRight),
- new Handler (ConsoleKey.UpArrow, CmdHistoryPrev),
- new Handler (ConsoleKey.DownArrow, CmdHistoryNext),
- new Handler (ConsoleKey.Enter, CmdDone),
- new Handler (ConsoleKey.Backspace, CmdBackspace),
- new Handler (ConsoleKey.Delete, CmdDeleteChar),
-
- // Emacs keys
- Handler.Control ('A', CmdHome),
- Handler.Control ('E', CmdEnd),
- Handler.Control ('B', CmdLeft),
- Handler.Control ('F', CmdRight),
- Handler.Control ('P', CmdHistoryPrev),
- Handler.Control ('N', CmdHistoryNext),
- Handler.Control ('K', CmdKillToEOF),
- Handler.Control ('Y', CmdYank),
- Handler.Control ('D', CmdDeleteChar),
- Handler.Control ('L', CmdRefresh),
- Handler.Control ('R', CmdReverseSearch),
- Handler.Control ('G', delegate {} ),
- Handler.Alt ('B', ConsoleKey.B, CmdBackwardWord),
- Handler.Alt ('F', ConsoleKey.F, CmdForwardWord),
-
- Handler.Alt ('D', ConsoleKey.D, CmdDeleteWord),
- Handler.Alt ((char) 8, ConsoleKey.Backspace, CmdDeleteBackword),
-
- // DEBUG
- Handler.Control ('T', CmdDebug),
-
- // quote
- Handler.Control ('Q', delegate { HandleChar (Console.ReadKey (true).KeyChar); })
- };
-
- rendered_text = new StringBuilder ();
- text = new StringBuilder ();
-
- history = new History (name, histsize);
-
- //if (File.Exists ("log"))File.Delete ("log");
- //log = File.CreateText ("log");
- }
-
- void CmdDebug ()
- {
- history.Dump ();
- Console.WriteLine ();
- Render ();
- }
-
- void Render ()
- {
- Console.Write (shown_prompt);
- Console.Write (rendered_text);
-
- int max = System.Math.Max (rendered_text.Length + shown_prompt.Length, max_rendered);
-
- for (int i = rendered_text.Length + shown_prompt.Length; i < max_rendered; i++)
- Console.Write (' ');
- max_rendered = shown_prompt.Length + rendered_text.Length;
-
- // Write one more to ensure that we always wrap around properly if we are at the
- // end of a line.
- Console.Write (' ');
-
- UpdateHomeRow (max);
- }
-
- void UpdateHomeRow (int screenpos)
- {
- int lines = 1 + (screenpos / Console.WindowWidth);
-
- home_row = Console.CursorTop - (lines - 1);
- if (home_row < 0)
- home_row = 0;
- }
-
-
- void RenderFrom (int pos)
- {
- int rpos = TextToRenderPos (pos);
- int i;
-
- for (i = rpos; i < rendered_text.Length; i++)
- Console.Write (rendered_text [i]);
-
- if ((shown_prompt.Length + rendered_text.Length) > max_rendered)
- max_rendered = shown_prompt.Length + rendered_text.Length;
- else {
- int max_extra = max_rendered - shown_prompt.Length;
- for (; i < max_extra; i++)
- Console.Write (' ');
- }
- }
-
- void ComputeRendered ()
- {
- rendered_text.Length = 0;
-
- for (int i = 0; i < text.Length; i++){
- int c = (int) text [i];
- if (c < 26){
- if (c == '\t')
- rendered_text.Append (" ");
- else {
- rendered_text.Append ('^');
- rendered_text.Append ((char) (c + (int) 'A' - 1));
- }
- } else
- rendered_text.Append ((char)c);
- }
- }
-
- int TextToRenderPos (int pos)
- {
- int p = 0;
-
- for (int i = 0; i < pos; i++){
- int c = (int) text [i];
- if (c < 26){
- if (c == 9)
- p += 4;
- else
- p += 2;
- } else
- p++;
- }
- return p;
- }
-
- int TextToScreenPos (int pos)
- {
- return shown_prompt.Length + TextToRenderPos (pos);
- }
-
- string Prompt {
- get { return prompt; }
- set { prompt = value; }
- }
-
- int LineCount {
- get {
- return (shown_prompt.Length + rendered_text.Length)/Console.WindowWidth;
- }
- }
-
- void ForceCursor (int newpos)
- {
- cursor = newpos;
-
- int actual_pos = shown_prompt.Length + TextToRenderPos (cursor);
- int row = home_row + (actual_pos/Console.WindowWidth);
- int col = actual_pos % Console.WindowWidth;
-
- if (row >= Console.BufferHeight)
- row = Console.BufferHeight-1;
- Console.SetCursorPosition (col, row);
-
- //log.WriteLine ("Going to cursor={0} row={1} col={2} actual={3} prompt={4} ttr={5} old={6}", newpos, row, col, actual_pos, prompt.Length, TextToRenderPos (cursor), cursor);
- //log.Flush ();
- }
-
- void UpdateCursor (int newpos)
- {
- if (cursor == newpos)
- return;
-
- ForceCursor (newpos);
- }
-
- void InsertChar (char c)
- {
- int prev_lines = LineCount;
- text = text.Insert (cursor, c);
- ComputeRendered ();
- if (prev_lines != LineCount){
-
- Console.SetCursorPosition (0, home_row);
- Render ();
- ForceCursor (++cursor);
- } else {
- RenderFrom (cursor);
- ForceCursor (++cursor);
- UpdateHomeRow (TextToScreenPos (cursor));
- }
- }
-
- //
- // Commands
- //
- void CmdDone ()
- {
- done = true;
- }
-
- void CmdHome ()
- {
- UpdateCursor (0);
- }
-
- void CmdEnd ()
- {
- UpdateCursor (text.Length);
- }
-
- void CmdLeft ()
- {
- if (cursor == 0)
- return;
-
- UpdateCursor (cursor-1);
- }
-
- void CmdBackwardWord ()
- {
- int p = WordBackward (cursor);
- if (p == -1)
- return;
- UpdateCursor (p);
- }
-
- void CmdForwardWord ()
- {
- int p = WordForward (cursor);
- if (p == -1)
- return;
- UpdateCursor (p);
- }
-
- void CmdRight ()
- {
- if (cursor == text.Length)
- return;
-
- UpdateCursor (cursor+1);
- }
-
- void RenderAfter (int p)
- {
- ForceCursor (p);
- RenderFrom (p);
- ForceCursor (cursor);
- }
-
- void CmdBackspace ()
- {
- if (cursor == 0)
- return;
-
- text.Remove (--cursor, 1);
- ComputeRendered ();
- RenderAfter (cursor);
- }
-
- void CmdDeleteChar ()
- {
- // If there is no input, this behaves like EOF
- if (text.Length == 0){
- done = true;
- text = null;
- Console.WriteLine ();
- return;
- }
-
- if (cursor == text.Length)
- return;
- text.Remove (cursor, 1);
- ComputeRendered ();
- RenderAfter (cursor);
- }
-
- int WordForward (int p)
- {
- if (p >= text.Length)
- return -1;
-
- int i = p;
- if (Char.IsPunctuation (text [p]) || Char.IsWhiteSpace (text[p])){
- for (; i < text.Length; i++){
- if (Char.IsLetterOrDigit (text [i]))
- break;
- }
- for (; i < text.Length; i++){
- if (!Char.IsLetterOrDigit (text [i]))
- break;
- }
- } else {
- for (; i < text.Length; i++){
- if (!Char.IsLetterOrDigit (text [i]))
- break;
- }
- }
- if (i != p)
- return i;
- return -1;
- }
-
- int WordBackward (int p)
- {
- if (p == 0)
- return -1;
-
- int i = p-1;
- if (i == 0)
- return 0;
-
- if (Char.IsPunctuation (text [i]) || Char.IsSymbol (text [i]) || Char.IsWhiteSpace (text[i])){
- for (; i >= 0; i--){
- if (Char.IsLetterOrDigit (text [i]))
- break;
- }
- for (; i >= 0; i--){
- if (!Char.IsLetterOrDigit (text[i]))
- break;
- }
- } else {
- for (; i >= 0; i--){
- if (!Char.IsLetterOrDigit (text [i]))
- break;
- }
- }
- i++;
-
- if (i != p)
- return i;
-
- return -1;
- }
-
- void CmdDeleteWord ()
- {
- int pos = WordForward (cursor);
-
- if (pos == -1)
- return;
-
- string k = text.ToString (cursor, pos-cursor);
-
- if (last_handler == CmdDeleteWord)
- kill_buffer = kill_buffer + k;
- else
- kill_buffer = k;
-
- text.Remove (cursor, pos-cursor);
- ComputeRendered ();
- RenderAfter (cursor);
- }
-
- void CmdDeleteBackword ()
- {
- int pos = WordBackward (cursor);
- if (pos == -1)
- return;
-
- string k = text.ToString (pos, cursor-pos);
-
- if (last_handler == CmdDeleteBackword)
- kill_buffer = k + kill_buffer;
- else
- kill_buffer = k;
-
- text.Remove (pos, cursor-pos);
- ComputeRendered ();
- RenderAfter (pos);
- }
-
- //
- // Adds the current line to the history if needed
- //
- void HistoryUpdateLine ()
- {
- history.Update (text.ToString ());
- }
-
- void CmdHistoryPrev ()
- {
- if (!history.PreviousAvailable ())
- return;
-
- HistoryUpdateLine ();
-
- SetText (history.Previous ());
- }
-
- void CmdHistoryNext ()
- {
- if (!history.NextAvailable())
- return;
-
- history.Update (text.ToString ());
- SetText (history.Next ());
-
- }
-
- void CmdKillToEOF ()
- {
- kill_buffer = text.ToString (cursor, text.Length-cursor);
- text.Length = cursor;
- ComputeRendered ();
- RenderAfter (cursor);
- }
-
- void CmdYank ()
- {
- int prev_lines = LineCount;
- text.Insert (cursor, kill_buffer);
- ComputeRendered ();
- if (prev_lines != LineCount){
- Console.SetCursorPosition (0, home_row);
- Render ();
- cursor += kill_buffer.Length;
- ForceCursor (cursor);
- } else {
- RenderFrom (cursor);
- cursor += kill_buffer.Length;
- ForceCursor (cursor);
- UpdateHomeRow (TextToScreenPos (cursor));
- }
- }
-
- void SetSearchPrompt (string s)
- {
- SetPrompt ("(reverse-i-search)`" + s + "': ");
- }
-
- void ReverseSearch ()
- {
- int p;
-
- if (cursor == text.Length){
- // The cursor is at the end of the string
-
- p = text.ToString ().LastIndexOf (search);
- if (p != -1){
- match_at = p;
- cursor = p;
- ForceCursor (cursor);
- return;
- }
- } else {
- // The cursor is somewhere in the middle of the string
- int start = (cursor == match_at) ? cursor - 1 : cursor;
- if (start != -1){
- p = text.ToString ().LastIndexOf (search, start);
- if (p != -1){
- match_at = p;
- cursor = p;
- ForceCursor (cursor);
- return;
- }
- }
- }
-
- // Need to search backwards in history
- HistoryUpdateLine ();
- string s = history.SearchBackward (search);
- if (s != null){
- match_at = -1;
- SetText (s);
- ReverseSearch ();
- }
- }
-
- void CmdReverseSearch ()
- {
- if (searching == 0){
- match_at = -1;
- last_search = search;
- searching = -1;
- search = "";
- SetSearchPrompt ("");
- } else {
- if (search == ""){
- if (last_search != "" && last_search != null){
- search = last_search;
- SetSearchPrompt (search);
-
- ReverseSearch ();
- }
- return;
- }
- ReverseSearch ();
- }
- }
-
- void SearchAppend (char c)
- {
- search = search + c;
- SetSearchPrompt (search);
-
- //
- // If the new typed data still matches the current text, stay here
- //
- if (cursor < text.Length){
- string r = text.ToString (cursor, text.Length - cursor);
- if (r.StartsWith (search))
- return;
- }
-
- ReverseSearch ();
- }
-
- void CmdRefresh ()
- {
- Console.Clear ();
- max_rendered = 0;
- Render ();
- ForceCursor (cursor);
- }
-
- void InterruptEdit (object sender, ConsoleCancelEventArgs a)
- {
- // Do not abort our program:
- a.Cancel = true;
-
- // Interrupt the editor
- edit_thread.Abort();
- }
-
- void HandleChar (char c)
- {
- if (searching != 0)
- SearchAppend (c);
- else
- InsertChar (c);
- }
-
- void EditLoop ()
- {
- ConsoleKeyInfo cki;
-
- while (!done){
- cki = Console.ReadKey (true);
-
- bool handled = false;
- foreach (Handler handler in handlers){
- ConsoleKeyInfo t = handler.CKI;
-
- if (t.Key == cki.Key && t.Modifiers == cki.Modifiers){
- handled = true;
- handler.KeyHandler ();
- last_handler = handler.KeyHandler;
- break;
- } else if (t.KeyChar == cki.KeyChar && t.Key == ConsoleKey.Zoom){
- handled = true;
- handler.KeyHandler ();
- last_handler = handler.KeyHandler;
- break;
- }
- }
- if (handled){
- if (searching != 0){
- if (last_handler != CmdReverseSearch){
- searching = 0;
- SetPrompt (prompt);
- }
- }
- continue;
- }
-
- if (cki.KeyChar != (char) 0)
- HandleChar (cki.KeyChar);
- }
- }
-
- void InitText (string initial)
- {
- text = new StringBuilder (initial);
- ComputeRendered ();
- cursor = text.Length;
- Render ();
- ForceCursor (cursor);
- }
-
- void SetText (string newtext)
- {
- Console.SetCursorPosition (0, home_row);
- InitText (newtext);
- }
-
- void SetPrompt (string newprompt)
- {
- shown_prompt = newprompt;
- Console.SetCursorPosition (0, home_row);
- Render ();
- ForceCursor (cursor);
- }
-
- public string Edit (string prompt, string initial)
- {
- edit_thread = Thread.CurrentThread;
- searching = 0;
- Console.CancelKeyPress += InterruptEdit;
-
- done = false;
- history.CursorToEnd ();
- max_rendered = 0;
-
- Prompt = prompt;
- shown_prompt = prompt;
- InitText (initial);
- history.Append (initial);
-
- do {
- try {
- EditLoop ();
- } catch (ThreadAbortException){
- searching = 0;
- Thread.ResetAbort ();
- Console.WriteLine ();
- SetPrompt (prompt);
- SetText ("");
- }
- } while (!done);
- Console.WriteLine ();
-
- Console.CancelKeyPress -= InterruptEdit;
-
- if (text == null){
- history.Close ();
- return null;
- }
-
- string result = text.ToString ();
- if (result != "")
- history.Accept (result);
- else
- history.RemoveLast ();
-
- return result;
- }
-
- //
- // Emulates the bash-like behavior, where edits done to the
- // history are recorded
- //
- class History {
- string [] history;
- int head, tail;
- int cursor, count;
- string histfile;
-
- public History (string app, int size)
- {
- if (size < 1)
- throw new ArgumentException ("size");
-
- if (app != null){
- string dir = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData);
- //Console.WriteLine (dir);
- if (!Directory.Exists (dir)){
- try {
- Directory.CreateDirectory (dir);
- } catch {
- app = null;
- }
- }
- if (app != null)
- histfile = Path.Combine (dir, app) + ".history";
- }
-
- history = new string [size];
- head = tail = cursor = 0;
-
- if (File.Exists (histfile)){
- using (StreamReader sr = File.OpenText (histfile)){
- string line;
-
- while ((line = sr.ReadLine ()) != null){
- if (line != "")
- Append (line);
- }
- }
- }
- }
-
- public void Close ()
- {
- if (histfile == null)
- return;
-
- try {
- using (StreamWriter sw = File.CreateText (histfile)){
- int start = (count == history.Length) ? head : tail;
- for (int i = start; i < start+count; i++){
- int p = i % history.Length;
- sw.WriteLine (history [p]);
- }
- }
- } catch {
- // ignore
- }
- }
-
- //
- // Appends a value to the history
- //
- public void Append (string s)
- {
- //Console.WriteLine ("APPENDING {0} {1}", s, Environment.StackTrace);
- history [head] = s;
- head = (head+1) % history.Length;
- if (head == tail)
- tail = (tail+1 % history.Length);
- if (count != history.Length)
- count++;
- }
-
- //
- // Updates the current cursor location with the string,
- // to support editing of history items. For the current
- // line to participate, an Append must be done before.
- //
- public void Update (string s)
- {
- history [cursor] = s;
- }
-
- public void RemoveLast ()
- {
- head = head-1;
- if (head < 0)
- head = history.Length-1;
- }
-
- public void Accept (string s)
- {
- int t = head-1;
- if (t < 0)
- t = history.Length-1;
-
- history [t] = s;
- }
-
- public bool PreviousAvailable ()
- {
- //Console.WriteLine ("h={0} t={1} cursor={2}", head, tail, cursor);
- if (count == 0 || cursor == tail)
- return false;
-
- return true;
- }
-
- public bool NextAvailable ()
- {
- int next = (cursor + 1) % history.Length;
- if (count == 0 || next > head)
- return false;
-
- return true;
- }
-
-
- //
- // Returns: a string with the previous line contents, or
- // nul if there is no data in the history to move to.
- //
- public string Previous ()
- {
- if (!PreviousAvailable ())
- return null;
-
- cursor--;
- if (cursor < 0)
- cursor = history.Length - 1;
-
- return history [cursor];
- }
-
- public string Next ()
- {
- if (!NextAvailable ())
- return null;
-
- cursor = (cursor + 1) % history.Length;
- return history [cursor];
- }
-
- public void CursorToEnd ()
- {
- if (head == tail)
- return;
-
- cursor = head;
- }
-
- public void Dump ()
- {
- Console.WriteLine ("Head={0} Tail={1} Cursor={2}", head, tail, cursor);
- for (int i = 0; i < history.Length;i++){
- Console.WriteLine (" {0} {1}: {2}", i == cursor ? "==>" : " ", i, history[i]);
- }
- //log.Flush ();
- }
-
- public string SearchBackward (string term)
- {
- for (int i = 1; i < count; i++){
- int slot = cursor-i;
- if (slot < 0)
- slot = history.Length-1;
- if (history [slot] != null && history [slot].IndexOf (term) != -1){
- cursor = slot;
- return history [slot];
- }
-
- // Will the next hit tail?
- slot--;
- if (slot < 0)
- slot = history.Length-1;
- if (slot == tail)
- break;
- }
-
- return null;
- }
-
- }
- }
-
-#if DEMO
- class Demo {
- static void Main ()
- {
- LineEditor le = new LineEditor (null);
- string s;
-
- while ((s = le.Edit ("shell> ", "")) != null){
- Console.WriteLine ("----> [{0}]", s);
- }
- }
- }
-#endif
-}
-#endif
anonymous.cs
assign.cs
attribute.cs
-driver.cs
cs-tokenizer.cs
cfold.cs
class.cs
decl.cs
delegate.cs
doc.cs
-enum.cs
+driver.cs
ecore.cs
+enum.cs
+eval.cs
expression.cs
flowanalysis.cs
generic.cs
-getline.cs
iterators.cs
lambda.cs
linq.cs
nullable.cs
parameter.cs
pending.cs
-repl.cs
report.cs
rootcontext.cs
roottypes.cs
doc-bootstrap.cs
enum.cs
ecore.cs
+eval.cs
expression.cs
flowanalysis.cs
generic-mcs.cs
nullable.cs
parameter.cs
pending.cs
-repl.cs
report.cs
rootcontext.cs
roottypes.cs
+++ /dev/null
-//
-// repl.cs: Support for using the compiler in interactive mode (read-eval-print loop)
-//
-// Authors:
-// Miguel de Icaza (miguel@gnome.org)
-//
-// Dual licensed under the terms of the MIT X11 or GNU GPL
-//
-// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
-// Copyright 2004, 2005, 2006, 2007, 2008 Novell, Inc
-//
-//
-// TODO:
-// Do not print results in Evaluate, do that elsewhere in preparation for Eval refactoring.
-// Driver.PartialReset should not reset the coretypes, nor the optional types, to avoid
-// computing that on every call.
-//
-using System;
-using System.IO;
-using System.Text;
-using System.Globalization;
-using System.Collections;
-using System.Reflection;
-using System.Reflection.Emit;
-using System.Threading;
-
-namespace Mono.CSharp {
-
- public static class InteractiveShell {
- static bool isatty = true;
- static public ArrayList using_alias_list = new ArrayList ();
- static public ArrayList using_list = new ArrayList ();
- public static Hashtable fields = new Hashtable ();
-
- static int count;
- static string current_debug_name;
-
-#if NET_2_0 && !SMCS_SOURCE
- static Mono.Terminal.LineEditor editor;
- static bool dumb;
- static Thread invoke_thread;
-
- static void ConsoleInterrupt (object sender, ConsoleCancelEventArgs a)
- {
- // Do not about our program
- a.Cancel = true;
-
- if (invoking)
- invoke_thread.Abort ();
- }
-
- static void SetupConsole ()
- {
- string term = Environment.GetEnvironmentVariable ("TERM");
- dumb = term == "dumb" || term == null || isatty == false;
-
- editor = new Mono.Terminal.LineEditor ("csharp", 300);
- Console.CancelKeyPress += ConsoleInterrupt;
- invoke_thread = System.Threading.Thread.CurrentThread;
- }
-
- static string GetLine (bool primary)
- {
- string prompt = primary ? InteractiveBase.Prompt : InteractiveBase.ContinuationPrompt;
-
- if (dumb){
- if (isatty)
- Console.Write (prompt);
-
- return Console.ReadLine ();
- } else {
- return editor.Edit (prompt, "");
- }
- }
-
-#else
- static void SetupConsole ()
- {
- // Here just to shut up the compiler warnings about unused invoking variable
- if (invoking)
- invoking = false;
-
- Console.Error.WriteLine ("Warning: limited functionality in 1.0 mode");
- }
-
- static string GetLine (bool primary)
- {
- string prompt = primary ? InteractiveBase.Prompt : InteractiveBase.ContinuationPrompt;
-
- if (isatty)
- Console.Write (prompt);
-
- return Console.ReadLine ();
- }
-
-#endif
- delegate string ReadLiner (bool primary);
-
- static void Reset ()
- {
- CompilerCallableEntryPoint.PartialReset ();
-
- //
- // PartialReset should not reset the core types, this is very redundant.
- //
- if (!TypeManager.InitCoreTypes ())
- throw new Exception ("Failed to InitCoreTypes");
- TypeManager.InitOptionalCoreTypes ();
-
- Location.AddFile ("<interactive>");
- Location.Initialize ();
-
- current_debug_name = "interactive" + (count++) + ".dll";
- if (Environment.GetEnvironmentVariable ("SAVE") != null){
- CodeGen.Init (current_debug_name, current_debug_name, false);
- } else
- CodeGen.InitDynamic (current_debug_name);
- }
-
- static void InitializeUsing ()
- {
- Evaluate ("using System; using System.Linq; using System.Collections.Generic; using System.Collections;");
- }
-
- static void InitTerminal ()
- {
- isatty = UnixUtils.isatty (0) && UnixUtils.isatty (1);
-
- // Work around, since Console is not accounting for
- // cursor position when writing to Stderr. It also
- // has the undesirable side effect of making
- // errors plain, with no coloring.
- Report.Stderr = Console.Out;
- SetupConsole ();
-
- if (isatty)
- Console.WriteLine ("Mono C# Shell, type \"help;\" for help\n\nEnter statements below.");
-
- }
-
-#if BOOTSTRAP_WITH_OLDLIB
- static void LoadStartupFiles ()
- {
- }
-#else
- static void LoadStartupFiles ()
- {
- string dir = Path.Combine (
- Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData),
- "csharp");
- if (!Directory.Exists (dir))
- return;
-
- foreach (string file in Directory.GetFiles (dir)){
- string l = file.ToLower ();
-
- if (l.EndsWith (".cs")){
- try {
- using (StreamReader r = File.OpenText (file)){
- ReadEvalPrintLoopWith (p => r.ReadLine ());
- }
- } catch {
- }
- } else if (l.EndsWith (".dll")){
- Driver.LoadAssembly (file, true);
- }
- }
- }
-#endif
-
- static void ReadEvalPrintLoopWith (ReadLiner readline)
- {
- string expr = null;
- while (true){
- string input = readline (expr == null);
- if (input == null)
- return;
-
- if (input == "")
- continue;
-
- expr = expr == null ? input : expr + "\n" + input;
-
- expr = Evaluate (expr);
- }
- }
-
- static public int ReadEvalPrintLoop ()
- {
- CompilerCallableEntryPoint.Reset ();
- Driver.LoadReferences ();
-
- InitTerminal ();
- RootContext.EvalMode = true;
-
- InitializeUsing ();
-
- LoadStartupFiles ();
- ReadEvalPrintLoopWith (GetLine);
-
- return 0;
- }
-
-
- static string Evaluate (string input)
- {
- if (input == null)
- return null;
-
- bool partial_input;
- CSharpParser parser = ParseString (true, input, out partial_input);
- if (parser == null){
- if (partial_input)
- return input;
-
- ParseString (false, input, out partial_input);
- return null;
- }
-
- //
- // The parser.InteractiveResult will eventually be multiple
- // different things. Currently they are statements, but
- // we will add support for copy-pasting entire blocks of
- // code, so we will allow namespaces, types, etc
- //
- object result = parser.InteractiveResult;
-
- try {
- if (!(result is Class))
- parser.CurrentNamespace.Extract (using_alias_list, using_list);
-
- object rval = ExecuteBlock (result as Class, parser.undo);
- //
- // We use a reference to a compiler type, in this case
- // Driver as a flag to indicate that this was a statement
- //
- if (rval != typeof (NoValueSet)){
- PrettyPrint (rval);
- Console.WriteLine ();
- }
- } catch (Exception e){
- Console.WriteLine (e);
- }
- return null;
- }
-
- enum InputKind {
- EOF,
- StatementOrExpression,
- CompilationUnit,
- Error
- }
-
- //
- // Deambiguates the input string to determine if we
- // want to process a statement or if we want to
- // process a compilation unit.
- //
- // This is done using a top-down predictive parser,
- // since the yacc/jay parser can not deambiguage this
- // without more than one lookahead token. There are very
- // few ambiguities.
- //
- static InputKind ToplevelOrStatement (SeekableStreamReader seekable)
- {
- Tokenizer tokenizer = new Tokenizer (seekable, Location.SourceFiles [0]);
-
- int t = tokenizer.token ();
- switch (t){
- case Token.EOF:
- return InputKind.EOF;
-
- // These are toplevels
- case Token.EXTERN:
- case Token.OPEN_BRACKET:
- case Token.ABSTRACT:
- case Token.CLASS:
- case Token.ENUM:
- case Token.INTERFACE:
- case Token.INTERNAL:
- case Token.NAMESPACE:
- case Token.PRIVATE:
- case Token.PROTECTED:
- case Token.PUBLIC:
- case Token.SEALED:
- case Token.STATIC:
- case Token.STRUCT:
- return InputKind.CompilationUnit;
-
- // Definitely expression
- case Token.FIXED:
- case Token.BOOL:
- case Token.BYTE:
- case Token.CHAR:
- case Token.DECIMAL:
- case Token.DOUBLE:
- case Token.FLOAT:
- case Token.INT:
- case Token.LONG:
- case Token.NEW:
- case Token.OBJECT:
- case Token.SBYTE:
- case Token.SHORT:
- case Token.STRING:
- case Token.UINT:
- case Token.ULONG:
- return InputKind.StatementOrExpression;
-
- // These need deambiguation help
- case Token.USING:
- t = tokenizer.token ();
- if (t == Token.EOF)
- return InputKind.EOF;
-
- if (t == Token.IDENTIFIER)
- return InputKind.CompilationUnit;
- return InputKind.StatementOrExpression;
-
-
- // Distinguish between:
- // delegate opt_anonymous_method_signature block
- // delegate type
- case Token.DELEGATE:
- t = tokenizer.token ();
- if (t == Token.EOF)
- return InputKind.EOF;
- if (t == Token.OPEN_PARENS || t == Token.OPEN_BRACE)
- return InputKind.StatementOrExpression;
- return InputKind.CompilationUnit;
-
- // Distinguih between:
- // unsafe block
- // unsafe as modifier of a type declaration
- case Token.UNSAFE:
- t = tokenizer.token ();
- if (t == Token.EOF)
- return InputKind.EOF;
- if (t == Token.OPEN_PARENS)
- return InputKind.StatementOrExpression;
- return InputKind.CompilationUnit;
-
- // These are errors: we list explicitly what we had
- // from the grammar, ERROR and then everything else
-
- case Token.READONLY:
- case Token.OVERRIDE:
- case Token.ERROR:
- return InputKind.Error;
-
- // This catches everything else allowed by
- // expressions. We could add one-by-one use cases
- // if needed.
- default:
- return InputKind.StatementOrExpression;
- }
- }
-
- //
- // Parses the string @input and returns a CSharpParser if succeeful.
- //
- // if @silent is set to true then no errors are
- // reported to the user. This is used to do various calls to the
- // parser and check if the expression is parsable.
- //
- // @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)
- {
- partial_input = false;
- Reset ();
- queued_fields.Clear ();
-
- Stream s = new MemoryStream (Encoding.Default.GetBytes (input));
- SeekableStreamReader seekable = new SeekableStreamReader (s, Encoding.Default);
-
- InputKind kind = ToplevelOrStatement (seekable);
- if (kind == InputKind.Error){
- if (!silent)
- Report.Error (-25, "Detection Parsing Error");
- partial_input = false;
- return null;
- }
-
- if (kind == InputKind.EOF){
- if (silent == false)
- Console.Error.WriteLine ("Internal error: EOF condition should have been detected in a previous call with silent=true");
- partial_input = true;
- return null;
-
- }
- seekable.Position = 0;
-
- CSharpParser parser = new CSharpParser (seekable, Location.SourceFiles [0]);
- parser.ErrorOutput = Report.Stderr;
-
- if (kind == InputKind.StatementOrExpression){
- parser.Lexer.putback_char = Tokenizer.EvalStatementParserCharacter;
- RootContext.StatementMode = true;
- } else {
- //
- // Do not activate EvalCompilationUnitParserCharacter until
- // I have figured out all the limitations to invoke methods
- // in the generated classes. See repl.txt
- //
- parser.Lexer.putback_char = Tokenizer.EvalUsingDeclarationsParserCharacter;
- //parser.Lexer.putback_char = Tokenizer.EvalCompilationUnitParserCharacter;
- RootContext.StatementMode = false;
- }
-
- if (silent)
- Report.DisableReporting ();
- try {
- parser.parse ();
- } finally {
- if (Report.Errors != 0){
- if (silent && parser.UnexpectedEOF)
- partial_input = true;
-
- parser.undo.ExecuteUndo ();
- parser = null;
- }
-
- if (silent)
- Report.EnableReporting ();
- }
- return parser;
- }
-
- static void p (string s)
- {
- Console.Write (s);
- }
-
- static string EscapeString (string s)
- {
- return s.Replace ("\"", "\\\"");
- }
-
- static void PrettyPrint (object result)
- {
- if (result == null){
- p ("null");
- return;
- }
-
- if (result is Array){
- Array a = (Array) result;
-
- p ("{ ");
- int top = a.GetUpperBound (0);
- for (int i = a.GetLowerBound (0); i <= top; i++){
- PrettyPrint (a.GetValue (i));
- if (i != top)
- p (", ");
- }
- p (" }");
- } else if (result is bool){
- if ((bool) result)
- p ("true");
- else
- p ("false");
- } else if (result is string){
- p (String.Format ("\"{0}\"", EscapeString ((string)result)));
- } else if (result is IDictionary){
- IDictionary dict = (IDictionary) result;
- int top = dict.Count, count = 0;
-
- p ("{");
- foreach (DictionaryEntry entry in dict){
- count++;
- p ("{ ");
- PrettyPrint (entry.Key);
- p (", ");
- PrettyPrint (entry.Value);
- if (count != top)
- p (" }, ");
- else
- p (" }");
- }
- p ("}");
- } else if (result is IEnumerable) {
- int i = 0;
- p ("{ ");
- foreach (object item in (IEnumerable) result) {
- if (i++ != 0)
- p (", ");
-
- PrettyPrint (item);
- }
- p (" }");
- } else {
- p (result.ToString ());
- }
- }
-
- delegate void HostSignature (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
- // other).
- //
- static ArrayList queued_fields = new ArrayList ();
-
- //static ArrayList types = new ArrayList ();
-
- static volatile bool invoking;
-
- static object ExecuteBlock (Class host, Undo undo)
- {
- RootContext.ResolveTree ();
- if (Report.Errors != 0){
- undo.ExecuteUndo ();
- return typeof (NoValueSet);
- }
-
- RootContext.PopulateTypes ();
- RootContext.DefineTypes ();
-
- if (Report.Errors != 0){
- undo.ExecuteUndo ();
- return typeof (NoValueSet);
- }
-
- TypeBuilder tb = null;
- MethodBuilder mb = null;
-
- if (host != null){
- tb = host.TypeBuilder;
- mb = null;
- foreach (MemberCore member in host.Methods){
- if (member.Name != "Host")
- continue;
-
- MethodOrOperator method = (MethodOrOperator) member;
- mb = method.MethodBuilder;
- break;
- }
-
- if (mb == null)
- throw new Exception ("Internal error: did not find the method builder for the generated method");
- }
-
- RootContext.EmitCode ();
- if (Report.Errors != 0)
- return typeof (NoValueSet);
-
- RootContext.CloseTypes ();
-
- if (Environment.GetEnvironmentVariable ("SAVE") != null)
- CodeGen.Save (current_debug_name, false);
-
- if (host == null)
- return typeof (NoValueSet);
-
- //
- // Unlike Mono, .NET requires that the MethodInfo is fetched, it cant
- // work from MethodBuilders. Retarded, I know.
- //
- Type tt = CodeGen.Assembly.Builder.GetType (tb.Name);
- MethodInfo mi = tt.GetMethod (mb.Name);
-
- // Pull the FieldInfos from the type, and keep track of them
- foreach (Field field in queued_fields){
- FieldInfo fi = tt.GetField (field.Name);
-
- FieldInfo old = (FieldInfo) fields [field.Name];
-
- // If a previous value was set, nullify it, so that we do
- // not leak memory
- if (old != null){
- if (!old.FieldType.IsValueType){
- try {
- old.SetValue (null, null);
- } catch {
- }
- }
- }
-
- fields [field.Name] = fi;
- }
- //types.Add (tb);
-
- queued_fields.Clear ();
-
- HostSignature invoker = (HostSignature) System.Delegate.CreateDelegate (typeof (HostSignature), mi);
- object retval = typeof (NoValueSet);
-
- try {
- invoking = true;
- invoker (ref retval);
- } catch (ThreadAbortException e){
- Thread.ResetAbort ();
- Console.WriteLine ("Interrupted!\n{0}", e);
- } finally {
- invoking = false;
- }
-
- // d.DynamicInvoke (new object [] { retval });
-
- return retval;
- }
-
- static public void LoadAliases (NamespaceEntry ns)
- {
- ns.Populate (using_alias_list, using_list);
- }
-
- //
- // Just a placeholder class, used as a sentinel to determine that the
- // generated code did not set a value
- class NoValueSet {
- }
-
- static public FieldInfo LookupField (string name)
- {
- FieldInfo fi = (FieldInfo) fields [name];
-
- return fi;
- }
-
- //
- // Puts the FieldBuilder into a queue of names that will be
- // registered. We can not register FieldBuilders directly
- // we need to fetch the FieldInfo after Reflection cooks the
- // types, or bad things happen (bad means: FieldBuilders behave
- // incorrectly across multiple assemblies, causing assignments to
- // invalid areas
- //
- // This also serves for the parser to register Field classes
- // that should be exposed as global variables
- //
- static public void QueueField (Field f)
- {
- queued_fields.Add (f);
- }
-
- static public void ShowUsing ()
- {
- foreach (object x in using_alias_list)
- Console.WriteLine ("using {0};", x);
-
- foreach (object x in using_list)
- Console.WriteLine ("using {0};", x);
- }
- }
-
- //
- // A local variable reference that will create a Field in a
- // Class with the resolved type. This is necessary so we can
- // support "var" as a field type in a class declaration.
- //
- // We allow LocalVariableReferece to do the heavy lifting, and
- // then we insert the field with the resolved type
- //
- public class LocalVariableReferenceWithClassSideEffect : LocalVariableReference {
- 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)
- {
- this.container = container;
- this.name = name;
- }
-
- public override bool Equals (object obj)
- {
- LocalVariableReferenceWithClassSideEffect lvr = obj as LocalVariableReferenceWithClassSideEffect;
- if (lvr == null)
- return false;
-
- if (lvr.name != name || lvr.container != container)
- return false;
-
- return base.Equals (obj);
- }
-
- public override int GetHashCode ()
- {
- return name.GetHashCode ();
- }
-
- override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
- {
- Expression ret = base.DoResolveLValue (ec, right_side);
- if (ret == null)
- return null;
-
- Field f = new Field (container, new TypeExpression (ret.Type, Location),
- Modifiers.PUBLIC | Modifiers.STATIC,
- name, null, Location);
- container.AddField (f);
- if (f.Define ())
- InteractiveShell.QueueField (f);
-
- return ret;
- }
- }
-
- /// <summary>
- /// A class used to assign values if the source expression is not void
- ///
- /// Used by the interactive shell to allow it to call this code to set
- /// the return value for an invocation.
- /// </summary>
- class OptionalAssign : SimpleAssign {
- public OptionalAssign (Expression t, Expression s, Location loc)
- : base (t, s, loc)
- {
- }
-
- public override Expression DoResolve (EmitContext ec)
- {
- CloneContext cc = new CloneContext ();
- Expression clone = source.Clone (cc);
-
- clone = clone.Resolve (ec);
- if (clone == null)
- return null;
-
- // This means its really a statement.
- if (clone.Type == TypeManager.void_type){
- source = source.Resolve (ec);
- target = null;
- type = TypeManager.void_type;
- eclass = ExprClass.Value;
- return this;
- }
-
- return base.DoResolve (ec);
- }
-
- public override void Emit (EmitContext ec)
- {
- if (target == null)
- source.Emit (ec);
- else
- base.Emit (ec);
- }
-
- public override void EmitStatement (EmitContext ec)
- {
- if (target == null)
- source.Emit (ec);
- else
- base.EmitStatement (ec);
- }
- }
-
- public class Undo {
- ArrayList undo_types;
-
- public Undo ()
- {
- undo_types = new ArrayList ();
- }
-
- public void AddTypeContainer (TypeContainer current_container, TypeContainer tc)
- {
- if (current_container == tc){
- Console.Error.WriteLine ("Internal error: inserting container into itself");
- return;
- }
-
- if (undo_types == null)
- undo_types = new ArrayList ();
- undo_types.Add (new Pair (current_container, tc));
- }
-
- public void ExecuteUndo ()
- {
- if (undo_types == null)
- return;
-
- foreach (Pair p in undo_types){
- TypeContainer current_container = (TypeContainer) p.First;
-
- current_container.RemoveTypeContainer ((TypeContainer) p.Second);
- }
- undo_types = null;
- }
- }
-
- /// <summary>
- /// The base class for every interaction line
- /// </summary>
- public class InteractiveBase {
- public static string Prompt = "csharp> ";
- public static string ContinuationPrompt = " > ";
-
- static string Quote (string s)
- {
- if (s.IndexOf ('"') != -1)
- s = s.Replace ("\"", "\\\"");
-
- return "\"" + s + "\"";
- }
-
- static public void ShowVars ()
- {
- foreach (DictionaryEntry de in InteractiveShell.fields){
- FieldInfo fi = InteractiveShell.LookupField ((string) de.Key);
- object value = null;
- bool error = false;
-
- try {
- if (value == null)
- value = "null";
- value = fi.GetValue (null);
- if (value is string)
- value = Quote ((string)value);
- } catch {
- error = true;
- }
-
- if (error)
- Console.WriteLine ("{0} {1} <error reading value>", TypeManager.CSharpName(fi.FieldType), de.Key);
- else
- Console.WriteLine ("{0} {1} = {2}", TypeManager.CSharpName(fi.FieldType), de.Key, value);
- }
- }
-
- static public void ShowUsing ()
- {
- InteractiveShell.ShowUsing ();
- }
-
- public delegate void Simple ();
-
- static public TimeSpan Time (Simple a)
- {
- DateTime start = DateTime.Now;
- a ();
- return DateTime.Now - start;
- }
-
-#if !SMCS_SOURCE
- static public void LoadPackage (string pkg)
- {
- if (pkg == null){
- Console.Error.WriteLine ("Invalid package specified");
- return;
- }
-
- string pkgout = Driver.GetPackageFlags (pkg, false);
- if (pkgout == null)
- return;
-
- string [] xargs = pkgout.Trim (new Char [] {' ', '\n', '\r', '\t'}).
- Split (new Char [] { ' ', '\t'});
-
- foreach (string s in xargs){
- if (s.StartsWith ("-r:") || s.StartsWith ("/r:") || s.StartsWith ("/reference:")){
- string lib = s.Substring (s.IndexOf (':')+1);
-
- //Console.WriteLine ("Loading: {0}", lib);
- Driver.LoadAssembly (lib, true);
- continue;
- }
- }
- }
-#endif
-
- static public void LoadAssembly (string assembly)
- {
- Driver.LoadAssembly (assembly, true);
- }
-
- static public string help {
- get {
- return "Static methods:\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" +
- " ShowUsing (); - Show active using decltions.\n" +
- " Prompt - The prompt used by the C# shell\n" +
- " ContinuationPrompt - The prompt for partial input\n" +
- " Time(() -> { }) - Times the specified code\n" +
- " quit;\n" +
- " help;\n";
- }
- }
-
- static public object quit {
- get {
- Environment.Exit (0);
- return null;
- }
- }
- }
-}
* Run a REPL on a socket (from Joe Shaw)
* Host a REPL on XSP (from Nat).
+* TODO
+
+ Clear struct fields inside the clearing code.
+
* Other ideas:
MD addin for "csharp"
doc-bootstrap.cs
enum.cs
ecore.cs
+eval.cs
expression.cs
flowanalysis.cs
generic.cs
nullable.cs
parameter.cs
pending.cs
-repl.cs
report.cs
rootcontext.cs
roottypes.cs
+2008-09-10 Miguel de Icaza <miguel@novell.com>
+
+ * Makefile (thisdir): Add csharp here.
+
2008-08-28 Zoltan Varga <vargaz@gmail.com>
* Makefile (net_2_0_SUBDIRS): Add gacutil.
net_2_0_bootstrap_SUBDIRS = resgen
net_2_0_SUBDIRS = \
- al corcompare mono-xsd wsdl compiler-tester monop xbuild resgen \
+ al corcompare csharp mono-xsd wsdl compiler-tester monop xbuild resgen \
mono-service mkbundle sgen security mconfig installutil nunitreport sqlsharp \
gacutil
net_2_1_raw_SUBDIRS = compiler-tester
net_2_1_SUBDIRS = compiler-tester tuner
-DIST_ONLY_SUBDIRS = xbuild sgen mconfig nunitreport sqlsharp
+DIST_ONLY_SUBDIRS = xbuild sgen mconfig nunitreport sqlsharp csharp
include ../build/rules.make
--- /dev/null
+2008-09-10 Miguel de Icaza <miguel@novell.com>
+
+ * Move the repl shell here.
+
--- /dev/null
+thisdir = tools/csharp
+SUBDIRS =
+include ../../build/rules.make
+
+LOCAL_MCS_FLAGS = -r:$(topdir)/class/lib/$(PROFILE)/gmcs.exe
+
+ifeq (net_2_0, $(PROFILE))
+PROGRAM = $(topdir)/class/lib/$(PROFILE)/csharp.exe
+else
+PROGRAM =
+endif
+
+DISTFILES = repl.txt
+
+CLEAN_FILES = csharp.exe *.mdb
+
+include ../../build/executable.make
\ No newline at end of file
--- /dev/null
+repl.cs
\ No newline at end of file
--- /dev/null
+//
+// getline.cs: A command line editor
+//
+// Authors:
+// Miguel de Icaza (miguel@novell.com)
+//
+// Copyright 2008 Novell, Inc.
+//
+// Dual-licensed under the terms of the MIT X11 license or the
+// Apache License 2.0
+//
+// USE -define:DEMO to build this as a standalone file and test it
+//
+// TODO:
+// Enter an error (a = 1); Notice how the prompt is in the wrong line
+// This is caused by Stderr not being tracked by System.Console.
+// Completion support
+// Why is Thread.Interrupt not working? Currently I resort to Abort which is too much.
+//
+// Limitations in System.Console:
+// Console needs SIGWINCH support of some sort
+// Console needs a way of updating its position after things have been written
+// behind its back (P/Invoke puts for example).
+// System.Console needs to get the DELETE character, and report accordingly.
+// Typing before the program start causes the cursor position to be wrong
+// This is caused by Console not reading all the available data
+// before sending the report-position sequence and reading it back.
+//
+#if NET_2_0 || NET_1_1
+#define IN_MCS_BUILD
+#endif
+
+// Only compile this code in the 2.0 profile, but not in the Moonlight one
+#if (IN_MCS_BUILD && NET_2_0 && !SMCS_SOURCE) || !IN_MCS_BUILD
+using System;
+using System.Text;
+using System.IO;
+using System.Threading;
+using System.Reflection;
+
+namespace Mono.Terminal {
+
+ public class LineEditor {
+ //static StreamWriter log;
+
+ // The text being edited.
+ StringBuilder text;
+
+ // The text as it is rendered (replaces (char)1 with ^A on display for example).
+ StringBuilder rendered_text;
+
+ // The prompt specified, and the prompt shown to the user.
+ string prompt;
+ string shown_prompt;
+
+ // The current cursor position, indexes into "text", for an index
+ // into rendered_text, use TextToRenderPos
+ int cursor;
+
+ // The row where we started displaying data.
+ int home_row;
+
+ // The maximum length that has been displayed on the screen
+ int max_rendered;
+
+ // If we are done editing, this breaks the interactive loop
+ bool done = false;
+
+ // The thread where the Editing started taking place
+ Thread edit_thread;
+
+ // Our object that tracks history
+ History history;
+
+ // The contents of the kill buffer (cut/paste in Emacs parlance)
+ string kill_buffer = "";
+
+ // The string being searched for
+ string search;
+ string last_search;
+
+ // whether we are searching (-1= reverse; 0 = no; 1 = forward)
+ int searching;
+
+ // The position where we found the match.
+ int match_at;
+
+ // Used to implement the Kill semantics (multiple Alt-Ds accumulate)
+ KeyHandler last_handler;
+
+ delegate void KeyHandler ();
+
+ struct Handler {
+ public ConsoleKeyInfo CKI;
+ public KeyHandler KeyHandler;
+
+ public Handler (ConsoleKey key, KeyHandler h)
+ {
+ CKI = new ConsoleKeyInfo ((char) 0, key, false, false, false);
+ KeyHandler = h;
+ }
+
+ public Handler (char c, KeyHandler h)
+ {
+ KeyHandler = h;
+ // Use the "Zoom" as a flag that we only have a character.
+ CKI = new ConsoleKeyInfo (c, ConsoleKey.Zoom, false, false, false);
+ }
+
+ public Handler (ConsoleKeyInfo cki, KeyHandler h)
+ {
+ CKI = cki;
+ KeyHandler = h;
+ }
+
+ public static Handler Control (char c, KeyHandler h)
+ {
+ return new Handler ((char) (c - 'A' + 1), h);
+ }
+
+ public static Handler Alt (char c, ConsoleKey k, KeyHandler h)
+ {
+ ConsoleKeyInfo cki = new ConsoleKeyInfo ((char) c, k, false, true, false);
+ return new Handler (cki, h);
+ }
+ }
+
+ static Handler [] handlers;
+
+ public LineEditor (string name) : this (name, 10) { }
+
+ public LineEditor (string name, int histsize)
+ {
+ handlers = new Handler [] {
+ new Handler (ConsoleKey.Home, CmdHome),
+ new Handler (ConsoleKey.End, CmdEnd),
+ new Handler (ConsoleKey.LeftArrow, CmdLeft),
+ new Handler (ConsoleKey.RightArrow, CmdRight),
+ new Handler (ConsoleKey.UpArrow, CmdHistoryPrev),
+ new Handler (ConsoleKey.DownArrow, CmdHistoryNext),
+ new Handler (ConsoleKey.Enter, CmdDone),
+ new Handler (ConsoleKey.Backspace, CmdBackspace),
+ new Handler (ConsoleKey.Delete, CmdDeleteChar),
+
+ // Emacs keys
+ Handler.Control ('A', CmdHome),
+ Handler.Control ('E', CmdEnd),
+ Handler.Control ('B', CmdLeft),
+ Handler.Control ('F', CmdRight),
+ Handler.Control ('P', CmdHistoryPrev),
+ Handler.Control ('N', CmdHistoryNext),
+ Handler.Control ('K', CmdKillToEOF),
+ Handler.Control ('Y', CmdYank),
+ Handler.Control ('D', CmdDeleteChar),
+ Handler.Control ('L', CmdRefresh),
+ Handler.Control ('R', CmdReverseSearch),
+ Handler.Control ('G', delegate {} ),
+ Handler.Alt ('B', ConsoleKey.B, CmdBackwardWord),
+ Handler.Alt ('F', ConsoleKey.F, CmdForwardWord),
+
+ Handler.Alt ('D', ConsoleKey.D, CmdDeleteWord),
+ Handler.Alt ((char) 8, ConsoleKey.Backspace, CmdDeleteBackword),
+
+ // DEBUG
+ Handler.Control ('T', CmdDebug),
+
+ // quote
+ Handler.Control ('Q', delegate { HandleChar (Console.ReadKey (true).KeyChar); })
+ };
+
+ rendered_text = new StringBuilder ();
+ text = new StringBuilder ();
+
+ history = new History (name, histsize);
+
+ //if (File.Exists ("log"))File.Delete ("log");
+ //log = File.CreateText ("log");
+ }
+
+ void CmdDebug ()
+ {
+ history.Dump ();
+ Console.WriteLine ();
+ Render ();
+ }
+
+ void Render ()
+ {
+ Console.Write (shown_prompt);
+ Console.Write (rendered_text);
+
+ int max = System.Math.Max (rendered_text.Length + shown_prompt.Length, max_rendered);
+
+ for (int i = rendered_text.Length + shown_prompt.Length; i < max_rendered; i++)
+ Console.Write (' ');
+ max_rendered = shown_prompt.Length + rendered_text.Length;
+
+ // Write one more to ensure that we always wrap around properly if we are at the
+ // end of a line.
+ Console.Write (' ');
+
+ UpdateHomeRow (max);
+ }
+
+ void UpdateHomeRow (int screenpos)
+ {
+ int lines = 1 + (screenpos / Console.WindowWidth);
+
+ home_row = Console.CursorTop - (lines - 1);
+ if (home_row < 0)
+ home_row = 0;
+ }
+
+
+ void RenderFrom (int pos)
+ {
+ int rpos = TextToRenderPos (pos);
+ int i;
+
+ for (i = rpos; i < rendered_text.Length; i++)
+ Console.Write (rendered_text [i]);
+
+ if ((shown_prompt.Length + rendered_text.Length) > max_rendered)
+ max_rendered = shown_prompt.Length + rendered_text.Length;
+ else {
+ int max_extra = max_rendered - shown_prompt.Length;
+ for (; i < max_extra; i++)
+ Console.Write (' ');
+ }
+ }
+
+ void ComputeRendered ()
+ {
+ rendered_text.Length = 0;
+
+ for (int i = 0; i < text.Length; i++){
+ int c = (int) text [i];
+ if (c < 26){
+ if (c == '\t')
+ rendered_text.Append (" ");
+ else {
+ rendered_text.Append ('^');
+ rendered_text.Append ((char) (c + (int) 'A' - 1));
+ }
+ } else
+ rendered_text.Append ((char)c);
+ }
+ }
+
+ int TextToRenderPos (int pos)
+ {
+ int p = 0;
+
+ for (int i = 0; i < pos; i++){
+ int c = (int) text [i];
+ if (c < 26){
+ if (c == 9)
+ p += 4;
+ else
+ p += 2;
+ } else
+ p++;
+ }
+ return p;
+ }
+
+ int TextToScreenPos (int pos)
+ {
+ return shown_prompt.Length + TextToRenderPos (pos);
+ }
+
+ string Prompt {
+ get { return prompt; }
+ set { prompt = value; }
+ }
+
+ int LineCount {
+ get {
+ return (shown_prompt.Length + rendered_text.Length)/Console.WindowWidth;
+ }
+ }
+
+ void ForceCursor (int newpos)
+ {
+ cursor = newpos;
+
+ int actual_pos = shown_prompt.Length + TextToRenderPos (cursor);
+ int row = home_row + (actual_pos/Console.WindowWidth);
+ int col = actual_pos % Console.WindowWidth;
+
+ if (row >= Console.BufferHeight)
+ row = Console.BufferHeight-1;
+ Console.SetCursorPosition (col, row);
+
+ //log.WriteLine ("Going to cursor={0} row={1} col={2} actual={3} prompt={4} ttr={5} old={6}", newpos, row, col, actual_pos, prompt.Length, TextToRenderPos (cursor), cursor);
+ //log.Flush ();
+ }
+
+ void UpdateCursor (int newpos)
+ {
+ if (cursor == newpos)
+ return;
+
+ ForceCursor (newpos);
+ }
+
+ void InsertChar (char c)
+ {
+ int prev_lines = LineCount;
+ text = text.Insert (cursor, c);
+ ComputeRendered ();
+ if (prev_lines != LineCount){
+
+ Console.SetCursorPosition (0, home_row);
+ Render ();
+ ForceCursor (++cursor);
+ } else {
+ RenderFrom (cursor);
+ ForceCursor (++cursor);
+ UpdateHomeRow (TextToScreenPos (cursor));
+ }
+ }
+
+ //
+ // Commands
+ //
+ void CmdDone ()
+ {
+ done = true;
+ }
+
+ void CmdHome ()
+ {
+ UpdateCursor (0);
+ }
+
+ void CmdEnd ()
+ {
+ UpdateCursor (text.Length);
+ }
+
+ void CmdLeft ()
+ {
+ if (cursor == 0)
+ return;
+
+ UpdateCursor (cursor-1);
+ }
+
+ void CmdBackwardWord ()
+ {
+ int p = WordBackward (cursor);
+ if (p == -1)
+ return;
+ UpdateCursor (p);
+ }
+
+ void CmdForwardWord ()
+ {
+ int p = WordForward (cursor);
+ if (p == -1)
+ return;
+ UpdateCursor (p);
+ }
+
+ void CmdRight ()
+ {
+ if (cursor == text.Length)
+ return;
+
+ UpdateCursor (cursor+1);
+ }
+
+ void RenderAfter (int p)
+ {
+ ForceCursor (p);
+ RenderFrom (p);
+ ForceCursor (cursor);
+ }
+
+ void CmdBackspace ()
+ {
+ if (cursor == 0)
+ return;
+
+ text.Remove (--cursor, 1);
+ ComputeRendered ();
+ RenderAfter (cursor);
+ }
+
+ void CmdDeleteChar ()
+ {
+ // If there is no input, this behaves like EOF
+ if (text.Length == 0){
+ done = true;
+ text = null;
+ Console.WriteLine ();
+ return;
+ }
+
+ if (cursor == text.Length)
+ return;
+ text.Remove (cursor, 1);
+ ComputeRendered ();
+ RenderAfter (cursor);
+ }
+
+ int WordForward (int p)
+ {
+ if (p >= text.Length)
+ return -1;
+
+ int i = p;
+ if (Char.IsPunctuation (text [p]) || Char.IsWhiteSpace (text[p])){
+ for (; i < text.Length; i++){
+ if (Char.IsLetterOrDigit (text [i]))
+ break;
+ }
+ for (; i < text.Length; i++){
+ if (!Char.IsLetterOrDigit (text [i]))
+ break;
+ }
+ } else {
+ for (; i < text.Length; i++){
+ if (!Char.IsLetterOrDigit (text [i]))
+ break;
+ }
+ }
+ if (i != p)
+ return i;
+ return -1;
+ }
+
+ int WordBackward (int p)
+ {
+ if (p == 0)
+ return -1;
+
+ int i = p-1;
+ if (i == 0)
+ return 0;
+
+ if (Char.IsPunctuation (text [i]) || Char.IsSymbol (text [i]) || Char.IsWhiteSpace (text[i])){
+ for (; i >= 0; i--){
+ if (Char.IsLetterOrDigit (text [i]))
+ break;
+ }
+ for (; i >= 0; i--){
+ if (!Char.IsLetterOrDigit (text[i]))
+ break;
+ }
+ } else {
+ for (; i >= 0; i--){
+ if (!Char.IsLetterOrDigit (text [i]))
+ break;
+ }
+ }
+ i++;
+
+ if (i != p)
+ return i;
+
+ return -1;
+ }
+
+ void CmdDeleteWord ()
+ {
+ int pos = WordForward (cursor);
+
+ if (pos == -1)
+ return;
+
+ string k = text.ToString (cursor, pos-cursor);
+
+ if (last_handler == CmdDeleteWord)
+ kill_buffer = kill_buffer + k;
+ else
+ kill_buffer = k;
+
+ text.Remove (cursor, pos-cursor);
+ ComputeRendered ();
+ RenderAfter (cursor);
+ }
+
+ void CmdDeleteBackword ()
+ {
+ int pos = WordBackward (cursor);
+ if (pos == -1)
+ return;
+
+ string k = text.ToString (pos, cursor-pos);
+
+ if (last_handler == CmdDeleteBackword)
+ kill_buffer = k + kill_buffer;
+ else
+ kill_buffer = k;
+
+ text.Remove (pos, cursor-pos);
+ ComputeRendered ();
+ RenderAfter (pos);
+ }
+
+ //
+ // Adds the current line to the history if needed
+ //
+ void HistoryUpdateLine ()
+ {
+ history.Update (text.ToString ());
+ }
+
+ void CmdHistoryPrev ()
+ {
+ if (!history.PreviousAvailable ())
+ return;
+
+ HistoryUpdateLine ();
+
+ SetText (history.Previous ());
+ }
+
+ void CmdHistoryNext ()
+ {
+ if (!history.NextAvailable())
+ return;
+
+ history.Update (text.ToString ());
+ SetText (history.Next ());
+
+ }
+
+ void CmdKillToEOF ()
+ {
+ kill_buffer = text.ToString (cursor, text.Length-cursor);
+ text.Length = cursor;
+ ComputeRendered ();
+ RenderAfter (cursor);
+ }
+
+ void CmdYank ()
+ {
+ int prev_lines = LineCount;
+ text.Insert (cursor, kill_buffer);
+ ComputeRendered ();
+ if (prev_lines != LineCount){
+ Console.SetCursorPosition (0, home_row);
+ Render ();
+ cursor += kill_buffer.Length;
+ ForceCursor (cursor);
+ } else {
+ RenderFrom (cursor);
+ cursor += kill_buffer.Length;
+ ForceCursor (cursor);
+ UpdateHomeRow (TextToScreenPos (cursor));
+ }
+ }
+
+ void SetSearchPrompt (string s)
+ {
+ SetPrompt ("(reverse-i-search)`" + s + "': ");
+ }
+
+ void ReverseSearch ()
+ {
+ int p;
+
+ if (cursor == text.Length){
+ // The cursor is at the end of the string
+
+ p = text.ToString ().LastIndexOf (search);
+ if (p != -1){
+ match_at = p;
+ cursor = p;
+ ForceCursor (cursor);
+ return;
+ }
+ } else {
+ // The cursor is somewhere in the middle of the string
+ int start = (cursor == match_at) ? cursor - 1 : cursor;
+ if (start != -1){
+ p = text.ToString ().LastIndexOf (search, start);
+ if (p != -1){
+ match_at = p;
+ cursor = p;
+ ForceCursor (cursor);
+ return;
+ }
+ }
+ }
+
+ // Need to search backwards in history
+ HistoryUpdateLine ();
+ string s = history.SearchBackward (search);
+ if (s != null){
+ match_at = -1;
+ SetText (s);
+ ReverseSearch ();
+ }
+ }
+
+ void CmdReverseSearch ()
+ {
+ if (searching == 0){
+ match_at = -1;
+ last_search = search;
+ searching = -1;
+ search = "";
+ SetSearchPrompt ("");
+ } else {
+ if (search == ""){
+ if (last_search != "" && last_search != null){
+ search = last_search;
+ SetSearchPrompt (search);
+
+ ReverseSearch ();
+ }
+ return;
+ }
+ ReverseSearch ();
+ }
+ }
+
+ void SearchAppend (char c)
+ {
+ search = search + c;
+ SetSearchPrompt (search);
+
+ //
+ // If the new typed data still matches the current text, stay here
+ //
+ if (cursor < text.Length){
+ string r = text.ToString (cursor, text.Length - cursor);
+ if (r.StartsWith (search))
+ return;
+ }
+
+ ReverseSearch ();
+ }
+
+ void CmdRefresh ()
+ {
+ Console.Clear ();
+ max_rendered = 0;
+ Render ();
+ ForceCursor (cursor);
+ }
+
+ void InterruptEdit (object sender, ConsoleCancelEventArgs a)
+ {
+ // Do not abort our program:
+ a.Cancel = true;
+
+ // Interrupt the editor
+ edit_thread.Abort();
+ }
+
+ void HandleChar (char c)
+ {
+ if (searching != 0)
+ SearchAppend (c);
+ else
+ InsertChar (c);
+ }
+
+ void EditLoop ()
+ {
+ ConsoleKeyInfo cki;
+
+ while (!done){
+ cki = Console.ReadKey (true);
+
+ bool handled = false;
+ foreach (Handler handler in handlers){
+ ConsoleKeyInfo t = handler.CKI;
+
+ if (t.Key == cki.Key && t.Modifiers == cki.Modifiers){
+ handled = true;
+ handler.KeyHandler ();
+ last_handler = handler.KeyHandler;
+ break;
+ } else if (t.KeyChar == cki.KeyChar && t.Key == ConsoleKey.Zoom){
+ handled = true;
+ handler.KeyHandler ();
+ last_handler = handler.KeyHandler;
+ break;
+ }
+ }
+ if (handled){
+ if (searching != 0){
+ if (last_handler != CmdReverseSearch){
+ searching = 0;
+ SetPrompt (prompt);
+ }
+ }
+ continue;
+ }
+
+ if (cki.KeyChar != (char) 0)
+ HandleChar (cki.KeyChar);
+ }
+ }
+
+ void InitText (string initial)
+ {
+ text = new StringBuilder (initial);
+ ComputeRendered ();
+ cursor = text.Length;
+ Render ();
+ ForceCursor (cursor);
+ }
+
+ void SetText (string newtext)
+ {
+ Console.SetCursorPosition (0, home_row);
+ InitText (newtext);
+ }
+
+ void SetPrompt (string newprompt)
+ {
+ shown_prompt = newprompt;
+ Console.SetCursorPosition (0, home_row);
+ Render ();
+ ForceCursor (cursor);
+ }
+
+ public string Edit (string prompt, string initial)
+ {
+ edit_thread = Thread.CurrentThread;
+ searching = 0;
+ Console.CancelKeyPress += InterruptEdit;
+
+ done = false;
+ history.CursorToEnd ();
+ max_rendered = 0;
+
+ Prompt = prompt;
+ shown_prompt = prompt;
+ InitText (initial);
+ history.Append (initial);
+
+ do {
+ try {
+ EditLoop ();
+ } catch (ThreadAbortException){
+ searching = 0;
+ Thread.ResetAbort ();
+ Console.WriteLine ();
+ SetPrompt (prompt);
+ SetText ("");
+ }
+ } while (!done);
+ Console.WriteLine ();
+
+ Console.CancelKeyPress -= InterruptEdit;
+
+ if (text == null){
+ history.Close ();
+ return null;
+ }
+
+ string result = text.ToString ();
+ if (result != "")
+ history.Accept (result);
+ else
+ history.RemoveLast ();
+
+ return result;
+ }
+
+ //
+ // Emulates the bash-like behavior, where edits done to the
+ // history are recorded
+ //
+ class History {
+ string [] history;
+ int head, tail;
+ int cursor, count;
+ string histfile;
+
+ public History (string app, int size)
+ {
+ if (size < 1)
+ throw new ArgumentException ("size");
+
+ if (app != null){
+ string dir = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData);
+ //Console.WriteLine (dir);
+ if (!Directory.Exists (dir)){
+ try {
+ Directory.CreateDirectory (dir);
+ } catch {
+ app = null;
+ }
+ }
+ if (app != null)
+ histfile = Path.Combine (dir, app) + ".history";
+ }
+
+ history = new string [size];
+ head = tail = cursor = 0;
+
+ if (File.Exists (histfile)){
+ using (StreamReader sr = File.OpenText (histfile)){
+ string line;
+
+ while ((line = sr.ReadLine ()) != null){
+ if (line != "")
+ Append (line);
+ }
+ }
+ }
+ }
+
+ public void Close ()
+ {
+ if (histfile == null)
+ return;
+
+ try {
+ using (StreamWriter sw = File.CreateText (histfile)){
+ int start = (count == history.Length) ? head : tail;
+ for (int i = start; i < start+count; i++){
+ int p = i % history.Length;
+ sw.WriteLine (history [p]);
+ }
+ }
+ } catch {
+ // ignore
+ }
+ }
+
+ //
+ // Appends a value to the history
+ //
+ public void Append (string s)
+ {
+ //Console.WriteLine ("APPENDING {0} {1}", s, Environment.StackTrace);
+ history [head] = s;
+ head = (head+1) % history.Length;
+ if (head == tail)
+ tail = (tail+1 % history.Length);
+ if (count != history.Length)
+ count++;
+ }
+
+ //
+ // Updates the current cursor location with the string,
+ // to support editing of history items. For the current
+ // line to participate, an Append must be done before.
+ //
+ public void Update (string s)
+ {
+ history [cursor] = s;
+ }
+
+ public void RemoveLast ()
+ {
+ head = head-1;
+ if (head < 0)
+ head = history.Length-1;
+ }
+
+ public void Accept (string s)
+ {
+ int t = head-1;
+ if (t < 0)
+ t = history.Length-1;
+
+ history [t] = s;
+ }
+
+ public bool PreviousAvailable ()
+ {
+ //Console.WriteLine ("h={0} t={1} cursor={2}", head, tail, cursor);
+ if (count == 0 || cursor == tail)
+ return false;
+
+ return true;
+ }
+
+ public bool NextAvailable ()
+ {
+ int next = (cursor + 1) % history.Length;
+ if (count == 0 || next > head)
+ return false;
+
+ return true;
+ }
+
+
+ //
+ // Returns: a string with the previous line contents, or
+ // nul if there is no data in the history to move to.
+ //
+ public string Previous ()
+ {
+ if (!PreviousAvailable ())
+ return null;
+
+ cursor--;
+ if (cursor < 0)
+ cursor = history.Length - 1;
+
+ return history [cursor];
+ }
+
+ public string Next ()
+ {
+ if (!NextAvailable ())
+ return null;
+
+ cursor = (cursor + 1) % history.Length;
+ return history [cursor];
+ }
+
+ public void CursorToEnd ()
+ {
+ if (head == tail)
+ return;
+
+ cursor = head;
+ }
+
+ public void Dump ()
+ {
+ Console.WriteLine ("Head={0} Tail={1} Cursor={2}", head, tail, cursor);
+ for (int i = 0; i < history.Length;i++){
+ Console.WriteLine (" {0} {1}: {2}", i == cursor ? "==>" : " ", i, history[i]);
+ }
+ //log.Flush ();
+ }
+
+ public string SearchBackward (string term)
+ {
+ for (int i = 1; i < count; i++){
+ int slot = cursor-i;
+ if (slot < 0)
+ slot = history.Length-1;
+ if (history [slot] != null && history [slot].IndexOf (term) != -1){
+ cursor = slot;
+ return history [slot];
+ }
+
+ // Will the next hit tail?
+ slot--;
+ if (slot < 0)
+ slot = history.Length-1;
+ if (slot == tail)
+ break;
+ }
+
+ return null;
+ }
+
+ }
+ }
+
+#if DEMO
+ class Demo {
+ static void Main ()
+ {
+ LineEditor le = new LineEditor (null);
+ string s;
+
+ while ((s = le.Edit ("shell> ", "")) != null){
+ Console.WriteLine ("----> [{0}]", s);
+ }
+ }
+ }
+#endif
+}
+#endif
--- /dev/null
+//
+// repl.cs: Support for using the compiler in interactive mode (read-eval-print loop)
+//
+// Authors:
+// Miguel de Icaza (miguel@gnome.org)
+//
+// Dual licensed under the terms of the MIT X11 or GNU GPL
+//
+// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
+// Copyright 2004, 2005, 2006, 2007, 2008 Novell, Inc
+//
+//
+// TODO:
+// Do not print results in Evaluate, do that elsewhere in preparation for Eval refactoring.
+// Driver.PartialReset should not reset the coretypes, nor the optional types, to avoid
+// computing that on every call.
+//
+using System;
+using System.IO;
+using System.Text;
+using System.Globalization;
+using System.Collections;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Threading;
+using Mono.CSharp;
+
+namespace Mono {
+
+ public static class CSharpShell {
+ static bool isatty = true;
+
+ static Mono.Terminal.LineEditor editor;
+ static bool dumb;
+ static Thread invoke_thread;
+
+ static void ConsoleInterrupt (object sender, ConsoleCancelEventArgs a)
+ {
+ // Do not about our program
+ a.Cancel = true;
+
+ Mono.CSharp.CSharpEvaluator.Interrupt ();
+ }
+
+ static void SetupConsole ()
+ {
+ string term = Environment.GetEnvironmentVariable ("TERM");
+ dumb = term == "dumb" || term == null || isatty == false;
+
+ editor = new Mono.Terminal.LineEditor ("csharp", 300);
+ Console.CancelKeyPress += ConsoleInterrupt;
+ }
+
+ static string GetLine (bool primary)
+ {
+ string prompt = primary ? InteractiveBase.Prompt : InteractiveBase.ContinuationPrompt;
+
+ if (dumb){
+ if (isatty)
+ Console.Write (prompt);
+
+ return Console.ReadLine ();
+ } else {
+ return editor.Edit (prompt, "");
+ }
+ }
+
+ delegate string ReadLiner (bool primary);
+
+ static void InitializeUsing ()
+ {
+ Evaluate ("using System; using System.Linq; using System.Collections.Generic; using System.Collections;");
+ }
+
+ static void InitTerminal ()
+ {
+ isatty = UnixUtils.isatty (0) && UnixUtils.isatty (1);
+
+ // Work around, since Console is not accounting for
+ // cursor position when writing to Stderr. It also
+ // has the undesirable side effect of making
+ // errors plain, with no coloring.
+ Report.Stderr = Console.Out;
+ SetupConsole ();
+
+ if (isatty)
+ Console.WriteLine ("Mono C# Shell, type \"help;\" for help\n\nEnter statements below.");
+
+ }
+
+ static void LoadStartupFiles ()
+ {
+ string dir = Path.Combine (
+ Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData),
+ "csharp");
+ if (!Directory.Exists (dir))
+ return;
+
+ foreach (string file in Directory.GetFiles (dir)){
+ string l = file.ToLower ();
+
+ if (l.EndsWith (".cs")){
+ try {
+ using (StreamReader r = File.OpenText (file)){
+ ReadEvalPrintLoopWith (p => r.ReadLine ());
+ }
+ } catch {
+ }
+ } else if (l.EndsWith (".dll")){
+ CSharpEvaluator.LoadAssembly (file);
+ }
+ }
+ }
+
+ static void ReadEvalPrintLoopWith (ReadLiner readline)
+ {
+ string expr = null;
+ while (true){
+ string input = readline (expr == null);
+ if (input == null)
+ return;
+
+ if (input == "")
+ continue;
+
+ expr = expr == null ? input : expr + "\n" + input;
+
+ expr = Evaluate (expr);
+ }
+ }
+
+ static public int ReadEvalPrintLoop ()
+ {
+ InitTerminal ();
+
+ InitializeUsing ();
+
+ LoadStartupFiles ();
+ ReadEvalPrintLoopWith (GetLine);
+
+ return 0;
+ }
+
+ static string Evaluate (string input)
+ {
+ bool result_set;
+ object result;
+
+ try {
+ input = CSharpEvaluator.Evaluate (input, out result, out result_set);
+
+ if (result_set){
+ PrettyPrint (result);
+ Console.WriteLine ();
+ }
+ } catch (Exception e){
+ Console.WriteLine (e);
+ }
+
+ return input;
+ }
+
+ static void p (string s)
+ {
+ Console.Write (s);
+ }
+
+ static string EscapeString (string s)
+ {
+ return s.Replace ("\"", "\\\"");
+ }
+
+ static void PrettyPrint (object result)
+ {
+ if (result == null){
+ p ("null");
+ return;
+ }
+
+ if (result is Array){
+ Array a = (Array) result;
+
+ p ("{ ");
+ int top = a.GetUpperBound (0);
+ for (int i = a.GetLowerBound (0); i <= top; i++){
+ PrettyPrint (a.GetValue (i));
+ if (i != top)
+ p (", ");
+ }
+ p (" }");
+ } else if (result is bool){
+ if ((bool) result)
+ p ("true");
+ else
+ p ("false");
+ } else if (result is string){
+ p (String.Format ("\"{0}\"", EscapeString ((string)result)));
+ } else if (result is IDictionary){
+ IDictionary dict = (IDictionary) result;
+ int top = dict.Count, count = 0;
+
+ p ("{");
+ foreach (DictionaryEntry entry in dict){
+ count++;
+ p ("{ ");
+ PrettyPrint (entry.Key);
+ p (", ");
+ PrettyPrint (entry.Value);
+ if (count != top)
+ p (" }, ");
+ else
+ p (" }");
+ }
+ p ("}");
+ } else if (result is IEnumerable) {
+ int i = 0;
+ p ("{ ");
+ foreach (object item in (IEnumerable) result) {
+ if (i++ != 0)
+ p (", ");
+
+ PrettyPrint (item);
+ }
+ p (" }");
+ } else {
+ p (result.ToString ());
+ }
+ }
+
+ static int Main (string [] args)
+ {
+ try {
+ CSharpEvaluator.Init (args);
+ } catch {
+ return 1;
+ }
+
+ return ReadEvalPrintLoop ();
+ }
+ }
+
+}
--- /dev/null
+Things to do for the REPL support in MCS:
+
+Documentation for the REPL mode for MCS can be found here:
+
+ http://mono-project.com/CsharpRepl
+
+* Embedding API
+
+ * Booting the compiler without Main ()
+ * Expose LoadAssembly/LoadPackage
+ * Register fields?
+ * Register a lookup function for fields?
+ * Register classes to expose to REPL
+
+* Embedded Library
+
+ * Run a REPL on a socket (from Joe Shaw)
+ * Host a REPL on XSP (from Nat).
+
+* TODO
+
+ Clear struct fields inside the clearing code.
+
+* Other ideas:
+
+ MD addin for "csharp"
+
+* Mix statements with other top-level declarations.
+
+csharp> class Y {static void Main () {Console.WriteLine ("Foo"); }}
+csharp> typeof (Y);
+Y
+csharp> Y.Main ();
+Exception caught by the compiler while compiling:
+ Block that caused the problem begin at: Internal(1,1):
+ Block being compiled: [<interactive>(1,2):,<interactive>(1,11):]
+System.NotSupportedException: The invoked member is not supported in a dynamic module.
+Internal compiler error at Internal(1,1):: exception caught while emitting MethodBuilder [Class2::Host]
+System.NotSupportedException: The invoked member is not supported in a dynamic module.
+ at System.Reflection.Emit.AssemblyBuilder.get_Location () [0x00000] in /second/home/cvs/mcs/class/corlib/System.Reflection.Emit/AssemblyBuilder.cs:214
+ at Mono.CSharp.Report.SymbolRelatedToPreviousError (System.Reflection.MemberInfo mi) [0x00036] in /second/home/cvs/mcs/mcs/report.cs:664
+ at Mono.CSharp.Expression.Error_MemberLookupFailed (System.Type container_type, System.Type qualifier_type, System.Type queried_type, System.String name, System.String class_name, MemberTypes mt, BindingFlags bf) [0x00121] in /second/home/cvs/mcs/mcs/ecore.cs:857
+ at Mono.CSharp.MemberAccess.DoResolve (Mono.CSharp.EmitContext ec, Mono.CSharp.Expression right_side) [0x00230] in /second/home/cvs/mcs/mcs/expression.cs:7426
+ at Mono.CSharp.MemberAccess.DoResolve (Mono.CSharp.EmitContext ec) [0x00000] in /second/home/cvs/mcs/mcs/expression.cs:7494
+ at Mono.CSharp.Expression.Resolve (Mono.CSharp.EmitContext ec, ResolveFlags flags) [0x00075] in /second/home/cvs/mcs/mcs/ecore.cs:479
+ at Mono.CSharp.Invocation.DoResolve (Mono.CSharp.EmitContext ec) [0x0000d] in /second/home/cvs/mcs/mcs/expression.cs:4725
+ at Mono.CSharp.Expression.Resolve (Mono.CSharp.EmitContext ec, ResolveFlags flags) [0x00075] in /second/home/cvs/mcs/mcs/ecore.cs:479
+ at Mono.CSharp.Expression.Resolve (Mono.CSharp.EmitContext ec) [0x00000] in /second/home/cvs/mcs/mcs/ecore.cs:506
+ at Mono.CSharp.OptionalAssign.DoResolve (Mono.CSharp.EmitContext ec) [0x00013] in /second/home/cvs/mcs/mcs/repl.cs:681
+ at Mono.CSharp.Expression.Resolve (Mono.CSharp.EmitContext ec, ResolveFlags flags) [0x00075] in /second/home/cvs/mcs/mcs/ecore.cs:479
+ at Mono.CSharp.Expression.Resolve (Mono.CSharp.EmitContext ec) [0x00000] in /second/home/cvs/mcs/mcs/ecore.cs:506
+ at Mono.CSharp.ExpressionStatement.ResolveStatement (Mono.CSharp.EmitContext ec) [0x00000] in /second/home/cvs/mcs/mcs/ecore.cs:1307
+ at Mono.CSharp.StatementExpression.Resolve (Mono.CSharp.EmitContext ec) [0x0000b] in /second/home/cvs/mcs/mcs/statement.cs:743
+ at Mono.CSharp.Block.Resolve (Mono.CSharp.EmitContext ec) [0x000f0] in /second/home/cvs/mcs/mcs/statement.cs:2254
+ at Mono.CSharp.ExplicitBlock.Resolve (Mono.CSharp.EmitContext ec) [0x00000] in /second/home/cvs/mcs/mcs/statement.cs:2550
+ at Mono.CSharp.EmitContext.ResolveTopBlock (Mono.CSharp.EmitContext anonymous_method_host, Mono.CSharp.ToplevelBlock block, Mono.CSharp.Parameters ip, IMethodData md, System.Boolean& unreachable) [0x00087] in /second/home/cvs/mcs/mcs/codegen.cs:796
+csharp>
+
+* Another one:
+
+csharp> class X { X (){ Console.WriteLine ("Called"); } }
+csharp> new X ();
+Exception caught by the compiler while compiling:
+ Block that caused the problem begin at: Internal(1,1):
+ Block being compiled: [<interactive>(1,2):,<interactive>(1,10):]
+System.NotSupportedException: The invoked member is not supported in a dynamic module.
+Internal compiler error at Internal(1,1):: exception caught while emitting MethodBuilder [Class0::Host]
+System.NotSupportedException: The invoked member is not supported in a dynamic module.
+ at System.Reflection.Emit.AssemblyBuilder.get_Location () [0x00000] in /second/home/cvs/mcs/class/corlib/System.Reflection.Emit/AssemblyBuilder.cs:214
+ at Mono.CSharp.Report.SymbolRelatedToPreviousError (System.Reflection.MemberInfo mi) [0x00036] in /second/home/cvs/mcs/mcs/report.cs:664
+ at Mono.CSharp.Expression.Error_MemberLookupFailed (System.Type container_type, System.Type qualifier_type, System.Type queried_type, System.String name, System.String class_name, MemberTypes mt, BindingFlags bf) [0x00121] in /second/home/cvs/mcs/mcs/ecore.cs:857
+ at Mono.CSharp.Expression.MemberLookupFinal (Mono.CSharp.EmitContext ec, System.Type qualifier_type, System.Type queried_type, System.String name, MemberTypes mt, BindingFlags bf, Location loc) [0x0002f] in /second/home/cvs/mcs/mcs/ecore.cs:804
+ at Mono.CSharp.New.DoResolve (Mono.CSharp.EmitContext ec) [0x002ad] in /second/home/cvs/mcs/mcs/expression.cs:5486
+ at Mono.CSharp.Expression.Resolve (Mono.CSharp.EmitContext ec, ResolveFlags flags) [0x00075] in /second/home/cvs/mcs/mcs/ecore.cs:479
+ at Mono.CSharp.Expression.Resolve (Mono.CSharp.EmitContext ec) [0x00000] in /second/home/cvs/mcs/mcs/ecore.cs:506
+ at Mono.CSharp.OptionalAssign.DoResolve (Mono.CSharp.EmitContext ec) [0x00013] in /second/home/cvs/mcs/mcs/repl.cs:687
+ at Mono.CSharp.Expression.Resolve (Mono.CSharp.EmitContext ec, ResolveFlags flags) [0x00075] in /second/home/cvs/mcs/mcs/ecore.cs:479
+ at Mono.CSharp.Expression.Resolve (Mono.CSharp.EmitContext ec) [0x00000] in /second/home/cvs/mcs/mcs/ecore.cs:506
+ at Mono.CSharp.ExpressionStatement.ResolveStatement (Mono.CSharp.EmitContext ec) [0x00000] in /second/home/cvs/mcs/mcs/ecore.cs:1307
+ at Mono.CSharp.StatementExpression.Resolve (Mono.CSharp.EmitContext ec) [0x0000b] in /second/home/cvs/mcs/mcs/statement.cs:743
+ at Mono.CSharp.Block.Resolve (Mono.CSharp.EmitContext ec) [0x000f0] in /second/home/cvs/mcs/mcs/statement.cs:2254
+ at Mono.CSharp.ExplicitBlock.Resolve (Mono.CSharp.EmitContext ec) [0x00000] in /second/home/cvs/mcs/mcs/statement.cs:2550
+ at Mono.CSharp.EmitContext.ResolveTopBlock (Mono.CSharp.EmitContext anonymous_method_host, Mono.CSharp.ToplevelBlock block, Mono.CSharp.Parameters ip, IMethodData md, System.Boolean& unreachable) [0x00087] in /second/home/cvs/mcs/mcs/codegen.cs:796
+csharp>
+
+* Important: we need to replace TypeBuidlers with Types after things
+ have been emitted, or stuff like this happens:
+
+csharp> public class y {}
+csharp> typeof (y);
+Class1
+
+
+* Clearing data
+
+ TODO: when clearing data for variables that have been overwritten
+ we need to check for structs and clear all the fields that contain
+ reference types.
+
+* DEBATABLE: Implement auto-insert-semicolon
+
+ This is easy to implement, just retry the parse with a
+ semicolon, the question is whether this is a good idea to do
+ in the first place or not.
+
+* Tab Completion
+
+ Implement tab completion on names, variables and type lookups.
+
+ This could be implemented by having the TAB key force the
+ expression to be evaluated with a special COMPLETE token
+ at the end.
+
+ Then the various productions (one by one) would have to
+ add support for COMPLETE, and having Resolve methods be
+ aware of this.
+