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();
}
}
}