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