using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using HeuristicLab.Core; using HeuristicLab.Random; namespace HeuristicLab.Problems.ProgramSynthesis { public class PushInterpreter : IInternalPushInterpreter, IDisposable { private Task currentTask; private int evalPushLimit; /// /// An interpreter for Push /// /// Determines the runtime behavior of the interpreter /// Note that random may be reseted /// Used to save resources used by the interpreter public PushInterpreter(IReadOnlyPushConfiguration config = null, IRandom random = null, InterpreterPoolContainer poolContainer = null) { randomOrigin = random ?? new MersenneTwister(); Random = (IRandom)randomOrigin.Clone(); Configuration = config ?? new PushConfiguration(); ExecStack = new PushStack(Configuration.MaxProgramLength); CodeStack = new PushStack(); NameStack = new PushStack(); BooleanStack = new PushStack(); IntegerStack = new PushStack(); FloatStack = new PushStack(); CharStack = new PushStack(); StringStack = new PushStack(); IntegerVectorStack = new PushStack>(); FloatVectorStack = new PushStack>(); BooleanVectorStack = new PushStack>(); StringVectorStack = new PushStack>(); PrintStack = new PrintStack(); Stacks = new Dictionary { { StackTypes.Exec, ExecStack }, { StackTypes.Code, CodeStack }, { StackTypes.Integer, IntegerStack }, { StackTypes.Float, FloatStack }, { StackTypes.Boolean, BooleanStack }, { StackTypes.Char, CharStack }, { StackTypes.String, StringStack }, { StackTypes.Name, NameStack }, { StackTypes.Print, PrintStack }, { StackTypes.IntegerVector, IntegerVectorStack }, { StackTypes.FloatVector, FloatVectorStack }, { StackTypes.BooleanVector, BooleanVectorStack }, { StackTypes.StringVector, StringVectorStack }, }; supportedStackTypes = Stacks.Keys.ToArray(); CustomExpressions = new Dictionary(); PoolContainer = poolContainer ?? new InterpreterPoolContainer(); InitEvalPushLimit(); ConfigureStacks(); } public PushInterpreter(int seed, IReadOnlyPushConfiguration config = null) : this(config, new FastRandom(seed)) { } private readonly StackTypes[] supportedStackTypes; public IReadOnlyDictionary Stacks { get; private set; } private IRandom randomOrigin; public IRandom Random { get; private 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 < 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 IPrintStack PrintStack { get; private set; } public IDictionary CustomExpressions { get; private set; } public bool IsNameQuoteFlagSet { get; set; } private readonly List inputExpressions = new List(); IReadOnlyList IInternalPushInterpreter.InputExpressions { get { return inputExpressions; } } public void SetInput( IReadOnlyList integers = null, IReadOnlyList floats = null, IReadOnlyList booleans = null, IReadOnlyList chars = null, IReadOnlyList strings = null, IReadOnlyList> integerVectors = null, IReadOnlyList> floatVectors = null, IReadOnlyList> stringVectors = null) { // Integer if (integers != null && integers.Count > 0) { var integerPushExpressionPool = PoolContainer.GetStatefulExpressionPool(); for (var i = 0; i < integers.Count; i++) { var expression = IntegerPushExpression.Create(integerPushExpressionPool, integers[i]); inputExpressions.Add(expression); } } // Float if (floats != null && floats.Count > 0) { var floatPushExpressionPool = PoolContainer.GetStatefulExpressionPool(); for (var i = 0; i < floats.Count; i++) { var expression = FloatPushExpression.Create(floatPushExpressionPool, floats[i]); inputExpressions.Add(expression); } } // Booleans if (booleans != null && booleans.Count > 0) { var booleanPushExpressionPool = PoolContainer.GetStatefulExpressionPool(); for (var i = 0; i < booleans.Count; i++) { var expression = BooleanPushExpression.Create(booleanPushExpressionPool, booleans[i]); inputExpressions.Add(expression); } } // Char if (chars != null && chars.Count > 0) { var charPushExpressionPool = PoolContainer.GetStatefulExpressionPool(); for (var i = 0; i < chars.Count; i++) { var expression = CharPushExpression.Create(charPushExpressionPool, chars[i]); inputExpressions.Add(expression); } } // String if (strings != null && strings.Count > 0) { var stringPushExpressionPool = PoolContainer.GetStatefulExpressionPool(); for (var i = 0; i < strings.Count; i++) { var expression = StringPushExpression.Create(stringPushExpressionPool, strings[i]); inputExpressions.Add(expression); } } // IntegerVector if (integerVectors != null && integerVectors.Count > 0) { var integerVectorPushExpressionPool = PoolContainer.GetStatefulExpressionPool(); for (var i = 0; i < integerVectors.Count; i++) { var expression = IntegerVectorPushExpression.Create(integerVectorPushExpressionPool, integerVectors[i]); inputExpressions.Add(expression); } } // FloatVector if (floatVectors != null && floatVectors.Count > 0) { var floatVectorPushExpressionPool = PoolContainer.GetStatefulExpressionPool(); for (var i = 0; i < floatVectors.Count; i++) { var expression = FloatVectorPushExpression.Create(floatVectorPushExpressionPool, floatVectors[i]); inputExpressions.Add(expression); } } // StringVector if (stringVectors != null && stringVectors.Count > 0) { var stringVectorPushExpressionPool = PoolContainer.GetStatefulExpressionPool(); for (var i = 0; i < stringVectors.Count; i++) { var expression = StringVectorPushExpression.Create(stringVectorPushExpressionPool, stringVectors[i]); inputExpressions.Add(expression); } } } 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(bool stepwise) { Run(PushProgram.Empty, 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 it gets evaluated in Run. * Otherwise the push program will be evaluated and the expressions of the program are pushed onto the EXEC stack. * Expanding the initial program is not counted in ExecCount */ InitRun(expression); if (Configuration.TopLevelPushCode && CodeStack.IsEnabled) CodeStack.Push(expression); 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 interpreter which clears and reconfigures stacks and local variables /// public void Reset(IRandom random = null) { if (random != null) randomOrigin = random; // Reset Random Random = (IRandom)randomOrigin.Clone(); InitEvalPushLimit(); ExecCounter = 0; IsAborted = false; IsPaused = false; currentTask = null; inputExpressions.Clear(); ClearStacks(); ConfigureStacks(); } private void InitEvalPushLimit() { // evalPushLimit is accessed very often and using the getter of the configuration object is rather expensive as the values is capsuled within a parameter evalPushLimit = Configuration.EvalPushLimit; } private void EvalPushLimitChanged(object sender, EventArgs e) { this.evalPushLimit = Configuration.EvalPushLimit; } private void ConfigureStacks() { for (var i = 0u; i < supportedStackTypes.Length; i++) { var key = supportedStackTypes[i]; Stacks[key].IsEnabled = Configuration.IsStackEnabled(key); } // exec stack must always be enabled this.ExecStack.IsEnabled = true; } /// /// Clears Stacks and custom expressions /// public void ClearStacks() { for (var i = 0u; i < supportedStackTypes.Length; i++) { var key = supportedStackTypes[i]; Stacks[key].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.IsStackEnabled(StackTypes.Exec)) { while (CanRun(10)) DoTenSteps(); } while (IsRunning) DoStep(); if (IsCompleted) Finally(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Finally() { if (Configuration.TopLevelPopCode && !CodeStack.IsEmpty && CodeStack.IsEnabled) CodeStack.Pop(); PoolContainer.DisposePools(); } private void InitRun(Expression expression) { ExecStack.Push(expression); if (!expression.IsProgram) return; DoStep(); ExecCounter--; // unpacking the initial program is not counted } [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool DoStep() { ExecCounter++; return ExecStack.Pop().TryEval(this); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void DoTenSteps() { ExecStack[0].TryEval(this); ExecStack[1].TryEval(this); ExecStack[2].TryEval(this); ExecStack[3].TryEval(this); ExecStack[4].TryEval(this); ExecStack[5].TryEval(this); ExecStack[6].TryEval(this); ExecStack[7].TryEval(this); ExecStack[8].TryEval(this); ExecStack[9].TryEval(this); ExecStack.Remove(10); ExecCounter += 10; } private Task InterpretAsync() { currentTask = Task.Run(() => Run()); return currentTask; } public virtual void Dispose() { PoolContainer.DisposePools(); } } }