using Nemerle.Collections;
using Nemerle.Compiler;
using Nemerle.Compiler.Parsetree;
using Nemerle.Text;
using Nemerle.Utility;
[assembly: System.Reflection.AssemblyTitle("Nemerle Evaluation Library")]
[assembly: System.Reflection.AssemblyDescription("Nemerle (http://nemerle.org) Runtime evaluation library")]
[assembly: System.Reflection.AssemblyCompany("University of Wroclaw")]
[assembly: System.Reflection.AssemblyProduct("Nemerle Evaluation Library")]
[assembly: System.Reflection.AssemblyCopyright("Copyright @ University of Wroclaw 2005-2008")]
[assembly: Nemerle.Utility.AssemblyVersionFromSVN("0.9.4.SVN")]
[assembly: System.Runtime.InteropServices.ComVisible (false)]
namespace Nemerle.Evaluation
{
/**
* Allows dynamic evaluation of code, with persistent computation
* history (retaining computed variables, functions, etc.) between
* subsequent calls to Evaluator ().Eval ().
*
*
* NemerleCodeProvider
*
*/
public class Evaluator : ManagerClass
{
// Evaluated code.
static mutable code : string;
// Whether the code compiled successfully.
mutable static compiled : bool;
// [(is_mutable, name, type)]
static mutable locals : list [bool * string * PExpr] = [];
// The new values returned after evaluation. This
// was made a field because it's in a try block.
mutable static newvals : list [object] = [];
// A list of namespace aliases and namespaces that are to be opened.
// [(Some (shortname), longname) or (None (), longname)]
public static mutable ns : list [option [string] * list [string]] = [];
// A list of assemblies to reference.
public static mutable refr : list [string] = [];
// [value]
public static mutable vals : list [object] = [];
public static mutable firstRun : bool = true;
private persistentMode : bool;
public this (path : string) {
this (true, path);
}
public this (persistent_mode : bool, path : string = null) {
base (CompilationOptions ());
this.persistentMode = persistent_mode;
this.InitOutput (System.Console.Out);
when (path != null) {
// We need to add the application directory to the search path, so
// the assemblies in nemish.exe directory can be loaded.
Options.LibraryPaths ::= path;
}
Options.CompileToMemory = true;
Options.IgnoreConfusion = true;
Options.ProgressBar = false;
Options.Sources = [""];
// HACK! HACK! HACK!
// This is broken, broken, broken! The values here shouldn't be
// static and there should be no need to clear them.
// HACK! HACK! HACK!
locals = [];
newvals = [];
vals = [];
refr = [];
firstRun = true;
code = null;
compiled = false;
ns = [];
}
public static EvaluateExpression ([Nemerle.Assertions.NotNull] code : string) : object
{
def eval = Evaluator (false, null);
match (eval.Eval (code)) {
| [] => null
| returned => returned.Last [3];
}
}
/**
* Evaluates supplied code in memory and returns computation results.
* Persistent computation history is maintained between subsequent calls.
*
*
* The code to evaluate.
*
*
* A sorted list of computed variables and functions with additional
* descriptive information. The tuple members from the list are:
* #1 - true if the variable was introduced during the last Eval,
* false if it was on the list before.
* #2 - true if the variable is mutable, false if not.
* #3 - name of the variable.
* #4 - value of the variable.
* #5 - type of the variable.
*
* The last computed value - the return value of the evaluated code
* is returned as a special variable named "it".
*
*/
public Eval (code: string) : list [bool * bool * string * object * PExpr] {
Evaluator.code = code;
this.LexingPipeline = fun (_) { null };
this.ParsingPipeline = fun (_) { [null] };
this.ScanningPipeline = DoTheStuff;
// Options.DumpTypedTree = true;
// Options.AdditionalDebug = true;
Options.PersistentLibraries = this.persistentMode && !firstRun;
// Link ourselves.
Options.ReferencedLibraries = System.Uri(this.GetType().Assembly.CodeBase).LocalPath ::
Evaluator.refr;
// unused variable
Options.Warnings.Disable (168);
// ignore computed value
Options.Warnings.Disable (10005);
// Locals and vals from previous calls to Eval.
def prevlocals = Evaluator.locals;
def prevvals = Evaluator.vals;
try {
this.Run ();
def ass = this.GeneratedAssembly;
def meth = ass.GetTypes () [0].GetMethod ("Run");
// And here are the new values (along with the old ones).
newvals = meth.Invoke (null, null) :> list [object];
Evaluator.compiled = true
}
catch {
| e => match (e) {
| _ is AssertionException
| _ is Recovery
| _ is System.ApplicationException =>
if (e.Source == "Nemerle.Compiler" ||
e.Source == "Nemerle.Evaluation") {
Evaluator.locals = prevlocals;
Evaluator.newvals = List.Rev (prevvals);
Evaluator.compiled = false;
unless (e.Message == "Nothing to parse.")
System.Console.WriteLine ($"Warning: $e");
}
else throw;
| _ => throw
}
}
// Check which variables are new to this call and set the first field
// in the return tuple - true for variable "it", new ones and those
// whose values and/or type has changed; false - for the rest.
def prev = Hashtable ();
List.Iter2 (prevlocals, prevvals.Rev (), fun ((mut, name, _), val) {
prev [name] = (mut, val)
});
Evaluator.vals = List.Rev (newvals);
List.Map2 (locals, newvals, fun ((mut, name, ty), val) {
def is_new =
if (prev.Contains (name)) {
def (prevmut, prevval) = prev [name];
(name == "it" && compiled == true) ||
(val != null && ! (val.Equals (prevval))) ||
(val == null && prevval != null) ||
prevmut != mut
} else true;
(is_new, mut, name, val, ty)
}).Rev ()
}
// Does the same that Eval does, but instead of evaluation the result
// provides a list of possible completion types and members
mutable static IsCompletionMode : bool = false;
// commented out until the new completion engine in finished
/*public Complete (code: string) : Nemerle.Completion.CompletionInfo {
IsCompletionMode = true;
Evaluator.code = code;
this.InitOutput (System.IO.StreamWriter (System.IO.MemoryStream ()));
mutable completionList : Nemerle.Completion.CompletionInfo = null;
//Nemerle.Completion.Engine.LesserInit();
def lexer = LexerString (this, code, Location (0, 1, 1));
this.Hierarchy = TypesManager(this);
this.ParsingPipeline = MainParser.Parse;
this.ScanningPipeline = ScanTypeHierarchy (this).ProcessDeclaration;
def decls = this.ParsingPipeline (lexer);
List.Iter (decls, this.ScanningPipeline);
// Link ourselves.
Options.ReferencedLibraries = System.Reflection.Assembly.GetAssembly
(this.GetType ()).Location ::
Evaluator.refr;
// Locals and vals from previous calls to Eval.
def prevlocals = Evaluator.locals;
def prevvals = Evaluator.vals;
this.Hierarchy.Run ();
try {
mutable my_method = null;
this.Hierarchy.infos.Iter (fun (ti) {
def members = ti.GetMethods();
members.Iter ( fun (member : IMethod) {
when (member.Name == "Main") {
my_method = member :> MethodBuilder;
}
} );
} );
def lexer = LexerCompletion (this, Evaluator.code + " ", Evaluator.code.Length);
my_method.GetHeader ().body =
FunBody.Parsed (MainParser.ParseExpr (my_method.DeclaringType.GlobalEnv, lexer));
my_method.RunBodyTyper ();
Evaluator.compiled = false
}
catch {
| e => match (e) {
| cr is Nemerle.Compiler.CompletionResult => () //completionList = Nemerle.Completion.Engine.translate_ovpossibility_to_info (cr.Overloads);
| _ is AssertionException
| _ is Recovery
| _ is System.ApplicationException =>
if (e.Source == "Nemerle.Compiler" ||
e.Source == "Nemerle.Evaluation") {
Evaluator.locals = prevlocals;
Evaluator.newvals = List.Rev (prevvals);
Evaluator.compiled = false;
//System.Console.WriteLine (e);
}
else throw;
| _ => throw
}
}
this.InitOutput (System.Console.Out);
this.ParsingPipeline = fun (_) { [null] };
this.ScanningPipeline = DoTheStuff;
IsCompletionMode = false;
completionList
}*/
internal DoTheStuff (_tdecl : Parsetree.TopDeclaration) : void
{
when (!Options.PersistentLibraries || firstRun)
this.MacrosRegistry.RegisterMacro (StagedMacro ());
firstRun = false;
// Open namespaces and set aliases.
def env = List.FoldLeft (Evaluator.ns, this.CoreEnv,
fun (x : option [string] * list [string],
acc : GlobalEnv) {
match (x [0]) {
| None => acc.AddOpenNamespace (x [1], Location.Default)
| Some (sname) => acc.AddNamespaceAlias (sname, x [1],
Location.Default)
}
});
MacroColors.PushNewColor (-1, env);
// Set the class in which we're going to put the evaluated code.
def cname = Macros.NewSymbol ();
def tb = this.CoreEnv.Define (<[ decl: public class $(cname : name) {
} ]>);
mutable body = null;
/*if (IsCompletionMode) {
def lexer = LexerCompletion (this, Evaluator.code, Evaluator.code.Length);
body = MainParser.ParseExpr (env, lexer);
}
else*/ {
body = MainParser.ParseExpr (env, Evaluator.code, allow_empty = true);
}
match (body) {
| null
| <[ ]> => throw System.ApplicationException ("Nothing to parse.")
| _ => ()
}
def make_last (last) {
| <[ def $_ = $_ ]>
| <[ mutable $_ = $_ ]>
| <[ def .. $_ ]> =>
<[ $last; $("stagedmacro" : usesite) (()) ]>
| _ => <[ $("stagedmacro" : usesite) ($last) ]>
}
// If the code ends with an assignment, append `()'.
// Put a call to stagedmacro at the end of the evaluated code
// with the last expression moved to the stagedmacro argument.
def whole = match (body) {
| <[ {..$seq } ]> =>
def (beg, last) = List.DivideLast (seq);
def last = make_last (last);
<[ {..$ (beg + [last]) } ]>
| _ => make_last (body)
}
// Recreate variables defined in previous calls to Eval.
def inits = List.FoldLeft (Evaluator.locals, [], fun (x, acc) {
def (mut, name, ty) = x;
match ((mut, ty)) {
| (false, <[ System.Object ]>) =>
<[ def $(name : usesite) = List.Hd (Evaluator.vals) : $ty ]> ::
<[ Evaluator.vals = List.Tl (Evaluator.vals) ]> :: acc
| (false, _) =>
<[ def $(name : usesite) = List.Hd (Evaluator.vals) :> $ty ]> ::
<[ Evaluator.vals = List.Tl (Evaluator.vals) ]> :: acc
| (_, <[ System.Object ]>) =>
<[ mutable $(name : usesite) =
List.Hd (Evaluator.vals) : $ty ]> ::
<[ Evaluator.vals = List.Tl (Evaluator.vals) ]> :: acc;
| _ =>
<[ mutable $(name : usesite) =
List.Hd (Evaluator.vals) :> $ty ]> ::
<[ Evaluator.vals = List.Tl (Evaluator.vals) ]> :: acc;
}
});
def w = <[ {.. $(inits + [whole]) } ]>;
// PrettyPrint.PrintExpr (None (), w);
tb.Define (<[ decl: public static Run () : list [System.Object] {
$w } ]>);
tb.Compile ();
MacroColors.PopColor ();
}
internal class StagedMacro : IMacro
{
public Run (ctx : Typer, val : list [SyntaxElement]) : PExpr {
match (val) {
// Fish out variables/functions and store them in our fields.
| [SyntaxElement.Expression (expr)] =>
Evaluator.locals = [];
def l = ctx.LocalContext.GetLocals ();
mutable values = l.Fold ([], fun (n : Name, loc : LocalValue, acc) {
match (n.ToString ()) {
| "it" => acc
| _ when loc.ValKind is LocalValue.Kind.BlockReturn => acc
| _ =>
Evaluator.locals = (loc.IsMutable, loc.Name,
PrettyPrint.TyVarToParseTree
(loc.Type)) :: Evaluator.locals;
<[ ($(n : name) : System.Object) ]> :: acc
}
});
def texpr = ctx.TypeExpr (expr);
match (texpr.MType()) {
| MType.Void =>
<[ $(texpr : typed); [..$values] ]>
| _ =>
Evaluator.locals = (false, "it", PrettyPrint.TyVarToParseTree (texpr.MType())) :: Evaluator.locals;
values ::= <[ $expr : System.Object ]>;
<[ [..$values] ]>
}
| _ => Util.ice ()
}
}
// This below is only to satisfy the interface requirements.
public CallTransform (l : list [PExpr]) : list [SyntaxElement]
{ List.Map (l, fun (x) { SyntaxElement.Expression (x) }) }
public GetName () : string { "stagedmacro" }
public GetNamespace () : string { "" }
public Location : Location { get { Location.Default } }
public IsInherited : bool { get { false } }
public IsTailRecursionTransparent : bool { get { false } }
public Keywords : list [string] { get { [] } }
public SyntaxExtension () : GrammarElement * (list [SyntaxElement] ->
list [SyntaxElement])
{ (null, fun (_) { [] }) }
public Usage : Nemerle.MacroUsageAttribute { get { null } }
}
}
}