PageRenderTime 57ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/Otp/Erlang/Format.cs

https://github.com/saleyn/otp.net
C# | 405 lines | 365 code | 20 blank | 20 comment | 50 complexity | fa0adcfca9b91e661d944717520de6b2 MD5 | raw file
  1. /*``The contents of this file are subject to the Erlang Public License,
  2. * Version 1.1, (the "License"); you may not use this file except in
  3. * compliance with the License. You should have received a copy of the
  4. * Erlang Public License along with this software. If not, it can be
  5. * retrieved via the world wide web at http://www.erlang.org/.
  6. *
  7. * Software distributed under the License is distributed on an "AS IS"
  8. * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  9. * the License for the specific language governing rights and limitations
  10. * under the License.
  11. *
  12. * The Initial Developer of the Original Code is Ericsson Utvecklings AB.
  13. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
  14. * AB. All Rights Reserved.''
  15. *
  16. * Copyright (c) 2011 Serge Aleynikov <saleyn@gmail.com>
  17. */
  18. using System.Globalization;
  19. namespace Otp.Erlang
  20. {
  21. using System;
  22. /*
  23. * Provides a C# representation of Erlang strings.
  24. **/
  25. internal class Formatter
  26. {
  27. private enum State {
  28. ERL_OK = 0
  29. , ERL_FMT_ERR = -1
  30. , ERL_MAX_ENTRIES = 255 /* Max entries in a tuple/list term */
  31. , ERL_MAX_NAME_LENGTH = 255 /* Max length of variable names */
  32. };
  33. private static int skip_null_chars(char[] fmt, int pos) {
  34. for (char c = fmt[pos]; pos < fmt.Length && (c == ' ' || c == '\t' || c == '\n'); c = fmt[++pos]);
  35. return pos;
  36. }
  37. private static string pvariable(char[] fmt, ref int pos, out TermType type)
  38. {
  39. int start = pos;
  40. for (pos = skip_null_chars(fmt, pos); pos < fmt.Length; pos++)
  41. {
  42. char c = fmt[pos];
  43. if (char.IsLetterOrDigit(c) || (c == '_'))
  44. continue;
  45. else
  46. break;
  47. }
  48. int i = pos;
  49. int end = pos;
  50. if (fmt.Length > i + 1 && fmt[i] == ':' && fmt[i + 1] == ':')
  51. {
  52. i = pos + 2;
  53. int tps = i;
  54. for (char c = fmt[i]; char.IsLetter(c) && i < fmt.Length - 1; c = fmt[++i]) ;
  55. if (fmt[i] == '(' && i < fmt.Length - 1 && fmt[i + 1] == ')')
  56. {
  57. pos = i + 2;
  58. string tp = new string(fmt, tps, i - tps);
  59. switch (tp)
  60. {
  61. case "int":
  62. case "integer":
  63. type = TermType.Int;
  64. break;
  65. case "str":
  66. case "string":
  67. type = TermType.String;
  68. break;
  69. case "atom":
  70. type = TermType.Atom;
  71. break;
  72. case "float":
  73. case "double":
  74. type = TermType.Double;
  75. break;
  76. case "binary":
  77. type = TermType.Binary;
  78. break;
  79. case "bool":
  80. case "boolean":
  81. type = TermType.Boolean;
  82. break;
  83. case "byte":
  84. type = TermType.Byte;
  85. break;
  86. case "char":
  87. type = TermType.Char;
  88. break;
  89. case "list":
  90. type = TermType.List;
  91. break;
  92. case "tuple":
  93. type = TermType.Tuple;
  94. break;
  95. case "pid":
  96. type = TermType.Pid;
  97. break;
  98. case "ref":
  99. case "reference":
  100. type = TermType.Ref;
  101. break;
  102. case "port":
  103. type = TermType.Port;
  104. break;
  105. default:
  106. throw new ArgumentException("Type '" + tps + "' is not supported!");
  107. }
  108. }
  109. else
  110. throw new ArgumentException("Invalid variable type specification: " +
  111. new string(fmt, start, pos - start));
  112. }
  113. else
  114. type = TermType.Object;
  115. int len = end - start;
  116. return new string(fmt, start, len);
  117. }
  118. private static Object createAtom(string s)
  119. {
  120. bool is_true = s == "true";
  121. if (is_true || s == "false")
  122. return new Erlang.Boolean(is_true);
  123. return new Erlang.Atom(s);
  124. }
  125. private static string patom(char[] fmt, ref int pos)
  126. {
  127. int start = pos;
  128. for (pos = skip_null_chars(fmt, pos); pos < fmt.Length; pos++)
  129. {
  130. char c = fmt[pos];
  131. if (char.IsLetterOrDigit(c) || (c == '_') || (c == '@'))
  132. continue;
  133. else
  134. break;
  135. }
  136. int len = pos - start;
  137. return new string(fmt, start, len);
  138. }
  139. private static string pdigit(char[] fmt, ref int pos)
  140. {
  141. int start = pos;
  142. bool dotp = false;
  143. for (pos = skip_null_chars(fmt, pos); pos < fmt.Length; pos++)
  144. {
  145. char c = fmt[pos];
  146. if (char.IsDigit(c) || c == '-')
  147. continue;
  148. else if (!dotp && c == '.')
  149. {
  150. dotp = true;
  151. continue;
  152. }
  153. else
  154. break;
  155. }
  156. int len = pos - start;
  157. return new string(fmt, start, len);
  158. }
  159. private static string pstring(char[] fmt, ref int pos)
  160. {
  161. int start = ++pos; // skip the first double quote
  162. for (; pos < fmt.Length; pos++)
  163. {
  164. char c = fmt[pos];
  165. if (c == '"') {
  166. if (fmt[pos - 1] == '\\')
  167. continue;
  168. else
  169. break;
  170. }
  171. }
  172. int len = pos++ - start;
  173. return new string(fmt, start, len);
  174. }
  175. private static string pquotedAtom(char[] fmt, ref int pos)
  176. {
  177. int start = ++pos; // skip first quote
  178. for (pos = skip_null_chars(fmt, pos); pos < fmt.Length; pos++)
  179. {
  180. char c = fmt[pos];
  181. if (c == '\'')
  182. {
  183. if (fmt[pos-1] == '\\')
  184. continue;
  185. else
  186. break;
  187. }
  188. }
  189. int len = pos++ - start;
  190. return new string(fmt, start, len);
  191. }
  192. private static State ptuple(char[] fmt, ref int pos,
  193. ref System.Collections.Generic.List<Erlang.Object> items, ref int argc, params object[] args)
  194. {
  195. int start = pos;
  196. State rc = State.ERL_FMT_ERR;
  197. if (pos < fmt.Length)
  198. {
  199. pos = skip_null_chars(fmt, pos);
  200. switch (fmt[pos++]) {
  201. case '}':
  202. rc = State.ERL_OK;
  203. break;
  204. case ',':
  205. rc = ptuple(fmt, ref pos, ref items, ref argc, args);
  206. break;
  207. default: {
  208. --pos;
  209. Erlang.Object obj = create(fmt, ref pos, ref argc, args);
  210. items.Add(obj);
  211. rc = ptuple(fmt, ref pos, ref items, ref argc, args);
  212. break;
  213. }
  214. }
  215. }
  216. return rc;
  217. }
  218. private static State plist(char[] fmt, ref int pos,
  219. ref System.Collections.Generic.List<Erlang.Object> items, ref int argc, params object[] args)
  220. {
  221. State rc = State.ERL_FMT_ERR;
  222. if (pos < fmt.Length)
  223. {
  224. pos = skip_null_chars(fmt, pos);
  225. switch (fmt[pos++]) {
  226. case ']':
  227. rc = State.ERL_OK;
  228. break;
  229. case ',':
  230. rc = plist(fmt, ref pos, ref items, ref argc, args);
  231. break;
  232. case '|':
  233. pos = skip_null_chars(fmt, pos);
  234. if (char.IsUpper(fmt[pos]) || fmt[pos] == '_') {
  235. TermType t;
  236. string s = pvariable(fmt, ref pos, out t);
  237. items.Add(new Erlang.Var(s, t));
  238. pos = skip_null_chars(fmt, pos);
  239. if (fmt[pos] == ']')
  240. rc = State.ERL_OK;
  241. break;
  242. }
  243. break;
  244. default: {
  245. --pos;
  246. Erlang.Object obj = create(fmt, ref pos, ref argc, args);
  247. items.Add(obj);
  248. rc = plist(fmt, ref pos, ref items, ref argc, args);
  249. break;
  250. }
  251. }
  252. }
  253. return rc;
  254. }
  255. private static State pformat(char[] fmt, ref int pos,
  256. ref System.Collections.Generic.List<Erlang.Object> items, ref int argc, params object[] args)
  257. {
  258. pos = skip_null_chars(fmt, pos);
  259. char c = fmt[pos++];
  260. if (args.Length < argc+1)
  261. throw new FormatException(string.Format("Missing argument for argument #{0}: ~{1}", argc, c));
  262. object o = args[argc++];
  263. switch (c)
  264. {
  265. case 'i':
  266. case 'd': items.Add(new Erlang.Long(o is int ? (int)o : (long)o)); break;
  267. case 'f': items.Add(new Erlang.Double((double)o)); break;
  268. case 's': items.Add(new Erlang.String((string)o)); break;
  269. case 'b': items.Add(new Erlang.Boolean((bool)o)); break;
  270. case 'c': items.Add(new Erlang.Char((char)o)); break;
  271. case 'w':
  272. Type to = o.GetType();
  273. // Native types
  274. if (to == typeof(int))
  275. items.Add(new Erlang.Long((int)o));
  276. else if (to == typeof(long))
  277. items.Add(new Erlang.Long((long)o));
  278. else if (to == typeof(double))
  279. items.Add(new Erlang.Double((double)o));
  280. else if (to == typeof(string))
  281. items.Add(new Erlang.String((string)o));
  282. else if (to == typeof(bool))
  283. items.Add(new Erlang.Boolean((bool)o));
  284. else if (to == typeof(char))
  285. items.Add(new Erlang.Char((char)o));
  286. // Erlang terms
  287. else if (typeof(Erlang.Object).IsAssignableFrom(o.GetType()))
  288. items.Add(o as Erlang.Object);
  289. else
  290. throw new FormatException(
  291. string.Format("Unknown type of argument #{0}: {1}", argc-1, to.ToString()));
  292. break;
  293. default:
  294. throw new FormatException(string.Format("Unsupported type ~{0} for argument #{1}", c, argc-1));
  295. }
  296. return State.ERL_OK;
  297. }
  298. internal static Erlang.Object create(
  299. char[] fmt, ref int pos, ref int argc, params object[] args)
  300. {
  301. var items = new System.Collections.Generic.List<Erlang.Object>();
  302. Erlang.Object result = null;
  303. pos = skip_null_chars(fmt, pos);
  304. switch (fmt[pos++]) {
  305. case '{':
  306. if (State.ERL_OK != ptuple(fmt, ref pos, ref items, ref argc, args))
  307. throw new FormatException(
  308. string.Format("Error parsing tuple at pos {0}", pos));
  309. result = new Erlang.Tuple(items.ToArray());
  310. break;
  311. case '[':
  312. if (fmt[pos] == ']') {
  313. result = new Erlang.List();
  314. pos++;
  315. break;
  316. }
  317. else if (State.ERL_OK == plist(fmt, ref pos, ref items, ref argc, args))
  318. {
  319. result = new Erlang.List(items.ToArray());
  320. break;
  321. }
  322. throw new FormatException(
  323. string.Format("Error parsing list at pos {0}", pos));
  324. case '$': /* char-value? */
  325. result = new Erlang.Char(fmt[pos++]);
  326. break;
  327. case '~':
  328. if (State.ERL_OK != pformat(fmt, ref pos, ref items, ref argc, args))
  329. throw new FormatException(
  330. string.Format("Error parsing term at pos {0}", pos));
  331. result = items[0];
  332. break;
  333. default:
  334. char c = fmt[--pos];
  335. if (char.IsLower(c)) { /* atom ? */
  336. string s = patom(fmt, ref pos);
  337. result = createAtom(s);
  338. } else if (char.IsUpper(c) || c == '_') {
  339. TermType t;
  340. string s = pvariable(fmt, ref pos, out t);
  341. result = new Erlang.Var(s, t);
  342. } else if (char.IsDigit(c) || c == '-') { /* integer/float ? */
  343. string s = pdigit(fmt, ref pos);
  344. if (s.IndexOf('.') < 0)
  345. result = new Erlang.Long(long.Parse(s, CultureInfo.InvariantCulture));
  346. else
  347. result = new Erlang.Double(double.Parse(s, CultureInfo.InvariantCulture));
  348. } else if (c == '"') { /* string ? */
  349. string s = pstring(fmt, ref pos);
  350. result = new Erlang.String(s);
  351. } else if (c == '\'') { /* quoted atom ? */
  352. string s = pquotedAtom(fmt, ref pos);
  353. result = createAtom(s);
  354. }
  355. break;
  356. }
  357. return result;
  358. }
  359. }
  360. }