Free cookie consent management tool by TermsFeed Policy Generator

source: branches/PushGP/HeuristicLab.PushGP/HeuristicLab.Problems.ProgramSynthesis/Push/Expressions/StringExpressions.cs @ 14952

Last change on this file since 14952 was 14952, checked in by pkimmesw, 7 years ago

#2665 Added IsNoop to Expression, Made Expressions storable, Fixed Debugger, Fixed and improved problem data and result visualisation, Added custom ErcOption view, Added problem difficulty to problem data name

File size: 23.1 KB
Line 
1namespace HeuristicLab.Problems.ProgramSynthesis.Push.Expressions {
2  using System;
3  using System.Linq;
4
5  using Attributes;
6
7  using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
8
9  using Interpreter;
10  using Stack;
11
12  [StorableClass]
13  [PushExpression(StackTypes.String, "STRING.FROMINTEGER", StackTypes.Integer)]
14  public class StringFromIntegerExpression : StatelessExpression {
15    public StringFromIntegerExpression() { }
16    [StorableConstructor]
17    protected StringFromIntegerExpression(bool deserializing) : base(deserializing) { }
18
19    public override bool IsNoop(IInternalPushInterpreter interpreter) {
20      return interpreter.IntegerStack.IsEmpty;
21    }
22
23    public override void Eval(IInternalPushInterpreter interpreter) {
24      var value = interpreter.IntegerStack.Pop();
25      interpreter.StringStack.Push(value.ToString());
26    }
27  }
28
29  [StorableClass]
30  [PushExpression(StackTypes.String, "STRING.FROMFLOAT", StackTypes.Float)]
31  public class StringFromFloatExpression : StatelessExpression {
32    public StringFromFloatExpression() { }
33    [StorableConstructor]
34    protected StringFromFloatExpression(bool deserializing) : base(deserializing) { }
35
36    public override bool IsNoop(IInternalPushInterpreter interpreter) {
37      return interpreter.FloatStack.IsEmpty;
38    }
39
40    public override void Eval(IInternalPushInterpreter interpreter) {
41      var value = interpreter.FloatStack.Pop();
42      interpreter.StringStack.Push(value.ToString());
43    }
44  }
45
46  [StorableClass]
47  [PushExpression(StackTypes.String, "STRING.FROMBOOLEAN", StackTypes.Boolean)]
48  public class StringFromBooleanExpression : StatelessExpression {
49    public StringFromBooleanExpression() { }
50    [StorableConstructor]
51    protected StringFromBooleanExpression(bool deserializing) : base(deserializing) { }
52
53    public override bool IsNoop(IInternalPushInterpreter interpreter) {
54      return interpreter.BooleanStack.IsEmpty;
55    }
56
57    public override void Eval(IInternalPushInterpreter interpreter) {
58      var value = interpreter.BooleanStack.Pop();
59      interpreter.StringStack.Push(value.ToString());
60    }
61  }
62
63  [StorableClass]
64  [PushExpression(StackTypes.String, "STRING.FROMCHAR", StackTypes.Char)]
65  public class StringFromCharExpression : StatelessExpression {
66    public StringFromCharExpression() { }
67    [StorableConstructor]
68    protected StringFromCharExpression(bool deserializing) : base(deserializing) { }
69
70    public override bool IsNoop(IInternalPushInterpreter interpreter) {
71      return interpreter.CharStack.IsEmpty;
72    }
73
74    public override void Eval(IInternalPushInterpreter interpreter) {
75      var value = interpreter.CharStack.Pop();
76      interpreter.StringStack.Push(value.ToString());
77    }
78  }
79
80  [StorableClass]
81  [PushExpression(StackTypes.String, "STRING.CONCAT")]
82  public class StringConcatExpression : StatelessExpression {
83    public StringConcatExpression() { }
84    [StorableConstructor]
85    protected StringConcatExpression(bool deserializing) : base(deserializing) { }
86
87    public override bool IsNoop(IInternalPushInterpreter interpreter) {
88      return interpreter.StringStack.Count < 2 ||
89             interpreter.StringStack.Top.Length + interpreter.StringStack[1].Length >= interpreter.Configuration.MaxStringLength;
90    }
91
92    public override void Eval(IInternalPushInterpreter interpreter) {
93      var str = interpreter.StringStack.Pop();
94      interpreter.StringStack.SetTop(str + interpreter.StringStack.Top);
95    }
96  }
97
98  /// <summary>
99  /// Conj char onto string
100  /// </summary>
101  [StorableClass]
102  [PushExpression(StackTypes.String, "STRING.CONJCHAR", StackTypes.Char)]
103  public class StringConjCharExpression : StatelessExpression {
104    public StringConjCharExpression() { }
105    [StorableConstructor]
106    protected StringConjCharExpression(bool deserializing) : base(deserializing) { }
107
108    public override bool IsNoop(IInternalPushInterpreter interpreter) {
109      return interpreter.StringStack.IsEmpty ||
110             interpreter.CharStack.IsEmpty ||
111             interpreter.StringStack.Top.Length + 1 >= interpreter.Configuration.MaxStringLength;
112    }
113
114    public override void Eval(IInternalPushInterpreter interpreter) {
115      var c = interpreter.CharStack.Pop();
116      interpreter.StringStack.SetTop(interpreter.StringStack.Top + c);
117    }
118  }
119
120  [StorableClass]
121  [PushExpression(StackTypes.String, "STRING.TAKE", StackTypes.Integer)]
122  public class StringTakeExpression : StatelessExpression {
123    public StringTakeExpression() { }
124    [StorableConstructor]
125    protected StringTakeExpression(bool deserializing) : base(deserializing) { }
126
127    public override bool IsNoop(IInternalPushInterpreter interpreter) {
128      return interpreter.StringStack.IsEmpty ||
129             interpreter.IntegerStack.IsEmpty;
130    }
131
132    public override void Eval(IInternalPushInterpreter interpreter) {
133      var value = interpreter.IntegerStack.Pop();
134      var str = interpreter.StringStack.Top;
135
136      if (str.Length == 0)
137        return;
138
139      value = Math.Abs(value) % str.Length;
140      interpreter.StringStack.SetTop(str.Substring(0, (int)value));
141    }
142  }
143
144  [StorableClass]
145  [PushExpression(StackTypes.String, "STRING.SUBSTRING", StackTypes.Integer)]
146  public class StringSubstringExpression : StatelessExpression {
147    public StringSubstringExpression() { }
148    [StorableConstructor]
149    protected StringSubstringExpression(bool deserializing) : base(deserializing) { }
150
151    public override bool IsNoop(IInternalPushInterpreter interpreter) {
152      return interpreter.StringStack.IsEmpty ||
153             interpreter.IntegerStack.Count < 2;
154    }
155
156    public override void Eval(IInternalPushInterpreter interpreter) {
157      var str = interpreter.StringStack.Top;
158      var firstValue = interpreter.IntegerStack[1];
159      var secondValue = interpreter.IntegerStack.Top;
160      interpreter.IntegerStack.Remove(2);
161
162      var first = Math.Min(str.Length - 1, Math.Max(firstValue, 0));
163      var second = Math.Min(str.Length - 1, Math.Max(secondValue, first));
164      var length = second - first;
165
166      if (length > 0)
167        interpreter.StringStack.SetTop(str.Substring((int)first, (int)length));
168    }
169  }
170
171  [StorableClass]
172  [PushExpression(StackTypes.String, "STRING.FIRST")]
173  public class StringFirstExpression : StatelessExpression {
174    public StringFirstExpression() { }
175    [StorableConstructor]
176    protected StringFirstExpression(bool deserializing) : base(deserializing) { }
177
178    public override bool IsNoop(IInternalPushInterpreter interpreter) {
179      return interpreter.StringStack.IsEmpty ||
180             interpreter.StringStack.Top.Length == 0;
181    }
182
183    public override void Eval(IInternalPushInterpreter interpreter) {
184      interpreter.StringStack.SetTop(interpreter.StringStack.Top[0].ToString());
185    }
186  }
187
188  [StorableClass]
189  [PushExpression(StackTypes.String, "STRING.LAST")]
190  public class StringLastExpression : StatelessExpression {
191    public StringLastExpression() { }
192    [StorableConstructor]
193    protected StringLastExpression(bool deserializing) : base(deserializing) { }
194
195    public override bool IsNoop(IInternalPushInterpreter interpreter) {
196      return interpreter.StringStack.IsEmpty ||
197             interpreter.StringStack.Top.Length == 0;
198    }
199
200    public override void Eval(IInternalPushInterpreter interpreter) {
201      var str = interpreter.StringStack.Top;
202      var c = str[str.Length - 1].ToString();
203      interpreter.StringStack.SetTop(c);
204    }
205  }
206
207  [StorableClass]
208  [PushExpression(StackTypes.String, "STRING.NTH", StackTypes.Integer)]
209  public class StringNthExpression : StatelessExpression {
210    public StringNthExpression() { }
211    [StorableConstructor]
212    protected StringNthExpression(bool deserializing) : base(deserializing) { }
213
214    public override bool IsNoop(IInternalPushInterpreter interpreter) {
215      return interpreter.StringStack.IsEmpty ||
216             interpreter.IntegerStack.IsEmpty ||
217             interpreter.StringStack.Top.Length == 0;
218    }
219
220    public override void Eval(IInternalPushInterpreter interpreter) {
221      var str = interpreter.StringStack.Top;
222      var index = str.Length == 1 ? 0 : (int)Math.Abs(interpreter.IntegerStack.Pop()) % str.Length;
223      var c = str[index].ToString();
224      interpreter.StringStack.SetTop(c);
225    }
226  }
227
228  [StorableClass]
229  [PushExpression(StackTypes.String, "STRING.REST")]
230  public class StringRestExpression : StatelessExpression {
231    public StringRestExpression() { }
232    [StorableConstructor]
233    protected StringRestExpression(bool deserializing) : base(deserializing) { }
234
235    public override bool IsNoop(IInternalPushInterpreter interpreter) {
236      return interpreter.StringStack.IsEmpty ||
237             interpreter.StringStack.Top.Length == 0;
238    }
239
240    public override void Eval(IInternalPushInterpreter interpreter) {
241      var str = interpreter.StringStack.Top;
242      interpreter.StringStack.SetTop(str.Length == 1 ? string.Empty : str.Substring(1, str.Length - 1));
243    }
244  }
245
246  [StorableClass]
247  [PushExpression(StackTypes.String, "STRING.BUTLAST")]
248  public class StringButLastExpression : StatelessExpression {
249    public StringButLastExpression() { }
250    [StorableConstructor]
251    protected StringButLastExpression(bool deserializing) : base(deserializing) { }
252
253    public override bool IsNoop(IInternalPushInterpreter interpreter) {
254      return interpreter.StringStack.IsEmpty ||
255             interpreter.StringStack.Top.Length == 0;
256    }
257
258    public override void Eval(IInternalPushInterpreter interpreter) {
259      var str = interpreter.StringStack.Top;
260      interpreter.StringStack.SetTop(str.Length == 1 ? string.Empty : str.Substring(0, str.Length - 1));
261    }
262  }
263
264  [StorableClass]
265  [PushExpression(StackTypes.String, "STRING.LENGTH", StackTypes.Integer)]
266  public class StringLengthExpression : StatelessExpression {
267    public StringLengthExpression() { }
268    [StorableConstructor]
269    protected StringLengthExpression(bool deserializing) : base(deserializing) { }
270
271    public override bool IsNoop(IInternalPushInterpreter interpreter) {
272      return interpreter.StringStack.IsEmpty;
273    }
274
275    public override void Eval(IInternalPushInterpreter interpreter) {
276      var str = interpreter.StringStack.Pop();
277      interpreter.IntegerStack.Push(str.Length);
278    }
279  }
280
281  [StorableClass]
282  [PushExpression(StackTypes.String, "STRING.REVERSE")]
283  public class StringReverseExpression : StatelessExpression {
284    public StringReverseExpression() { }
285    [StorableConstructor]
286    protected StringReverseExpression(bool deserializing) : base(deserializing) { }
287
288    public override bool IsNoop(IInternalPushInterpreter interpreter) {
289      return interpreter.StringStack.IsEmpty;
290    }
291
292    public override void Eval(IInternalPushInterpreter interpreter) {
293      interpreter.StringStack.SetTop(interpreter.StringStack.Top.Reverse().ToString());
294    }
295  }
296
297  [StorableClass]
298  [PushExpression(StackTypes.String, "STRING.PARSETOCHARS")]
299  public class StringParseToCharsExpression : StatelessExpression {
300    public StringParseToCharsExpression() { }
301    [StorableConstructor]
302    protected StringParseToCharsExpression(bool deserializing) : base(deserializing) { }
303
304    public override bool IsNoop(IInternalPushInterpreter interpreter) {
305      return interpreter.StringStack.IsEmpty;
306    }
307
308    public override void Eval(IInternalPushInterpreter interpreter) {
309      if (interpreter.StringStack.Top.Length == 0) {
310        interpreter.StringStack.Pop();
311        return;
312      }
313
314      var str = interpreter.StringStack.Top;
315      interpreter.StringStack.SetTop(str[0].ToString());
316
317      if (str.Length > 1) {
318        var chars = new string[str.Length - 1];
319        for (var i = 0; i < str.Length - 1; i++) {
320          chars[i] = str[i + 1].ToString();
321        }
322
323        interpreter.StringStack.Push(chars);
324      }
325    }
326  }
327
328  [StorableClass]
329  [PushExpression(StackTypes.String, "STRING.SPLIT")]
330  public class StringSplitExpression : StatelessExpression {
331    public StringSplitExpression() { }
332    [StorableConstructor]
333    protected StringSplitExpression(bool deserializing) : base(deserializing) { }
334
335    public override bool IsNoop(IInternalPushInterpreter interpreter) {
336      return interpreter.StringStack.IsEmpty ||
337             interpreter.StringStack.Top.Trim().Split().Length <= 1;
338    }
339
340    public override void Eval(IInternalPushInterpreter interpreter) {
341      var words = interpreter.StringStack.Top.Trim().Split();
342
343      interpreter.StringStack.SetTop(words[0]);
344      interpreter.StringStack.Push(words, 1);
345    }
346  }
347
348  /// <summary>
349  /// True if top string is empty
350  /// </summary>
351  [StorableClass]
352  [PushExpression(StackTypes.String, "STRING.EMPTYSTRING", StackTypes.Boolean)]
353  public class StringEmptyStringExpression : StatelessExpression {
354    public StringEmptyStringExpression() { }
355    [StorableConstructor]
356    protected StringEmptyStringExpression(bool deserializing) : base(deserializing) { }
357
358    public override bool IsNoop(IInternalPushInterpreter interpreter) {
359      return interpreter.StringStack.IsEmpty;
360    }
361
362    public override void Eval(IInternalPushInterpreter interpreter) {
363      var str = interpreter.StringStack.Pop();
364      interpreter.BooleanStack.Push(str.Length == 0);
365    }
366  }
367
368  /// <summary>
369  /// True if top string is a substring of second string; false otherwise
370  /// </summary>
371  [StorableClass]
372  [PushExpression(StackTypes.String, "STRING.CONTAINS", StackTypes.Boolean)]
373  public class StringContainsExpression : StatelessExpression {
374    public StringContainsExpression() { }
375    [StorableConstructor]
376    protected StringContainsExpression(bool deserializing) : base(deserializing) { }
377
378    public override bool IsNoop(IInternalPushInterpreter interpreter) {
379      return interpreter.StringStack.Count < 2;
380    }
381
382    public override void Eval(IInternalPushInterpreter interpreter) {
383      var first = interpreter.StringStack[1];
384      var second = interpreter.StringStack.Top;
385      interpreter.StringStack.Remove(2);
386
387      interpreter.BooleanStack.Push(first.IndexOf(second, StringComparison.Ordinal) >= 0);
388    }
389  }
390
391  /// <summary>
392  /// True if the top char is in the top string
393  /// </summary>
394  [StorableClass]
395  [PushExpression(StackTypes.String, "STRING.CONTAINSCHAR", StackTypes.Boolean | StackTypes.Char)]
396  public class StringContainsCharExpression : StatelessExpression {
397    public StringContainsCharExpression() { }
398    [StorableConstructor]
399    protected StringContainsCharExpression(bool deserializing) : base(deserializing) { }
400
401    public override bool IsNoop(IInternalPushInterpreter interpreter) {
402      return interpreter.StringStack.IsEmpty ||
403             interpreter.CharStack.IsEmpty;
404    }
405
406    public override void Eval(IInternalPushInterpreter interpreter) {
407      var str = interpreter.StringStack.Pop();
408      var c = interpreter.CharStack.Pop();
409
410      interpreter.BooleanStack.Push(str.IndexOf(c) >= 0);
411    }
412  }
413
414  /// <summary>
415  /// Puts on the integer stack the index of the top char in the top string
416  /// </summary>
417  [StorableClass]
418  [PushExpression(StackTypes.String, "STRING.INDEXOFCHAR", StackTypes.Integer | StackTypes.Char)]
419  public class StringIndexOfCharExpression : StatelessExpression {
420    public StringIndexOfCharExpression() { }
421    [StorableConstructor]
422    protected StringIndexOfCharExpression(bool deserializing) : base(deserializing) { }
423
424    public override bool IsNoop(IInternalPushInterpreter interpreter) {
425      return interpreter.StringStack.IsEmpty ||
426             interpreter.CharStack.IsEmpty;
427    }
428
429    public override void Eval(IInternalPushInterpreter interpreter) {
430      var str = interpreter.StringStack.Pop();
431      var c = interpreter.CharStack.Pop();
432
433      interpreter.IntegerStack.Push(str.IndexOf(c));
434    }
435  }
436
437  /// <summary>
438  /// The number of times the top char is in the top string
439  /// </summary>
440  [StorableClass]
441  [PushExpression(StackTypes.String, "STRING.OCCURENCESOFCHAR", StackTypes.Integer | StackTypes.Char)]
442  public class StringOccurrencesOfCharExpression : StatelessExpression {
443    public StringOccurrencesOfCharExpression() { }
444    [StorableConstructor]
445    protected StringOccurrencesOfCharExpression(bool deserializing) : base(deserializing) { }
446
447    public override bool IsNoop(IInternalPushInterpreter interpreter) {
448      return interpreter.StringStack.IsEmpty ||
449             interpreter.CharStack.IsEmpty;
450    }
451
452    public override void Eval(IInternalPushInterpreter interpreter) {
453      var str = interpreter.StringStack.Pop();
454      var c = interpreter.CharStack.Pop();
455
456      var count = 0;
457      for (var i = 0; i < str.Length; i++)
458        if (str[i] == c) count++;
459
460      interpreter.IntegerStack.Push(count);
461    }
462  }
463
464  /// <summary>
465  /// In third string on stack, replaces second string with first string
466  /// </summary>
467  [StorableClass]
468  [PushExpression(StackTypes.String, "STRING.REPLACE")]
469  public class StringReplaceExpression : StatelessExpression {
470    public StringReplaceExpression() { }
471    [StorableConstructor]
472    protected StringReplaceExpression(bool deserializing) : base(deserializing) { }
473
474    public override bool IsNoop(IInternalPushInterpreter interpreter) {
475      return interpreter.StringStack.Count < 3;
476    }
477
478    public override void Eval(IInternalPushInterpreter interpreter) {
479      var first = interpreter.StringStack[1];
480      var second = interpreter.StringStack.Top;
481      interpreter.StringStack.Remove(2);
482
483      if (first.Length == 0 || second.Length == 0)
484        interpreter.StringStack.SetTop(first);
485      else {
486        var result = first.Replace(second, interpreter.StringStack.Top);
487        interpreter.StringStack.SetTop(result);
488      }
489    }
490  }
491
492  /// <summary>
493  /// In third string on stack, replaces first occurence of second string with first string
494  /// </summary>
495  [StorableClass]
496  [PushExpression(StackTypes.String, "STRING.REPLACEFIRST")]
497  public class StringReplaceLastExpression : StatelessExpression {
498    public StringReplaceLastExpression() { }
499    [StorableConstructor]
500    protected StringReplaceLastExpression(bool deserializing) : base(deserializing) { }
501
502    public override bool IsNoop(IInternalPushInterpreter interpreter) {
503      return interpreter.StringStack.Count < 3;
504    }
505
506    public override void Eval(IInternalPushInterpreter interpreter) {
507      var first = interpreter.StringStack[0];
508      var second = interpreter.StringStack[1];
509      var third = interpreter.StringStack[2];
510      interpreter.StringStack.Remove(2);
511
512      var result = ReplaceFirst(third, second, first);
513      interpreter.StringStack.SetTop(result);
514    }
515
516    private static string ReplaceFirst(string text, string search, string replace) {
517      int pos = text.IndexOf(search);
518      if (pos < 0) {
519        return text;
520      }
521      return text.Substring(0, pos) + replace + text.Substring(pos + search.Length);
522    }
523  }
524
525  /// <summary>
526  /// In top string on stack, replaces all occurences of second char with first char
527  /// </summary>
528  [StorableClass]
529  [PushExpression(StackTypes.String, "STRING.REPLACECHAR", StackTypes.Char)]
530  public class StringReplaceCharExpression : StatelessExpression {
531    public StringReplaceCharExpression() { }
532    [StorableConstructor]
533    protected StringReplaceCharExpression(bool deserializing) : base(deserializing) { }
534
535    public override bool IsNoop(IInternalPushInterpreter interpreter) {
536      return interpreter.StringStack.IsEmpty ||
537             interpreter.CharStack.Count < 2;
538    }
539
540    public override void Eval(IInternalPushInterpreter interpreter) {
541      var first = interpreter.CharStack[1];
542      var second = interpreter.CharStack.Top;
543      interpreter.CharStack.Remove(2);
544
545      var result = interpreter.StringStack.Top.Replace(first, second);
546      interpreter.StringStack.SetTop(result);
547    }
548  }
549
550  /// <summary>
551  /// In top string on stack, replaces first occurence of second char with first char
552  /// </summary>
553  [StorableClass]
554  [PushExpression(StackTypes.String, "STRING.REPLACEFIRSTCHAR", StackTypes.Char)]
555  public class StringReplaceLastCharExpression : StatelessExpression {
556    public StringReplaceLastCharExpression() { }
557    [StorableConstructor]
558    protected StringReplaceLastCharExpression(bool deserializing) : base(deserializing) { }
559
560    public override bool IsNoop(IInternalPushInterpreter interpreter) {
561      return interpreter.StringStack.IsEmpty ||
562             interpreter.CharStack.Count < 2;
563    }
564
565    public override void Eval(IInternalPushInterpreter interpreter) {
566      var str = interpreter.StringStack.Top;
567      var first = interpreter.CharStack[1];
568      var second = interpreter.CharStack.Top;
569      interpreter.CharStack.Remove(2);
570
571      var pos = str.IndexOf(first);
572
573      if (pos < 0)
574        return;
575
576      var result = str.Substring(0, pos) +
577                   second +
578                   str.Substring(Math.Min(pos + 2, str.Length - 1));
579
580      interpreter.StringStack.SetTop(result);
581    }
582  }
583
584  /// <summary>
585  /// In top string on stack, remove all occurences of char
586  /// </summary>
587  [StorableClass]
588  [PushExpression(StackTypes.String, "STRING.REMOVECHAR", StackTypes.Char)]
589  public class StringRemoveCharExpression : StatelessExpression {
590    public StringRemoveCharExpression() { }
591    [StorableConstructor]
592    protected StringRemoveCharExpression(bool deserializing) : base(deserializing) { }
593
594    public override bool IsNoop(IInternalPushInterpreter interpreter) {
595      return interpreter.StringStack.IsEmpty ||
596             interpreter.CharStack.IsEmpty;
597    }
598
599    public override void Eval(IInternalPushInterpreter interpreter) {
600      var c = interpreter.CharStack.Pop();
601      var result = interpreter.StringStack.Top.Trim(c);
602      interpreter.StringStack.SetTop(result);
603    }
604  }
605
606  /// <summary>
607  /// Returns a function that sets char at index in string
608  /// </summary>
609  [StorableClass]
610  [PushExpression(StackTypes.String, "STRING.SETCHAR", StackTypes.Char | StackTypes.Integer)]
611  public class StringSetCharExpression : StatelessExpression {
612    public StringSetCharExpression() { }
613    [StorableConstructor]
614    protected StringSetCharExpression(bool deserializing) : base(deserializing) { }
615
616    public override bool IsNoop(IInternalPushInterpreter interpreter) {
617      return interpreter.StringStack.IsEmpty ||
618             interpreter.CharStack.IsEmpty ||
619             interpreter.IntegerStack.IsEmpty;
620    }
621
622    public override void Eval(IInternalPushInterpreter interpreter) {
623      var str = interpreter.StringStack.Top;
624      var i = (int)interpreter.IntegerStack.Pop();
625      var c = interpreter.CharStack.Pop();
626
627      if (str.Length == 0) {
628        interpreter.StringStack.Pop();
629        return;
630      }
631
632      var pos = str.Length == 1 ? 0 : Math.Abs(i) % (str.Length - 1);
633      var result = str.Substring(0, pos) + c + str.Substring(Math.Min(pos + 2, str.Length - 1));
634
635      interpreter.StringStack.SetTop(result);
636    }
637  }
638}
Note: See TracBrowser for help on using the repository browser.