namespace HeuristicLab.Problems.ProgramSynthesis.Push.Interpreter { using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Attributes; using Configuration; using Core; using Expressions; using Parser; using Random; using Stack; #if DEBUG using System.Diagnostics; using System.Linq; #endif public class PushInterpreter : IInternalPushInterpreter, IDisposable { private Task currentTask; public PushInterpreter(IReadOnlyPushConfiguration config = null, IRandom random = null, InterpreterPoolContainer poolContainer = null) { Random = random ?? new FastRandom(); Configuration = config ?? new PushConfiguration(); // setting the capacity of the stacks to max points ensures that there will be enough memory at runtime ExecStack = new PushStack(Configuration.MaxPointsInProgram); CodeStack = new PushStack { IsEnabled = Configuration.EnabledStacks[StackTypes.Code] }; NameStack = new PushStack { IsEnabled = Configuration.EnabledStacks[StackTypes.Code] }; BooleanStack = new PushStack { IsEnabled = Configuration.EnabledStacks[StackTypes.Boolean] }; IntegerStack = new PushStack { IsEnabled = Configuration.EnabledStacks[StackTypes.Integer] }; FloatStack = new PushStack { IsEnabled = Configuration.EnabledStacks[StackTypes.Float] }; CharStack = new PushStack { IsEnabled = Configuration.EnabledStacks[StackTypes.Char] }; StringStack = new PushStack { IsEnabled = Configuration.EnabledStacks[StackTypes.String] }; IntegerVectorStack = new PushStack> { IsEnabled = Configuration.EnabledStacks[StackTypes.IntegerVector] }; FloatVectorStack = new PushStack> { IsEnabled = Configuration.EnabledStacks[StackTypes.FloatVector] }; BooleanVectorStack = new PushStack> { IsEnabled = Configuration.EnabledStacks[StackTypes.BooleanVector] }; StringVectorStack = new PushStack> { IsEnabled = Configuration.EnabledStacks[StackTypes.StringVector] }; PrintStack = new PushStack { IsEnabled = Configuration.EnabledStacks[StackTypes.Print] }; CustomExpressions = new Dictionary(); PoolContainer = poolContainer ?? new InterpreterPoolContainer(); } public PushInterpreter(int seed, IReadOnlyPushConfiguration config = null) : this(config, new FastRandom(seed)) { } public IRandom Random { get; set; } public long ExecCounter { get; private set; } public bool IsPaused { get; private set; } public bool IsAborted { get; private set; } public bool IsRunning { get { return !ExecStack.IsEmpty && !IsPaused && !IsAborted && (ExecCounter < Configuration.EvalPushLimit); } } public bool IsCompleted { get { return ExecStack.IsEmpty || ExecCounter >= Configuration.EvalPushLimit; } } public bool CanStep { get { return !IsCompleted && !IsAborted && IsPaused; } } public InterpreterPoolContainer PoolContainer { get; private set; } public IReadOnlyPushConfiguration Configuration { get; protected set; } [PushStack(StackTypes.Code)] public IPushStack CodeStack { get; private set; } [PushStack(StackTypes.Exec)] public IPushStack ExecStack { get; private set; } [PushStack(StackTypes.Name)] public IPushStack NameStack { get; private set; } [PushStack(StackTypes.Boolean)] public IPushStack BooleanStack { get; private set; } [PushStack(StackTypes.Integer)] public IPushStack IntegerStack { get; private set; } [PushStack(StackTypes.Float)] public IPushStack FloatStack { get; private set; } [PushStack(StackTypes.Char)] public IPushStack CharStack { get; private set; } [PushStack(StackTypes.String)] public IPushStack StringStack { get; private set; } [PushStack(StackTypes.IntegerVector)] public IPushStack> IntegerVectorStack { get; private set; } [PushStack(StackTypes.FloatVector)] public IPushStack> FloatVectorStack { get; private set; } [PushStack(StackTypes.BooleanVector)] public IPushStack> BooleanVectorStack { get; private set; } [PushStack(StackTypes.StringVector)] public IPushStack> StringVectorStack { get; private set; } [PushStack(StackTypes.Print)] public IPushStack PrintStack { get; private set; } public IDictionary CustomExpressions { get; private set; } public bool IsNameQuoteFlagSet { get; set; } public Task RunAsync(string code, CancellationToken token = default(CancellationToken)) { var program = PushParser.Parse(code); currentTask = RunAsync(program, token); return currentTask; } public async Task RunAsync(Expression expression, CancellationToken token = default(CancellationToken)) { await Task.Run(() => Run(expression), token); } public void Run(string code, bool stepwise = false) { var program = PushParser.Parse(code); Run(program, stepwise); } public void Run(Expression expression, bool stepwise = false) { IsPaused = stepwise; /* Push top expression so the loop is able to enter * If the top expression is a single statement then the loop has nothing to do. * Otherwise the expand expression will be evaluated and pushes code onto the EXEC stack. * Expanding the initial program is not counted */ ExecStack.Push(expression); if (Configuration.TopLevelPushCode) CodeStack.Push(expression); // expand if program if (expression.IsProgram) DoStep(); Run(); } public bool CanRun(int count) { return ExecStack.Count >= count && !IsPaused && !IsAborted && ExecCounter + count < Configuration.EvalPushLimit; } public async Task AbortAsync() { if (IsAborted || IsCompleted) return; IsAborted = true; PoolContainer.DisposePools(); if (currentTask != null) await currentTask; } public async Task AbortAndResetAsync() { await AbortAsync(); Reset(); } public async Task PauseAsync() { if (IsPaused || IsCompleted) return; IsPaused = true; if (currentTask != null) await currentTask; } public async Task ResumeAsync() { if (IsPaused || !IsAborted) { IsPaused = false; await InterpretAsync(); } else await currentTask; } public void Resume() { if (!IsPaused && IsAborted) return; IsPaused = false; Run(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Step() { if (!CanStep) return false; var successful = DoStep(); if (IsCompleted) Finally(); return successful; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Step(int count) { for (var i = 0u; i < count; i++) Step(); } /// /// Reset while interpreter /// public void Reset() { ExecCounter = 0; IsAborted = false; IsPaused = false; currentTask = null; Clear(); } /// /// Clears stacks and custom expressions /// public void Clear() { ExecStack.Clear(); CodeStack.Clear(); NameStack.Clear(); BooleanStack.Clear(); IntegerStack.Clear(); FloatStack.Clear(); CustomExpressions.Clear(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Run() { // if no stack which is modifies the exec stack is enabled, unroll loop due to performance reasons if (!Configuration.EnabledStacks[StackTypes.Exec] && !Configuration.EnabledStacks[StackTypes.Code]) { while (CanRun(10)) DoTenSteps(); } while (IsRunning) DoStep(); if (IsCompleted) Finally(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Finally() { if (Configuration.TopLevelPopCode && !CodeStack.IsEmpty) CodeStack.Pop(); PoolContainer.DisposePools(); } #if DEBUG private Expression last; private bool DoStep() { double[] bk; if (!FloatStack.IsEmpty) { bk = FloatStack.Peek(Math.Min(FloatStack.Count, 3)); } var expression = ExecStack.Pop(); var succ = expression.Eval(this); if ((ExecStack.Count > 0 && ExecStack.Top == null) || (CodeStack.Count > 0 && CodeStack.Top == null) || FloatStack.Any(x => double.IsNaN(x) || double.IsInfinity(x)) || StringStack.Count > 0 && StringStack.Any(x => x == null)) { Debugger.Break(); } ExecCounter++; last = expression; return succ; } #else [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool DoStep() { ExecCounter++; return ExecStack.Pop().Eval(this); } #endif [MethodImpl(MethodImplOptions.AggressiveInlining)] private void DoTenSteps() { ExecStack[0].Eval(this); ExecStack[1].Eval(this); ExecStack[2].Eval(this); ExecStack[3].Eval(this); ExecStack[4].Eval(this); ExecStack[5].Eval(this); ExecStack[6].Eval(this); ExecStack[7].Eval(this); ExecStack[8].Eval(this); ExecStack[9].Eval(this); ExecStack.Remove(10); ExecCounter += 10; } private Task InterpretAsync() { currentTask = Task.Run(() => Run()); return currentTask; } public virtual void Dispose() { PoolContainer.DisposePools(); } } }