/xFunc.Maths/MathLexer.cs

http://xfunc.codeplex.com · C# · 556 lines · 473 code · 69 blank · 14 comment · 110 complexity · 94339225a0b46442820711a24e16afd6 MD5 · raw file

  1. // Copyright 2012-2013 Dmitry Kischenko
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
  12. // express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. using System;
  16. using System.Collections.Generic;
  17. using System.Globalization;
  18. using System.Linq;
  19. using xFunc.Maths.Exceptions;
  20. using xFunc.Maths.Resources;
  21. namespace xFunc.Maths
  22. {
  23. public class MathLexer : IMathLexer
  24. {
  25. private readonly HashSet<string> notVar;
  26. public MathLexer()
  27. {
  28. notVar = new HashSet<string> { "and", "or", "xor" };
  29. }
  30. public IEnumerable<MathToken> Tokenize(string function)
  31. {
  32. if (string.IsNullOrWhiteSpace(function))
  33. throw new ArgumentNullException("function", Resource.NotSpecifiedFunction);
  34. function = function.ToLower().Replace(" ", "");
  35. var tokens = new List<MathToken>();
  36. for (int i = 0; i < function.Length; )
  37. {
  38. char letter = function[i];
  39. var token = new MathToken();
  40. if (letter == '(')
  41. {
  42. token.Type = MathTokenType.OpenBracket;
  43. }
  44. else if (letter == ')')
  45. {
  46. token.Type = MathTokenType.CloseBracket;
  47. }
  48. else if (letter == '+')
  49. {
  50. if (i - 1 >= 0)
  51. {
  52. char pre = function[i - 1];
  53. if (pre == '(')
  54. {
  55. i++;
  56. continue;
  57. }
  58. }
  59. else
  60. {
  61. i++;
  62. continue;
  63. }
  64. token.Type = MathTokenType.Addition;
  65. }
  66. else if (letter == '-')
  67. {
  68. if (i - 1 >= 0)
  69. {
  70. char pre = function[i - 1];
  71. if (pre == '(')
  72. {
  73. token.Type = MathTokenType.UnaryMinus;
  74. tokens.Add(token);
  75. i++;
  76. continue;
  77. }
  78. }
  79. else
  80. {
  81. token.Type = MathTokenType.UnaryMinus;
  82. tokens.Add(token);
  83. i++;
  84. continue;
  85. }
  86. token.Type = MathTokenType.Subtraction;
  87. }
  88. else if (letter == '*')
  89. {
  90. token.Type = MathTokenType.Multiplication;
  91. }
  92. else if (letter == '/')
  93. {
  94. token.Type = MathTokenType.Division;
  95. }
  96. else if (letter == '^')
  97. {
  98. token.Type = MathTokenType.Exponentiation;
  99. }
  100. else if (letter == ',')
  101. {
  102. token.Type = MathTokenType.Comma;
  103. }
  104. else if (letter == '~')
  105. {
  106. token.Type = MathTokenType.Not;
  107. }
  108. else if (letter == '&')
  109. {
  110. token.Type = MathTokenType.And;
  111. }
  112. else if (letter == '|')
  113. {
  114. token.Type = MathTokenType.Or;
  115. }
  116. else if (letter == ':' && i + 1 < function.Length && function[i + 1] == '=')
  117. {
  118. token.Type = MathTokenType.Assign;
  119. tokens.Add(token);
  120. i += 2;
  121. continue;
  122. }
  123. else if (char.IsDigit(letter))
  124. {
  125. int length = 1;
  126. int j;
  127. for (j = i + 1; j < function.Length && char.IsDigit(function[j]); j++)
  128. length++;
  129. if (j < function.Length && function[j] == '.')
  130. {
  131. length++;
  132. for (j += 1; j < function.Length && char.IsDigit(function[j]); j++)
  133. length++;
  134. }
  135. var number = function.Substring(i, length);
  136. token.Type = MathTokenType.Number;
  137. token.Number = double.Parse(number, CultureInfo.InvariantCulture);
  138. tokens.Add(token);
  139. if (i + length < function.Length && char.IsLetter(function[i + length]) && !notVar.Any(s => function.Substring(i + length).StartsWith(s)))
  140. {
  141. token = new MathToken(MathTokenType.Multiplication);
  142. tokens.Add(token);
  143. }
  144. i += length;
  145. continue;
  146. }
  147. else if (char.IsLetter(letter))
  148. {
  149. var sub = function.Substring(i);
  150. if (sub.StartsWith("pi"))
  151. {
  152. token.Type = MathTokenType.Variable;
  153. token.Variable = '?';
  154. tokens.Add(token);
  155. i += 2;
  156. continue;
  157. }
  158. if (sub.StartsWith("exp"))
  159. {
  160. token.Type = MathTokenType.E;
  161. tokens.Add(token);
  162. i += 3;
  163. continue;
  164. }
  165. if (sub.StartsWith("abs"))
  166. {
  167. token.Type = MathTokenType.Absolute;
  168. tokens.Add(token);
  169. i += 3;
  170. continue;
  171. }
  172. if (sub.StartsWith("sh"))
  173. {
  174. token.Type = MathTokenType.Sineh;
  175. tokens.Add(token);
  176. i += 2;
  177. continue;
  178. }
  179. if (sub.StartsWith("sinh"))
  180. {
  181. token.Type = MathTokenType.Sineh;
  182. tokens.Add(token);
  183. i += 4;
  184. continue;
  185. }
  186. if (sub.StartsWith("ch"))
  187. {
  188. token.Type = MathTokenType.Cosineh;
  189. tokens.Add(token);
  190. i += 2;
  191. continue;
  192. }
  193. if (sub.StartsWith("cosh"))
  194. {
  195. token.Type = MathTokenType.Cosineh;
  196. tokens.Add(token);
  197. i += 4;
  198. continue;
  199. }
  200. if (sub.StartsWith("th"))
  201. {
  202. token.Type = MathTokenType.Tangenth;
  203. tokens.Add(token);
  204. i += 2;
  205. continue;
  206. }
  207. if (sub.StartsWith("tanh"))
  208. {
  209. token.Type = MathTokenType.Tangenth;
  210. tokens.Add(token);
  211. i += 4;
  212. continue;
  213. }
  214. if (sub.StartsWith("cth"))
  215. {
  216. token.Type = MathTokenType.Cotangenth;
  217. tokens.Add(token);
  218. i += 3;
  219. continue;
  220. }
  221. if (sub.StartsWith("coth"))
  222. {
  223. token.Type = MathTokenType.Cotangenth;
  224. tokens.Add(token);
  225. i += 4;
  226. continue;
  227. }
  228. if (sub.StartsWith("sech"))
  229. {
  230. token.Type = MathTokenType.Secanth;
  231. tokens.Add(token);
  232. i += 4;
  233. continue;
  234. }
  235. if (sub.StartsWith("csch"))
  236. {
  237. token.Type = MathTokenType.Cosecanth;
  238. tokens.Add(token);
  239. i += 4;
  240. continue;
  241. }
  242. if (sub.StartsWith("arsinh"))
  243. {
  244. token.Type = MathTokenType.Arsineh;
  245. tokens.Add(token);
  246. i += 6;
  247. continue;
  248. }
  249. if (sub.StartsWith("arcosh"))
  250. {
  251. token.Type = MathTokenType.Arcosineh;
  252. tokens.Add(token);
  253. i += 6;
  254. continue;
  255. }
  256. if (sub.StartsWith("artanh"))
  257. {
  258. token.Type = MathTokenType.Artangenth;
  259. tokens.Add(token);
  260. i += 6;
  261. continue;
  262. }
  263. if (sub.StartsWith("arcoth"))
  264. {
  265. token.Type = MathTokenType.Arcotangenth;
  266. tokens.Add(token);
  267. i += 6;
  268. continue;
  269. }
  270. if (sub.StartsWith("arsech"))
  271. {
  272. token.Type = MathTokenType.Arsecanth;
  273. tokens.Add(token);
  274. i += 6;
  275. continue;
  276. }
  277. if (sub.StartsWith("arcsch"))
  278. {
  279. token.Type = MathTokenType.Arcosecanth;
  280. tokens.Add(token);
  281. i += 6;
  282. continue;
  283. }
  284. if (sub.StartsWith("sin"))
  285. {
  286. token.Type = MathTokenType.Sine;
  287. tokens.Add(token);
  288. i += 3;
  289. continue;
  290. }
  291. if (sub.StartsWith("cosec"))
  292. {
  293. token.Type = MathTokenType.Cosecant;
  294. tokens.Add(token);
  295. i += 5;
  296. continue;
  297. }
  298. if (sub.StartsWith("csc"))
  299. {
  300. token.Type = MathTokenType.Cosecant;
  301. tokens.Add(token);
  302. i += 3;
  303. continue;
  304. }
  305. if (sub.StartsWith("cos"))
  306. {
  307. token.Type = MathTokenType.Cosine;
  308. tokens.Add(token);
  309. i += 3;
  310. continue;
  311. }
  312. if (sub.StartsWith("tg"))
  313. {
  314. token.Type = MathTokenType.Tangent;
  315. tokens.Add(token);
  316. i += 2;
  317. continue;
  318. }
  319. if (sub.StartsWith("tan"))
  320. {
  321. token.Type = MathTokenType.Tangent;
  322. tokens.Add(token);
  323. i += 3;
  324. continue;
  325. }
  326. if (sub.StartsWith("cot") || sub.StartsWith("ctg"))
  327. {
  328. token.Type = MathTokenType.Cotangent;
  329. tokens.Add(token);
  330. i += 3;
  331. continue;
  332. }
  333. if (sub.StartsWith("sec"))
  334. {
  335. token.Type = MathTokenType.Secant;
  336. tokens.Add(token);
  337. i += 3;
  338. continue;
  339. }
  340. if (sub.StartsWith("arcsin"))
  341. {
  342. token.Type = MathTokenType.Arcsine;
  343. tokens.Add(token);
  344. i += 6;
  345. continue;
  346. }
  347. if (sub.StartsWith("arccosec"))
  348. {
  349. token.Type = MathTokenType.Arccosecant;
  350. tokens.Add(token);
  351. i += 8;
  352. continue;
  353. }
  354. if (sub.StartsWith("arccsc"))
  355. {
  356. token.Type = MathTokenType.Arccosecant;
  357. tokens.Add(token);
  358. i += 6;
  359. continue;
  360. }
  361. if (sub.StartsWith("arccos"))
  362. {
  363. token.Type = MathTokenType.Arccosine;
  364. tokens.Add(token);
  365. i += 6;
  366. continue;
  367. }
  368. if (sub.StartsWith("arctg"))
  369. {
  370. token.Type = MathTokenType.Arctangent;
  371. tokens.Add(token);
  372. i += 5;
  373. continue;
  374. }
  375. if (sub.StartsWith("arctan"))
  376. {
  377. token.Type = MathTokenType.Arctangent;
  378. tokens.Add(token);
  379. i += 6;
  380. continue;
  381. }
  382. if (sub.StartsWith("arccot") || sub.StartsWith("arcctg"))
  383. {
  384. token.Type = MathTokenType.Arccotangent;
  385. tokens.Add(token);
  386. i += 6;
  387. continue;
  388. }
  389. if (sub.StartsWith("arcsec"))
  390. {
  391. token.Type = MathTokenType.Arcsecant;
  392. tokens.Add(token);
  393. i += 6;
  394. continue;
  395. }
  396. if (sub.StartsWith("sqrt"))
  397. {
  398. token.Type = MathTokenType.Sqrt;
  399. tokens.Add(token);
  400. i += 4;
  401. continue;
  402. }
  403. if (sub.StartsWith("root"))
  404. {
  405. token.Type = MathTokenType.Root;
  406. tokens.Add(token);
  407. i += 4;
  408. continue;
  409. }
  410. if (sub.StartsWith("lg"))
  411. {
  412. token.Type = MathTokenType.Lg;
  413. tokens.Add(token);
  414. i += 2;
  415. continue;
  416. }
  417. if (sub.StartsWith("ln"))
  418. {
  419. token.Type = MathTokenType.Ln;
  420. tokens.Add(token);
  421. i += 2;
  422. continue;
  423. }
  424. if (sub.StartsWith("log"))
  425. {
  426. token.Type = MathTokenType.Log;
  427. tokens.Add(token);
  428. i += 3;
  429. continue;
  430. }
  431. if (sub.StartsWith("deriv"))
  432. {
  433. token.Type = MathTokenType.Derivative;
  434. tokens.Add(token);
  435. i += 5;
  436. continue;
  437. }
  438. if (sub.StartsWith("not"))
  439. {
  440. token.Type = MathTokenType.Not;
  441. tokens.Add(token);
  442. i += 3;
  443. continue;
  444. }
  445. if (sub.StartsWith("and"))
  446. {
  447. token.Type = MathTokenType.And;
  448. tokens.Add(token);
  449. i += 3;
  450. continue;
  451. }
  452. if (sub.StartsWith("or"))
  453. {
  454. token.Type = MathTokenType.Or;
  455. tokens.Add(token);
  456. i += 2;
  457. continue;
  458. }
  459. if (sub.StartsWith("xor"))
  460. {
  461. token.Type = MathTokenType.XOr;
  462. tokens.Add(token);
  463. i += 3;
  464. continue;
  465. }
  466. token.Type = MathTokenType.Variable;
  467. token.Variable = letter;
  468. tokens.Add(token);
  469. if (i + 1 < function.Length && char.IsLetter(function[i + 1]) && !notVar.Any(s => function.Substring(i + 1).StartsWith(s)))
  470. {
  471. token = new MathToken(MathTokenType.Multiplication);
  472. tokens.Add(token);
  473. }
  474. i++;
  475. continue;
  476. }
  477. else
  478. {
  479. throw new MathLexerException(string.Format(Resource.NotSupportedSymbol, letter));
  480. }
  481. tokens.Add(token);
  482. i++;
  483. }
  484. return tokens;
  485. }
  486. }
  487. }