1 module orelang.operator.StringOperators;
2 import orelang.expression.ImmediateValue,
3        orelang.operator.IOperator,
4        orelang.Engine,
5        orelang.Value;
6 import std.algorithm,
7        std..string,
8        std.array;
9 
10 /**
11  * This module provides operators with string.
12  * Current provided operators:
13  *  - string-concat
14  *  - string-join
15  *  - string-split
16  *  - string-length
17  *  - string-slice
18  *  - as-string
19  *  - string-repeat
20  */
21 
22 /**
23  * concat strings into a string 
24  */
25 class StringConcatOperator : IOperator {
26   /**
27    * call
28    */
29   public Value call(Engine engine, Value[] args) {
30     if (args[0].type == ValueType.Array && args.length == 1) {
31       return new Value(
32         engine.eval(args[0]).getArray
33           .map!(arg => (arg.type == ValueType.SymbolValue ? engine.eval(arg) : arg).getString)
34           .join
35         );
36     } else if (args[0].type == ValueType.ImmediateValue && args[0].getImmediateValue.value.type == ValueType.Array && args.length == 1) {
37       return new Value(
38         args[0].getImmediateValue.value.getArray
39           .map!(arg => (arg.type == ValueType.SymbolValue ? engine.eval(arg) : arg).getString)
40           .join
41         );
42     } else {
43       return new Value(
44         args
45           .map!(arg => engine.eval(arg).getString)
46           .join
47       );
48     }
49   }
50 }
51 
52 /**
53  * join strings into a string with a separator
54  */
55 class StringJoinOperator : IOperator {
56   /**
57    * call
58    */
59   public Value call(Engine engine, Value[] args) {
60     if (args[0].type == ValueType.Array && args.length >= 1) {
61       return new Value(
62         engine.eval(args[0]).getArray
63           .map!(arg => engine.eval(arg).getString)
64           .join(args.length == 1 ? "" : engine.eval(args[1]).getString)
65         );
66     } else if (args[0].type == ValueType.ImmediateValue && args[0].getImmediateValue.value.type == ValueType.Array && args.length >= 1) {
67       return new Value(
68         args[0].getImmediateValue.value.getArray
69           .map!(arg => (arg.type == ValueType.SymbolValue ? engine.eval(arg) : arg).getString)
70           .join(args.length == 1 ? "" : engine.eval(args[1]).getString)
71         );
72     } else {
73       return new Value(
74         args[0..$-1]
75           .map!(arg => engine.eval(arg).getString)
76           .join(engine.eval(args[$-1]).getString)
77       );
78     }
79   }
80 }
81 
82 /**
83  * split a string into strings with a separator
84  */ 
85 class StringSplitOperator : IOperator {
86   /**
87    * call
88    */
89   public Value call(Engine engine, Value[] args) {
90     Value[] rets;
91     
92     foreach (value; engine.eval(args[0]).getString.split(args.length == 1 ? "" : engine.eval(args[1]).getString)) {
93       rets ~= new Value(value);
94     }
95 
96     return new Value(rets);
97   }
98 }
99 
100 /**
101  * return a length of the string
102  */ 
103 class StringLengthOperator : IOperator {
104   /**
105    * call
106    */
107   public Value call(Engine engine, Value[] args) {
108     import std.conv;
109     
110     if (args[0].type == ValueType.String) {
111       return new Value(args[0].getString.length.to!double);
112     } else {
113       Value eargs0 = engine.eval(args[0]);
114 
115       if (eargs0.type == ValueType.String) {
116         return new Value(eargs0.getString.length.to!double);
117       } else {
118         throw new Exception("[string-length] Invalid argument was given");
119       }
120     }
121   }
122 }
123 
124 /**
125  * Tak a slice from the range of the string
126  */
127 class StringSliceOperator : IOperator {
128   /**
129    * call
130    */
131   import std.conv;
132   public Value call(Engine engine, Value[] args) {
133     string str;
134     Value eargs0 = engine.eval(args[0]);
135     str = eargs0.getString;
136     
137     long slice1  = engine.eval(args[1]).getNumeric.to!long,
138          slice2  = engine.eval(args[2]).getNumeric.to!long;
139 
140     if ((0 <= slice1 && 0 <= slice2) && (slice1 <= str.length && slice2 <= str.length)) {
141       return new Value(str[slice1..slice2]);
142     } else {
143       throw new Exception("[string-slice] Invalid");
144     }
145   }
146 }
147 
148 class AsStringOperator : IOperator {
149   /**
150    * call
151    */
152   import std.conv;
153   public Value call(Engine engine, Value[] args) {
154     return new Value(new ImmediateValue(new Value((args[0].type == ValueType.SymbolValue ? engine.eval(args[0]) : args[0]).toString)));
155   }
156 }
157 
158 class StringRepeatOperator : IOperator {
159   /**
160    * call
161    */
162   import std.algorithm,
163          std..string,
164          std.array,
165          std.range,
166          std.conv;
167   public Value call(Engine engine, Value[] args) {
168     string pattern;
169     long n;
170 
171     if (args[0].type == ValueType.SymbolValue) {
172       pattern = engine.eval(args[0]).getString;
173     } else if (args[0].type == ValueType.String) {
174       pattern = args[0].getString;
175     } 
176 
177     if (args[1].type == ValueType.Numeric) {
178       n = args[1].getNumeric.to!long;
179     } else {
180       n = engine.eval(args[1]).getNumeric.to!long;
181     }
182 
183     return new Value(n.iota.map!(i => pattern).array.join);
184   }
185 }
186 
187 class StringChompOperator : IOperator {
188   /**
189    * call
190    */
191   import std..string;
192 
193   public Value call(Engine engine, Value[] args) {
194     return new Value(engine.eval(args[0]).getString.chomp);
195   }
196 }