using Nemerle.Collections.List; using Nemerle.Compiler.Parsetree; using Nemerle.Text; using Nemerle.Utility.NString; using SC = System.Console; using System; using System.IO; using System.Text; using System.Text.RegularExpressions.Regex; using Interp = Nemerle.Evaluation.Interpreter.Internals; [assembly: System.Reflection.AssemblyTitle("Nemerle Interactive Shell")] [assembly: System.Reflection.AssemblyDescription("Nemerle (http://nemerle.org) Interactive Shell")] [assembly: System.Reflection.AssemblyCompany("University of Wroclaw")] [assembly: System.Reflection.AssemblyProduct("Nemerle Interactive Shell")] [assembly: System.Reflection.AssemblyCopyright("Copyright @ University of Wroclaw 2005-2008")] [assembly: Nemerle.Utility.AssemblyVersionFromSVN("0.9.4.SVN")] namespace Nemerle.Evaluation.Interpreter { module MainClass { public Main (args : array [string]) : void { // Check readline.n for details. HacksFramework.Run (); def profile = Path.Combine (Environment.GetFolderPath (Environment. SpecialFolder.Personal), ".nemerlish_profile"); // create nemerlish_profile file if it does not exist. unless (File.Exists (profile)) { def stream = File.Create (profile); def sw = StreamWriter (stream); sw.Write ("/* Open the namespace holding nemerlish-related properties. */\n"); sw.Write ("using Nemerle.Evaluation.Interpreter.Internals;\n"); sw.Close (); stream.Close (); } def histfile = Path.Combine (Environment.GetFolderPath (Environment. SpecialFolder.Personal), ".nemerlish_history"); def ce = Console.Error; def print_welcome () { SC.WriteLine ("Welcome to Nemerle interpreter " + $"(using ncc $(Interp.nccversion)).\n"); SC.WriteLine ("Please enter expressions appended with \";;\"."); SC.WriteLine ("Type \"Help;;\" for more info.\n"); } print_welcome (); // Read code for evaluation from a file. def fromfile (filename) { def sr = File.OpenText (filename); def readfile (acc) { def l = sr.ReadLine (); if (l != null) if (acc == "") readfile (l) else readfile (acc + " " + l) else acc } def res = readfile (""); sr.Close (); res } // Read the contents of all files specified after "-f" at the // command-line. FIXME: Warn about problems, instead of ignoring // invalid input silently. def initialcode = if (args.Length > 1) match (FromArray (args)) { | "-f" :: filenames => FoldRight (profile :: filenames, "", fun (file, acc) { try { acc + fromfile (file) } catch { | _ => acc } }) | _ => "" } else try { fromfile (profile) } catch { | _ => "" } def e = Nemerle.Evaluation.Evaluator (System.IO.Path.GetDirectoryName (System.Uri (typeof (MainClass).Assembly.CodeBase).LocalPath)); Evaluator.refr ::= Interp.interpassembly; // Evaluate code "c" and print the results. def printev (c) { try { def (nmnvts) = e.Eval (c); Iter (nmnvts, fun (x) { // (is_new, is_mutable, name, value, type). def (newvar, mut, name, val, ty) = x; when (newvar) match ((mut, val, ty)) { | (false, null, _) => SC.WriteLine ($"def $name : $ty") | (_, null, _) => SC.WriteLine ($"mutable $name : $ty") | (_, _, <[ $_ -> $_ ]>) => SC.WriteLine ($"def $name : $ty") | (false, _, _) => try { SC.WriteLine ($"def $name = $val : $ty") } catch { | _ => ce.WriteLine ($"Warning: omitting the value of $name, " "because it contains null(s)."); SC.WriteLine ($"def $name : $ty") } | _ => try { SC.WriteLine ($"mutable $name = $val : $ty") } catch { | _ => ce.WriteLine ($"Warning: omitting the value of $name, " "because it contains null(s)."); SC.WriteLine ($"mutable $name : $ty") } } }) } catch { | e is Exception => unless (Nemerle.Compiler.Message.SeenError) { ce.WriteLine (e.Message + e.StackTrace); ce.WriteLine (e.ToString ()) } } } // Commented out until new completion engine is finished /*def get_type_name (t: ConstructedTypeInfo) : string { mutable name = ""; if (t is ConstructedTypeInfo.Array) { def ar = t :> ConstructedTypeInfo.Array; name = "array[" + get_type_name(ar.Type) + "]"; } else if (t is ConstructedTypeInfo.Class) { def cl = t :> ConstructedTypeInfo.Class; mutable nameByNow = ""; match (cl.Type) { | dti is DeclaredTypeInfo => { if (!dti.IsNested) nameByNow = dti.Namespace + "." + dti.Name; else // Nested classes follow the convention Namespace.DeclaringType+NestedType nameByNow = dti.DeclaringType.Namespace + "." + dti.DeclaringType.Name + "+" + dti.Name; when (dti.TypeParameters.Length > 0) { nameByNow += "["; mutable whereClauses = ""; foreach (typarm in dti.TypeParameters) { nameByNow += typarm.Name + ", "; when (typarm.TypeConstraints.Length > 0) { whereClauses += " where " + typarm.Name + " : "; foreach (constraint in typarm.TypeConstraints) { whereClauses += get_type_name (constraint) + ", "; } whereClauses = whereClauses.TrimEnd (',', ' '); } } nameByNow += name.TrimEnd (',', ' ') + whereClauses; } } | rti is ReferencedTypeInfo => nameByNow = rti.Type.FullName; | _ => (); } when (cl.SubstitutedArguments.Length > 0) { nameByNow += "["; foreach (cdt in cl.SubstitutedArguments) nameByNow += get_type_name(cdt) + ", "; nameByNow = nameByNow.TrimEnd(',', ' '); nameByNow += "]"; } name = nameByNow; } else if (t is ConstructedTypeInfo.Function) { def fu = t :> ConstructedTypeInfo.Function; name = get_type_name (fu.From) + "->" + get_type_name(fu.To); } else if (t is ConstructedTypeInfo.GenericSpecifier) { def gs = t :> ConstructedTypeInfo.GenericSpecifier; name = gs.Name; // It only shows the name, no constraints } else if (t is ConstructedTypeInfo.Tuple) { def tu = t :> ConstructedTypeInfo.Tuple; mutable nameByNow = ""; foreach (cdt in tu.Types) nameByNow += get_type_name(cdt) + "*"; name = nameByNow.Trim('*'); } else name = "void"; name = name.Replace ("System.Byte", "byte") .Replace ("System.SByte", "sbyte") .Replace ("System.Int16", "short") .Replace ("System.UInt16", "ushort") .Replace ("System.Int32", "int") .Replace ("System.UInt32", "uint") .Replace ("System.Int64", "long") .Replace ("System.UInt64", "ulong") .Replace ("System.Single", "float") .Replace ("System.Double", "double") .Replace ("System.Decimal", "decimal") .Replace ("System.String", "string") .Replace ("System.Object", "object") .Replace ("System.Boolean", "bool") .Replace ("System.Char", "char") .Replace ("Nemerle.Core.list", "list"); name }*/ def printcomp (_c) { /*def information = e.Complete (c); when (information is CompletionMembers) { def possibilities = (information :> CompletionMembers); if (possibilities.Members.Length == 0) { SC.WriteLine ("(no possibilities found)"); } else { def reversePossibilities = Nemerle.Collections.List.FromArray (possibilities.Members).Reverse (); foreach (possibility in reversePossibilities) { // match (possibility.CompletionKind) // { // | Member => def minfo = possibility :> CompletionMember; match (possibility) { | f is FieldInfo => mutable name = f.Name; when (f.IsMutable) name = "mutable " + name; name += get_type_name (f.Type); when (f.IsLiteral && f.Value != null) name += " = " + f.Value.ToString(); SC.WriteLine (name); | m is MethodInfo => mutable name = ""; if (m.IsConstructor) name = "this"; else if (m.IsStaticConstructor) name = "static this"; else name = m.Name; when (m.TypeParameters.Length > 0) { name += "["; foreach (typarm in m.TypeParameters) { name += typarm.Name + ", "; } name = name.TrimEnd (',', ' ') + "]"; } name += " ("; foreach (parm in m.Parameters) { name += parm.Name + " : " + get_type_name (parm.Type) + ", " ; } name = name.TrimEnd (',', ' ') + ")"; name += " : " + get_type_name (m.ReturnType); SC.WriteLine (name); | p is PropertyInfo => mutable name = p.Name; when (p.IsIndexer) { name += "["; foreach (parm in p.IndexerParameters) { name += get_type_name (parm) + ", "; } name += name.TrimEnd (',', ' ') + "]"; } name += " : " + get_type_name (p.Type) + " { "; when (p.Getter != null) name += "get; "; when (p.Setter != null) name += "set; "; name += "}"; SC.WriteLine (name); | e is EventInfo => def name = "event " + e.Name + " : " + get_type_name (e.Type); SC.WriteLine (name); | _ => (); } } } }*/ } def rl = NativeReadLine.GetInstance (); // def rl = ConsoleReadLine (); def usehist = HistoryFile.RecreateHistory (rl, histfile); // The main loop. def readinput (code, line) { def readandloop (c) { readinput (c, rl.ReadLine (Interp.Prompt)) } match (line) { | null => System.Environment.Exit (0); | _ => rl.AddHistory (line); when (usehist) try { HistoryFile.AddHistory (histfile, line) } catch { | e => ce.WriteLine ("Warning: " + e.Message) } regexp match (line) { | @"^\s*$" => readandloop (code) | _ => { def l = line.TrimEnd (); if (l.EndsWith (";;")) { def m = code + l.TrimEnd (array [';',' ','\t']); regexp match (m) { // Don't evaluate lines containing only whitespace and ";". | @"^[\s;]*$" => Interp.Prompt = Interp.PS1; readandloop ("") // Fish out using statements, open apropriate namespaces // and/or set namespace aliases, cut the statement out of // the code and evaluate what remained. | @"(?.*(([;}]|(\*/))\s*|^\s*))using\s+(?[a-zA-Z0-9]+((\.[a-zA-Z0-9]+)*(\s*=\s*[a-zA-Z0-9]+)*)*)[\s;]*(?.*)$" => def newns = match (Split (Replace (use, @"\s", ""), ['='])) { | [h] => (None (), Split (h, ['.'])) | [h, t] => (Some (h), Split (t, ['.'])) | _ => assert (false) } Evaluator.ns ::= newns; def c = beg + end; regexp match (c) { | @"[\s;]*" => Interp.Prompt = Interp.PS1; readandloop ("") // Use readinput instead of printev (c), so other // using statements can be evaluated in the same way. | _ => readinput (c, ";;") } // Run external commands in a sub-shell. | @"^\s*!(?.*)$" => def p = Diagnostics.Process (); regexp match (input) { | @"\s*" => if (Environment. GetEnvironmentVariable ("SHELL") != "") p.StartInfo.FileName = "$SHELL" else p.StartInfo.FileName = "cmd.exe" | _ => p.StartInfo.FileName = input } p.StartInfo.UseShellExecute = true; _ = p.Start(); p.WaitForExit() | _ => printev (m) } Interp.Prompt = Interp.PS1; readandloop ("") } else if (l.EndsWith ("**")) { // Code completion def m = code + l.TrimEnd (array ['*',' ','\t']); regexp match (m) { // Don't evaluate lines containing only whitespace and ";". | @"^[\s;]*$" => Interp.Prompt = Interp.PS2; readandloop (code) | _ => printcomp (m) } Interp.Prompt = Interp.PS2; readandloop (code); } else { Interp.Prompt = Interp.PS2; readandloop (code + " " + line) } } } } } if (initialcode != "") { SC.WriteLine ("Please wait while evaluating the config file.."); readinput (initialcode, ";;") } else readinput ("", rl.ReadLine (Interp.Prompt)) } } module HistoryFile { public AddHistory (filename: string, line : string) : void { def sw = File.AppendText (filename); sw.WriteLine (line); sw.Close (); } public RecreateHistory (rl : IReadLine, filename : string) : bool { def ce = Console.Error; try { def sr = File.OpenText (filename); def loop (i, prev) { def l = sr.ReadLine (); when (l != null) { when (l != prev) rl.AddHistory (l); loop (i + 1, l) } } loop (0, ""); sr.Close (); true } catch { | _ is FileNotFoundException => try { _ = AddHistory (filename, ""); true } catch { | e => ce.WriteLine ("Warning: " + e.Message); false } | e => ce.WriteLine ("Warning: " + e.Message); false } } } }