PageRenderTime 49ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/lisp/String.cs

http://github.com/toshok/shelisp
C# | 471 lines | 388 code | 78 blank | 5 comment | 84 complexity | 0bdb59f637a7e8ed889fc72f934e0f25 MD5 | raw file
Possible License(s): GPL-3.0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Text.RegularExpressions;
  5. namespace Shelisp {
  6. public class String : Array {
  7. public String (string s)
  8. {
  9. this.native_string = new StringBuilder(s);
  10. }
  11. public override bool LispEqual (Shelisp.Object other)
  12. {
  13. if (!(other is String))
  14. return false;
  15. return this.native_string.ToString() == ((String)other).native_string.ToString();
  16. }
  17. public StringBuilder native_string;
  18. public override int Length {
  19. get { return native_string.Length; }
  20. }
  21. public override Shelisp.Object this[int index]
  22. {
  23. get { return (Number)(int)native_string[index]; }
  24. set {
  25. if (Number.IsInt(value))
  26. throw new WrongTypeArgumentException ("integer", value ?? L.Qnil);
  27. native_string[index] = (char)Number.ToInt(value);
  28. }
  29. }
  30. public override IEnumerator<Shelisp.Object> GetEnumerator ()
  31. {
  32. for (int i = 0; i < native_string.Length; i ++)
  33. yield return (int)native_string[i];
  34. }
  35. public override int GetHashCode ()
  36. {
  37. return native_string.ToString().GetHashCode();
  38. }
  39. public override string ToString (string format_type)
  40. {
  41. switch (format_type) {
  42. case "princ":
  43. return native_string.ToString();
  44. case "prin1":
  45. return string.Concat ("\"", native_string.ToString(), "\"");
  46. default:
  47. throw new NotSupportedException (string.Format ("unsupported format type `{0}'", format_type));
  48. }
  49. }
  50. public static implicit operator System.String (String str)
  51. {
  52. return str.native_string.ToString();
  53. }
  54. [LispBuiltin]
  55. public static Shelisp.Object Fstring_match(L l, Shelisp.Object regex, Shelisp.Object str, [LispOptional] Shelisp.Object start)
  56. {
  57. if (!(regex is Shelisp.String))
  58. throw new WrongTypeArgumentException ("stringp", regex);
  59. if (!(str is Shelisp.String))
  60. throw new WrongTypeArgumentException ("stringp", str);
  61. if (!L.NILP(start) && !Number.IsInt(start))
  62. throw new WrongTypeArgumentException ("integerp", start);
  63. string regex_s = (string)(String)regex;
  64. string str_s = (string)(String)str;
  65. int start_i = L.NILP(start) ? 0 : (int)((Shelisp.Number)start).boxed;
  66. regex_s = regex_s.Replace ("\\(", "OMGOPENPAREN").Replace ("(", "\\(").Replace ("OMGOPENPAREN", "(");
  67. regex_s = regex_s.Replace ("\\)", "OMGCLOSEPAREN").Replace (")", "\\)").Replace ("OMGCLOSEPAREN", ")");
  68. regex_s = regex_s.Replace ("\\'", "$");
  69. var re = new Regex (regex_s);
  70. var match = re.Match (str_s, start_i);
  71. if (match.Success == false) {
  72. // do we need to clear out L.match_data?
  73. return L.Qnil;
  74. }
  75. int[] match_data = new int[match.Groups.Count * 2];
  76. for (int i = 0; i < match.Groups.Count; i ++) {
  77. match_data[i*2] = match.Groups[i].Index;
  78. match_data[i*2 + 1] = match.Groups[i].Index + match.Groups[i].Length;
  79. }
  80. L.match_data = match_data;
  81. return new Shelisp.Number (match.Groups[0].Index);
  82. }
  83. [LispBuiltin]
  84. public static Shelisp.Object Fmatch_data (L l)
  85. {
  86. if (L.match_data == null)
  87. return L.Qnil;
  88. return L.int_array_to_list (L.match_data);
  89. }
  90. [LispBuiltin (DocString = @"Set internal data on last search match from elements of LIST.
  91. LIST should have been created by calling `match-data' previously.
  92. If optional arg RESEAT is non-nil, make markers on LIST point nowhere.")]
  93. public static Shelisp.Object Fset_match_data (L l, Shelisp.Object data, Shelisp.Object reseat)
  94. {
  95. if (!L.LISTP(data))
  96. throw new WrongTypeArgumentException ("listp", data);
  97. if (L.NILP (data))
  98. L.match_data = null;
  99. List data_l = (List)data;
  100. L.match_data = new int[data_l.Length];
  101. int i = 0;
  102. foreach (var item in data_l)
  103. L.match_data[i++] = (int)(Number)item;
  104. return data;
  105. }
  106. [LispBuiltin]
  107. public static Shelisp.Object Fmatch_end (L l, Shelisp.Object idx, [LispOptional] Shelisp.Object str)
  108. {
  109. if (!L.NILP(idx) && !Number.IsInt(idx))
  110. throw new WrongTypeArgumentException ("integerp", idx);
  111. int idx_i = (int)((Shelisp.Number)idx).boxed;
  112. if (L.match_data == null)
  113. return L.Qnil;
  114. return idx_i > L.match_data.Length/2 ? L.Qnil : new Shelisp.Number (L.match_data[idx_i * 2 + 1]);
  115. }
  116. [LispBuiltin]
  117. public static Shelisp.Object Fmatch_beginning (L l, Shelisp.Object idx, [LispOptional] Shelisp.Object str)
  118. {
  119. if (!L.NILP(idx) && !Number.IsInt(idx))
  120. throw new WrongTypeArgumentException ("integerp", idx);
  121. int idx_i = (int)((Shelisp.Number)idx).boxed;
  122. if (L.match_data == null)
  123. return L.Qnil;
  124. return idx_i > L.match_data.Length/2 ? L.Qnil : new Shelisp.Number (L.match_data[idx_i * 2]);
  125. }
  126. [LispBuiltin (DocString = @"Replace text matched by last search with NEWTEXT.
  127. Leave point at the end of the replacement text.
  128. If second arg FIXEDCASE is non-nil, do not alter case of replacement text.
  129. Otherwise maybe capitalize the whole text, or maybe just word initials,
  130. based on the replaced text.
  131. If the replaced text has only capital letters
  132. and has at least one multiletter word, convert NEWTEXT to all caps.
  133. Otherwise if all words are capitalized in the replaced text,
  134. capitalize each word in NEWTEXT.
  135. If third arg LITERAL is non-nil, insert NEWTEXT literally.
  136. Otherwise treat `\\' as special:
  137. `\\&' in NEWTEXT means substitute original matched text.
  138. `\\N' means substitute what matched the Nth `\\(...\\)'.
  139. If Nth parens didn't match, substitute nothing.
  140. `\\\\' means insert one `\\'.
  141. Case conversion does not apply to these substitutions.
  142. FIXEDCASE and LITERAL are optional arguments.
  143. The optional fourth argument STRING can be a string to modify.
  144. This is meaningful when the previous match was done against STRING,
  145. using `string-match'. When used this way, `replace-match'
  146. creates and returns a new string made by copying STRING and replacing
  147. the part of STRING that was matched.
  148. The optional fifth argument SUBEXP specifies a subexpression;
  149. it says to replace just that subexpression with NEWTEXT,
  150. rather than replacing the entire matched text.
  151. This is, in a vague sense, the inverse of using `\\N' in NEWTEXT;
  152. `\\N' copies subexp N into NEWTEXT, but using N as SUBEXP puts
  153. NEWTEXT in place of subexp N.
  154. This is useful only after a regular expression search or match,
  155. since only regular expressions have distinguished subexpressions.")]
  156. public static Shelisp.Object Freplace_match (L l, Shelisp.Object newtext, [LispOptional] Shelisp.Object fixedcase, Shelisp.Object literal, Shelisp.Object str, Shelisp.Object subexp)
  157. {
  158. if (!L.NILP (str)) {
  159. int start = L.match_data[0];
  160. int end = L.match_data[1];
  161. string str_s = (string)(Shelisp.String)str;
  162. return (Shelisp.String)System.String.Concat (str_s.Substring (0, start), newtext.ToString("princ"), str_s.Substring (end));
  163. }
  164. else {
  165. Console.WriteLine ("replace-match not implemented"); return str;
  166. }
  167. }
  168. [LispBuiltin]
  169. public static Shelisp.Object Fsubstring (L l, Shelisp.Object str, Shelisp.Object start, [LispOptional] Shelisp.Object end)
  170. {
  171. if (!(str is Shelisp.String))
  172. throw new WrongTypeArgumentException ("stringp", str);
  173. if (!Number.IsInt(start))
  174. throw new WrongTypeArgumentException ("integerp", start);
  175. if (!L.NILP(end) && !Number.IsInt(end))
  176. throw new WrongTypeArgumentException ("integerp", end);
  177. string str_s = (string)(Shelisp.String)str;
  178. int start_i = Number.ToInt(start);
  179. int end_i = L.NILP(end) ? str_s.Length : Number.ToInt(end);
  180. if (start_i < 0)
  181. start_i += str_s.Length;
  182. if (end_i < 0)
  183. end_i += str_s.Length;
  184. return (Shelisp.String)str_s.Substring (start_i, end_i - start_i);
  185. }
  186. [LispBuiltin (DocString = @"Return a substring of STRING, without text properties.
  187. It starts at index FROM and ends before TO.
  188. TO may be nil or omitted; then the substring runs to the end of STRING.
  189. If FROM is nil or omitted, the substring starts at the beginning of STRING.
  190. If FROM or TO is negative, it counts from the end.
  191. With one argument, just copy STRING without its properties.")]
  192. public static Shelisp.Object Fsubstring_no_properties (L l, Shelisp.Object str, [LispOptional] Shelisp.Object from, Shelisp.Object to)
  193. {
  194. if (!(str is Shelisp.String))
  195. throw new WrongTypeArgumentException ("stringp", str);
  196. if (!L.NILP(from) && !Number.IsInt(from))
  197. throw new WrongTypeArgumentException ("integerp", from);
  198. if (!L.NILP(to) && !Number.IsInt(to))
  199. throw new WrongTypeArgumentException ("integerp", to);
  200. string str_s = (string)(Shelisp.String)str;
  201. int from_i = L.NILP(from) ? 0 : Number.ToInt(from);
  202. int to_i = L.NILP(to) ? str_s.Length : Number.ToInt(to);
  203. if (from_i < 0)
  204. from_i += str_s.Length;
  205. if (to_i < 0)
  206. to_i += str_s.Length;
  207. return (Shelisp.String)str_s.Substring (from_i, to_i - from_i);
  208. }
  209. [LispBuiltin]
  210. public static Shelisp.Object Fstring_to_number(L l, Shelisp.Object str)
  211. {
  212. if (!(str is Shelisp.String))
  213. throw new WrongTypeArgumentException ("stringp", str);
  214. string s = (string)(String)str;
  215. int i;
  216. if (Int32.TryParse (s, out i))
  217. return new Shelisp.Number(i);
  218. float f;
  219. if (Single.TryParse (s, out f))
  220. return new Shelisp.Number(f);
  221. throw new Exception (string.Format ("failed to parse string '{0}'", s));
  222. }
  223. [LispBuiltin]
  224. public static Shelisp.Object Fstring_to_multibyte (L l, Shelisp.Object str)
  225. {
  226. Console.WriteLine ("string-to-multibyte not implemented");
  227. return str;
  228. }
  229. [LispBuiltin]
  230. public static Shelisp.Object Fupcase(L l, Shelisp.Object str)
  231. {
  232. if (!(str is Shelisp.String))
  233. throw new WrongTypeArgumentException ("stringp", str);
  234. string s = (string)(String)str;
  235. return (Shelisp.String)s.ToUpper();
  236. }
  237. [LispBuiltin]
  238. public static Shelisp.Object Fstring_equal(L l, Shelisp.Object str1, Shelisp.Object str2)
  239. {
  240. if (!(str1 is Shelisp.String && str2 is Shelisp.String))
  241. return L.Qnil;
  242. return (string)(Shelisp.String)str1 == (string)(Shelisp.String)str2 ? L.Qt : L.Qnil;
  243. }
  244. [LispBuiltin]
  245. public static Shelisp.Object Fconcat(L l, params Shelisp.Object[] seqs)
  246. {
  247. StringBuilder sb = new StringBuilder ();
  248. foreach (var s in seqs) {
  249. if (L.NILP (s))
  250. continue;
  251. if (!(s is Sequence))
  252. throw new WrongTypeArgumentException ("sequencep", s);
  253. Sequence seq = (Sequence)s;
  254. if (seq is String)
  255. sb.Append ((string)(Shelisp.String)seq);
  256. else {
  257. foreach (var el in seq) {
  258. if (Number.IsInt (el))
  259. sb.Append ((char)Number.ToInt(el));
  260. else
  261. sb.Append (el.ToString("prin1"));
  262. }
  263. }
  264. }
  265. return (Shelisp.String)sb.ToString();
  266. }
  267. [LispBuiltin]
  268. public static Shelisp.Object Fstringp(L l, Shelisp.Object str)
  269. {
  270. return str is Shelisp.String ? L.Qt : L.Qnil;
  271. }
  272. [LispBuiltin]
  273. public static Shelisp.Object Fmultibyte_string_p(L l, Shelisp.Object str)
  274. {
  275. // XXX
  276. return str is Shelisp.String ? L.Qt : L.Qnil;
  277. }
  278. [LispBuiltin]
  279. public static Shelisp.Object Fpropertize(L l, Shelisp.Object str, params Shelisp.Object[] propvals)
  280. {
  281. Shelisp.Object rv = L.Qnil, properties = L.Qnil;
  282. /* Number of prop/vals must be even */
  283. if ((propvals.Length & 1) == 1) {
  284. throw new Exception("Wrong number of arguments"); // XXX a better exception type here?
  285. }
  286. /* First argument must be a string. */
  287. //CHECK_STRING (args[0]);
  288. rv = Shelisp.String.Fcopy_sequence(l, str);
  289. for (int i = 0; i < propvals.Length; i += 2) {
  290. properties = Shelisp.List.Fcons(l, propvals[i], Shelisp.List.Fcons(l, propvals[i+1], properties));
  291. }
  292. #if notyet
  293. Shelisp.String.Fadd_text_properties(new Shelisp.Number(0),
  294. new Shelisp.Number(rv.Length),
  295. properties, rv);
  296. #endif
  297. return rv;
  298. }
  299. [LispBuiltin (DocString = "Return a regexp string which matches exactly STRING and nothing else.")]
  300. public static Shelisp.Object Fregexp_quote (L l, Shelisp.Object str)
  301. {
  302. if (!(str is Shelisp.String))
  303. throw new WrongTypeArgumentException ("stringp", str);
  304. StringBuilder sb = new StringBuilder ();
  305. string str_s = (string)(Shelisp.String)str;
  306. for (int i = 0; i < str_s.Length; i ++) {
  307. char str_i = str_s[i];
  308. if (str_i == '['
  309. || str_i == '*' || str_i == '.' || str_i == '\\'
  310. || str_i == '?' || str_i == '+'
  311. || str_i == '^' || str_i == '$')
  312. sb.Append ('\\');
  313. sb.Append (str_i);
  314. }
  315. return (Shelisp.String)sb.ToString();
  316. }
  317. [LispBuiltin]
  318. public static Shelisp.Object Fre_search_forward (L l, Shelisp.Object regexp, [LispOptional] Shelisp.Object bound, Shelisp.Object noerror, Shelisp.Object count)
  319. {
  320. Console.WriteLine ("re-search-forward not implemented");
  321. return L.Qnil;
  322. }
  323. [LispBuiltin]
  324. public static Shelisp.Object Fre_search_backward (L l, Shelisp.Object regexp, [LispOptional] Shelisp.Object bound, Shelisp.Object noerror, Shelisp.Object count)
  325. {
  326. Console.WriteLine ("re-search-backward not implemented");
  327. return L.Qnil;
  328. }
  329. [LispBuiltin]
  330. public static Shelisp.Object Fchar_to_string (L l, Shelisp.Object ch)
  331. {
  332. string str = new string ((char)(int)(Shelisp.Number)ch, 1);
  333. return (Shelisp.String)str;
  334. }
  335. [LispBuiltin (DocString = @"Convert argument to capitalized form and return that.
  336. This means that each word's first character is upper case
  337. and the rest is lower case.
  338. The argument may be a character or string. The result has the same type.
  339. The argument object is not altered--the value is a copy.")]
  340. public static Shelisp.Object Fcapitalize (L l, Shelisp.Object obj)
  341. {
  342. if (obj is Shelisp.String) {
  343. StringBuilder sb = new StringBuilder ((string)(Shelisp.String)obj);
  344. bool need_capitalization = true;
  345. for (int i = 0; i < sb.Length; i ++) {
  346. if (Char.IsLetter (sb[i])) {
  347. if (need_capitalization) {
  348. sb[i] = Char.ToUpper(sb[i]);
  349. need_capitalization = false;
  350. }
  351. }
  352. else {
  353. need_capitalization = true;
  354. }
  355. }
  356. return (Shelisp.String)sb.ToString();
  357. }
  358. else {
  359. char c = (char)Shelisp.Number.ToInt(obj);
  360. return new Shelisp.Number((int)Char.ToUpper(c));
  361. }
  362. }
  363. [LispBuiltin (DocString = @"Return t if first arg string is less than second in lexicographic order.
  364. Case is significant.
  365. Symbols are also allowed; their print names are used instead.")]
  366. public static Shelisp.Object Fstring_lessp (L l, Shelisp.Object s1, Shelisp.Object s2)
  367. {
  368. string s1_s, s2_s;
  369. if (s1 is Symbol)
  370. s1_s = ((Symbol)s1).name;
  371. else {
  372. if (!(s1 is String))
  373. throw new WrongTypeArgumentException ("stringp", s1);
  374. s1_s = (string)(Shelisp.String)s1;
  375. }
  376. if (s2 is Symbol)
  377. s2_s = ((Symbol)s2).name;
  378. else {
  379. if (!(s2 is String))
  380. throw new WrongTypeArgumentException ("stringp", s2);
  381. s2_s = (string)(Shelisp.String)s2;
  382. }
  383. return s1_s.CompareTo(s2_s) < 0 ? L.Qt : L.Qnil;
  384. }
  385. }
  386. }