namespace HeuristicLab.Problems.ProgramSynthesis.Push.Expressions {
using System;
using System.Globalization;
using System.Linq;
using Attributes;
using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
using HeuristicLab.Problems.ProgramSynthesis.Push.Extensions;
using Interpreter;
using Stack;
///
/// Stringifies and pushes the top INTEGER.,
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.FROMINTEGER",
"Stringifies and pushes the top INTEGER.",
StackTypes.Integer)]
public class StringFromIntegerExpression : StatelessExpression {
public StringFromIntegerExpression() { }
[StorableConstructor]
protected StringFromIntegerExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.IntegerStack.IsEmpty;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var value = interpreter.IntegerStack.Pop();
interpreter.StringStack.Push(value.ToString());
}
}
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.FROMFLOAT",
"Stringifies and pushes the top FLOAT.",
StackTypes.Float)]
public class StringFromFloatExpression : StatelessExpression {
public StringFromFloatExpression() { }
[StorableConstructor]
protected StringFromFloatExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.FloatStack.IsEmpty;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var value = interpreter.FloatStack.Pop();
var str = value.ToString(interpreter.Configuration.FloatStringFormat, CultureInfo.InvariantCulture);
interpreter.StringStack.Push(str);
}
}
///
/// Stringifies and pushes the top BOOLEAN.
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.FROMBOOLEAN",
"Stringifies and pushes the top BOOLEAN.",
StackTypes.Boolean)]
public class StringFromBooleanExpression : StatelessExpression {
public StringFromBooleanExpression() { }
[StorableConstructor]
protected StringFromBooleanExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.BooleanStack.IsEmpty;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var value = interpreter.BooleanStack.Pop();
interpreter.StringStack.Push(value.ToString());
}
}
///
/// Stringifies and pushes the top CHAR.
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.FROMCHAR",
"Stringifies and pushes the top CHAR.",
StackTypes.Char)]
public class StringFromCharExpression : StatelessExpression {
public StringFromCharExpression() { }
[StorableConstructor]
protected StringFromCharExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.CharStack.IsEmpty;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var value = interpreter.CharStack.Pop();
interpreter.StringStack.Push(value.ToString());
}
}
///
/// Concats the top 2 strings.
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.CONCAT",
"Concats the top 2 strings.")]
public class StringConcatExpression : StatelessExpression {
public StringConcatExpression() { }
[StorableConstructor]
protected StringConcatExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.StringStack.Count < 2 ||
interpreter.StringStack.Top.Length + interpreter.StringStack[1].Length > interpreter.Configuration.MaxStringLength;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var str = interpreter.StringStack.Pop();
interpreter.StringStack.Top += str;
}
}
///
/// Conj char onto string
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.CONJCHAR",
"Conj top CHAR onto top STRING",
StackTypes.Char)]
public class StringConjCharExpression : StatelessExpression {
public StringConjCharExpression() { }
[StorableConstructor]
protected StringConjCharExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.StringStack.IsEmpty ||
interpreter.CharStack.IsEmpty ||
interpreter.StringStack.Top.Length + 1 > interpreter.Configuration.MaxStringLength;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var c = interpreter.CharStack.Pop();
interpreter.StringStack.Top += c;
}
}
///
/// Same as substring 0-n, whereby n is taken from the INTEGER stack.
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.TAKE",
"Same as substring 0-n, whereby n is taken from the INTEGER stack.",
StackTypes.Integer)]
public class StringTakeExpression : StatelessExpression {
public StringTakeExpression() { }
[StorableConstructor]
protected StringTakeExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.StringStack.IsEmpty ||
interpreter.IntegerStack.IsEmpty;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var str = interpreter.StringStack.Top;
if (str.Length == 0)
return;
var endIndex = interpreter.IntegerStack.Pop().AsInt(str.Length);
interpreter.StringStack.Top = str.Substring(0, endIndex);
}
}
///
/// Replaces top STRING with a substring from index A to B, whereby A and B are taken from the INTEGER STACK.
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.SUBSTRING",
"Replaces top STRING with a substring from index A to B, whereby A and B are taken from the INTEGER STACK.",
StackTypes.Integer)]
public class StringSubstringExpression : StatelessExpression {
public StringSubstringExpression() { }
[StorableConstructor]
protected StringSubstringExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.StringStack.IsEmpty ||
interpreter.IntegerStack.Count < 2;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var str = interpreter.StringStack.Top;
var firstValue = interpreter.IntegerStack[1];
var secondValue = interpreter.IntegerStack.Top;
interpreter.IntegerStack.Remove(2);
var first = Math.Min(str.Length - 1, Math.Max(firstValue, 0));
var second = Math.Min(str.Length - 1, Math.Max(secondValue, first));
var length = second - first;
if (length > 0)
interpreter.StringStack.Top = str.Substring((int)first, (int)length);
}
}
///
/// Pushes first char of top STRING.
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.FIRST",
"Pushes first char of top STRING.")]
public class StringFirstExpression : StatelessExpression {
public StringFirstExpression() { }
[StorableConstructor]
protected StringFirstExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.StringStack.IsEmpty ||
interpreter.StringStack.Top.Length == 0;
}
public override void Eval(IInternalPushInterpreter interpreter) {
interpreter.StringStack.Top = interpreter.StringStack.Top[0].ToString();
}
}
///
/// Pushes last char of top STRING.
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.LAST",
"Pushes last char of top STRING.")]
public class StringLastExpression : StatelessExpression {
public StringLastExpression() { }
[StorableConstructor]
protected StringLastExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.StringStack.IsEmpty ||
interpreter.StringStack.Top.Length == 0;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var str = interpreter.StringStack.Top;
var c = str[str.Length - 1].ToString();
interpreter.StringStack.Top = c;
}
}
///
/// Pushes nth char of top STRING, whereby n is take from the INTEGER stack.
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.NTH",
"Pushes nth char of top STRING, whereby n is take from the INTEGER stack.",
StackTypes.Integer)]
public class StringNthExpression : StatelessExpression {
public StringNthExpression() { }
[StorableConstructor]
protected StringNthExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.StringStack.IsEmpty ||
interpreter.IntegerStack.IsEmpty ||
interpreter.StringStack.Top.Length == 0;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var str = interpreter.StringStack.Top;
var index = str.Length == 1 ? 0 : interpreter.IntegerStack.Pop().AsInt(str.Length);
var c = str[index].ToString();
interpreter.StringStack.Top = c;
}
}
///
/// Removes first char of top STRING.
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.REST",
"Removes first char of top STRING.")]
public class StringRestExpression : StatelessExpression {
public StringRestExpression() { }
[StorableConstructor]
protected StringRestExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.StringStack.IsEmpty ||
interpreter.StringStack.Top.Length == 0;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var str = interpreter.StringStack.Top;
interpreter.StringStack.Top = str.Length == 1 ? string.Empty : str.Substring(1, str.Length - 1);
}
}
///
/// Removes last char of top STRING.
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.BUTLAST",
"Removes last char of top STRING.")]
public class StringButLastExpression : StatelessExpression {
public StringButLastExpression() { }
[StorableConstructor]
protected StringButLastExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.StringStack.IsEmpty ||
interpreter.StringStack.Top.Length == 0;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var str = interpreter.StringStack.Top;
interpreter.StringStack.Top = str.Length == 1 ? string.Empty : str.Substring(0, str.Length - 1);
}
}
///
/// Pushes length of top STRING onto the INTEGER stack.
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.LENGTH",
"Pushes length of top STRING onto the INTEGER stack.",
StackTypes.Integer)]
public class StringLengthExpression : StatelessExpression {
public StringLengthExpression() { }
[StorableConstructor]
protected StringLengthExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.StringStack.IsEmpty;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var str = interpreter.StringStack.Pop();
interpreter.IntegerStack.Push(str.Length);
}
}
///
/// Reverses the top STRING.
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.REVERSE",
"Reverses the top STRING.")]
public class StringReverseExpression : StatelessExpression {
public StringReverseExpression() { }
[StorableConstructor]
protected StringReverseExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.StringStack.IsEmpty || interpreter.StringStack.Top.Length <= 1;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var chars = interpreter.StringStack.Top.ToArray();
Array.Reverse(chars);
interpreter.StringStack.Top = new string(chars);
}
}
///
/// Maps every char of top STRING as separate string.
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.PARSETOCHARS",
"Maps every char of top STRING as separate string.")]
public class StringParseToCharsExpression : StatelessExpression {
public StringParseToCharsExpression() { }
[StorableConstructor]
protected StringParseToCharsExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.StringStack.IsEmpty;
}
public override void Eval(IInternalPushInterpreter interpreter) {
if (interpreter.StringStack.Top.Length == 0) {
interpreter.StringStack.Pop();
return;
}
var str = interpreter.StringStack.Top;
interpreter.StringStack.Top = str[0].ToString();
if (str.Length > 1) {
var chars = new string[str.Length - 1];
for (var i = 0; i < str.Length - 1; i++) {
chars[i] = str[i + 1].ToString();
}
interpreter.StringStack.Push(chars);
}
}
}
///
/// Splits top STRING by whitespace chars.
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.SPLIT",
"Splits top STRING by whitespace chars.")]
public class StringSplitExpression : StatelessExpression {
public StringSplitExpression() { }
[StorableConstructor]
protected StringSplitExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.StringStack.IsEmpty;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var words = interpreter.StringStack.Top
.Trim()
.Split()
.Where(x => !string.IsNullOrWhiteSpace(x))
.Reverse()
.ToList();
if (words.Count == 0) {
interpreter.StringStack.Pop();
} else {
interpreter.StringStack.Top = words[0];
interpreter.StringStack.Push(words, 1);
}
}
}
///
/// True if top string is empty.
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.EMPTYSTRING",
"True if top STRING is empty.",
StackTypes.Boolean)]
public class StringEmptyStringExpression : StatelessExpression {
public StringEmptyStringExpression() { }
[StorableConstructor]
protected StringEmptyStringExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.StringStack.IsEmpty;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var str = interpreter.StringStack.Pop();
interpreter.BooleanStack.Push(str.Length == 0);
}
}
///
/// True if top string is a substring of second string; false otherwise
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.CONTAINS",
"True if top STRING is a substring of second STRING, false otherwise.",
StackTypes.Boolean)]
public class StringContainsExpression : StatelessExpression {
public StringContainsExpression() { }
[StorableConstructor]
protected StringContainsExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.StringStack.Count < 2;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var first = interpreter.StringStack[1];
var second = interpreter.StringStack.Top;
interpreter.StringStack.Remove(2);
interpreter.BooleanStack.Push(first.IndexOf(second, StringComparison.Ordinal) >= 0);
}
}
///
/// True if the top char is in the top string
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.CONTAINSCHAR",
"True if the top CHAR is in the top STRING.",
StackTypes.Boolean | StackTypes.Char)]
public class StringContainsCharExpression : StatelessExpression {
public StringContainsCharExpression() { }
[StorableConstructor]
protected StringContainsCharExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.StringStack.IsEmpty ||
interpreter.CharStack.IsEmpty;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var str = interpreter.StringStack.Pop();
var c = interpreter.CharStack.Pop();
interpreter.BooleanStack.Push(str.IndexOf(c) >= 0);
}
}
///
/// Puts on the integer stack the index of the top char in the top string
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.INDEXOFCHAR",
"Puts on the INTEGER stack the index of the top CHAR in the top STRING.",
StackTypes.Integer | StackTypes.Char)]
public class StringIndexOfCharExpression : StatelessExpression {
public StringIndexOfCharExpression() { }
[StorableConstructor]
protected StringIndexOfCharExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.StringStack.IsEmpty ||
interpreter.CharStack.IsEmpty;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var str = interpreter.StringStack.Pop();
var c = interpreter.CharStack.Pop();
interpreter.IntegerStack.Push(str.IndexOf(c));
}
}
///
/// The number of times the top char is in the top string
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.OCCURRENCESOFCHAR",
"The number of times the top CHAR is in the top STRING.",
StackTypes.Integer | StackTypes.Char)]
public class StringOccurrencesOfCharExpression : StatelessExpression {
public StringOccurrencesOfCharExpression() { }
[StorableConstructor]
protected StringOccurrencesOfCharExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.StringStack.IsEmpty ||
interpreter.CharStack.IsEmpty;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var str = interpreter.StringStack.Pop();
var c = interpreter.CharStack.Pop();
var count = 0;
for (var i = 0; i < str.Length; i++)
if (str[i] == c) count++;
interpreter.IntegerStack.Push(count);
}
}
///
/// In third STRING, replaces second string with first string
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.REPLACE",
"In third STRING, replaces second STRING with first STRING.")]
public class StringReplaceExpression : StatelessExpression {
public StringReplaceExpression() { }
[StorableConstructor]
protected StringReplaceExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.StringStack.Count < 3;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var result = GetResultString(interpreter);
// Should be checked in isNoop but would require to calculate result twice which is an expensive operation
if (result.Length > interpreter.Configuration.MaxStringLength)
return;
interpreter.StringStack.Remove(2);
interpreter.StringStack.Top = result;
}
private static string GetResultString(IInternalPushInterpreter interpreter) {
var third = interpreter.StringStack[2];
var second = interpreter.StringStack[1];
var first = interpreter.StringStack[0];
var result = third.Length == 0 || second.Length == 0
? third
: third.Replace(second, first);
return result;
}
}
///
/// In third STRING, replaces first occurrence of second string with first string
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.REPLACEFIRST",
"In third STRING, replaces first occurrence of second STRING with first STRING")]
public class StringReplaceFirstExpression : StatelessExpression {
public StringReplaceFirstExpression() { }
[StorableConstructor]
protected StringReplaceFirstExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.StringStack.Count < 3;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var result = GetResultString(interpreter);
// Should be checked in isNoop but would require to calculate result twice which is an expensive operation
if (result.Length > interpreter.Configuration.MaxStringLength)
return;
interpreter.StringStack.Remove(2);
interpreter.StringStack.Top = result;
}
private static string GetResultString(IInternalPushInterpreter interpreter) {
var third = interpreter.StringStack[2];
var second = interpreter.StringStack[1];
var first = interpreter.StringStack[0];
var result = third.Length == 0 || second.Length == 0
? third
: ReplaceFirst(third, second, first);
return result;
}
private static string ReplaceFirst(string text, string search, string replace) {
var pos = text.IndexOf(search, StringComparison.Ordinal);
return pos < 0 ? text : text.Substring(0, pos) + replace + text.Substring(pos + search.Length);
}
}
///
/// In top STRING, replaces all occurrences of second char with first char
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.REPLACECHAR",
"In top STRING, replaces all occurrences of second CHAR with first CHAR.",
StackTypes.Char)]
public class StringReplaceCharExpression : StatelessExpression {
public StringReplaceCharExpression() { }
[StorableConstructor]
protected StringReplaceCharExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.StringStack.IsEmpty ||
interpreter.CharStack.Count < 2;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var first = interpreter.CharStack[1];
var second = interpreter.CharStack.Top;
interpreter.CharStack.Remove(2);
var result = interpreter.StringStack.Top.Replace(first, second);
interpreter.StringStack.Top = result;
}
}
///
/// In top STRING, replaces first occurrence of second char with first char
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.REPLACEFIRSTCHAR",
"In top STRING, replaces first occurrence of second CHAR with first CHAR.",
StackTypes.Char)]
public class StringReplaceFirstCharExpression : StatelessExpression {
public StringReplaceFirstCharExpression() { }
[StorableConstructor]
protected StringReplaceFirstCharExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.StringStack.IsEmpty ||
interpreter.CharStack.Count < 2;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var str = interpreter.StringStack.Top;
var first = interpreter.CharStack[1];
var second = interpreter.CharStack.Top;
interpreter.CharStack.Remove(2);
var pos = str.IndexOf(first);
if (pos < 0)
return;
var result = str.Substring(0, pos) +
second +
str.Substring(Math.Min(pos + 1, str.Length - 1));
interpreter.StringStack.Top = result;
}
}
///
/// In top STRING, remove all occurrences of char
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.REMOVECHAR",
"In top STRING, remove all occurrences of top CHAR.",
StackTypes.Char)]
public class StringRemoveCharExpression : StatelessExpression {
public StringRemoveCharExpression() { }
[StorableConstructor]
protected StringRemoveCharExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.StringStack.IsEmpty ||
interpreter.CharStack.IsEmpty;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var c = interpreter.CharStack.Pop();
var result = interpreter.StringStack.Top.Trim(c);
interpreter.StringStack.Top = result;
}
}
///
/// Sets char at index in string
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.SETCHAR",
"Sets top CHAR at index in top STRING, whereby the index is taken from the INTEGER stack.",
StackTypes.Char | StackTypes.Integer)]
public class StringSetCharExpression : StatelessExpression {
public StringSetCharExpression() { }
[StorableConstructor]
protected StringSetCharExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.StringStack.IsEmpty ||
interpreter.CharStack.IsEmpty ||
interpreter.IntegerStack.IsEmpty;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var str = interpreter.StringStack.Top;
var c = interpreter.CharStack.Pop();
var x = interpreter.IntegerStack.Pop();
if (str.Length == 0) return;
if (str.Length == 1) {
interpreter.StringStack.Top = c.ToString();
return;
}
var pos = x.AsInt(str.Length);
var result = str.Substring(0, pos) + c + str.Substring(Math.Min(pos + 1, str.Length - 1));
interpreter.StringStack.Top = result;
}
}
///
/// Iterates over a string using the code on the exec stack.
///
[StorableClass]
[PushExpression(
StackTypes.String,
"STRING.ITERATE",
"Iterates over the top STRING using the CODE on the EXEC stack.",
StackTypes.Exec | StackTypes.Char, requiredBlockCount: 1)]
public class StringIterateExpression : StatelessExpression {
public StringIterateExpression() { }
[StorableConstructor]
protected StringIterateExpression(bool deserializing) : base(deserializing) { }
public override bool IsNoop(IInternalPushInterpreter interpreter) {
return interpreter.StringStack.IsEmpty ||
interpreter.ExecStack.IsEmpty;
}
public override void Eval(IInternalPushInterpreter interpreter) {
var str = interpreter.StringStack.Pop();
if (str.Length == 0) {
interpreter.ExecStack.Pop();
}
// If the rest of the string is empty, we're done iterating.
else if (str.Length == 1) {
interpreter.CharStack.Push(str[0]);
} else {
var pool = interpreter.PoolContainer.GetStatefulExpressionPool();
interpreter.CharStack.Push(str[0]);
interpreter.ExecStack.Push(
this,
StringPushExpression.Create(pool, str.Substring(1)),
interpreter.ExecStack.Top);
}
}
}
}