PageRenderTime 48ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/ScriptEngine/Parser.cs

https://bitbucket.org/masakimuto/masalibs
C# | 311 lines | 253 code | 19 blank | 39 comment | 40 complexity | 68cd377b4d502544f97a4ac17110a64a MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Diagnostics;
  6. using DefinitionDictionary = System.Collections.Generic.Dictionary<string, object[]>;
  7. namespace Masa.ScriptEngine
  8. {
  9. /// <summary>
  10. /// ()の中身を表す構造体。最初と最後に( ) はつかない
  11. /// </summary>
  12. struct PareBlock
  13. {
  14. public object[] tokens;
  15. public PareBlock(object[] t)
  16. {
  17. tokens = Parser.ParseStatement(t);//再帰
  18. }
  19. }
  20. /// <summary>
  21. /// オプション項を表す構造体。最初に : はつかない
  22. /// </summary>
  23. struct OptionBlock
  24. {
  25. public object[] Tokens;
  26. public string Name;
  27. public OptionBlock(object[] t)
  28. {
  29. if (t.Length == 0)
  30. {
  31. throw new ParseException("オプションの記法が不正\n : の後にトークンがない");
  32. }
  33. if (!(t[0] is string))
  34. {
  35. throw new ParseException("オプションの記法が不正\n : 後のトークンが文字列でない");
  36. }
  37. //tokens = t;
  38. Name = (string)t[0];
  39. Tokens = Parser.ParseStatement(t.Skip(1).ToArray());//再帰
  40. }
  41. }
  42. /// <summary>
  43. /// Scannerが作ったトークン列をLine, PareBlock, OptionBlockなどにまとめる
  44. /// </summary>
  45. static class Parser
  46. {
  47. public static Line[] Parse(object[] tokens)
  48. {
  49. var lines = SplitTokensToLine(tokens).Where(l=>!l.IsEmpty);
  50. foreach (Line item in lines)
  51. {
  52. if (!(item.Tokens[0] is string))
  53. {
  54. throw new ParseException("行頭が無効な字です。", item);
  55. }
  56. item.Tokens = ParseStatement(item.Tokens);
  57. }
  58. tokens = null;//不要
  59. return lines.ToArray();
  60. }
  61. static Line[] SplitTokensToLine(object[] tokens)
  62. {
  63. var tokenLines = DivideLine(tokens);
  64. var lines = new List<Line>();
  65. var definition = new DefinitionDictionary();
  66. for (int i = 0; i < tokenLines.Count; i++)
  67. {
  68. var l = new Line(i + 1, CheckBlockLevel(tokenLines[i]), ProcessDefinition( CleanLine(tokenLines[i]), definition));
  69. if (!l.IsEmpty) lines.Add(l);
  70. l.Index = lines.Count - 1;
  71. }
  72. return lines.ToArray();
  73. }
  74. static object[] ProcessDefinition(object[] line, DefinitionDictionary dict)
  75. {
  76. if (line.Length > 0 && line[0].Equals(Marks.Define))
  77. {
  78. dict[line[1] as string] = line.Skip(2).ToArray();
  79. return null;
  80. }
  81. else
  82. {
  83. List<object> tokens = new List<object>();
  84. object[] def = null;
  85. foreach (var item in line)
  86. {
  87. if (item is string && dict.TryGetValue(item as string, out def))
  88. {
  89. tokens.AddRange(def);
  90. }
  91. else
  92. {
  93. tokens.Add(item);
  94. }
  95. }
  96. return tokens.ToArray();
  97. //var n = new Line(line.Number, line.Level, new obj
  98. }
  99. }
  100. /// <summary>
  101. /// 1文を生トークン、PareBlock, OptionBlockにばらす
  102. /// </summary>
  103. /// <param name="line"></param>
  104. /// <returns></returns>
  105. internal static object[] ParseStatement(object[] line)
  106. {
  107. object[] tokens = GroupToPareBlock(line);
  108. tokens = GroupToOptionBlock(tokens);
  109. return tokens;
  110. }
  111. /// <summary>
  112. /// 5 20 ( 3 + 4 * (2 + 1) ) : from x y : way 360 (5 + 4)
  113. /// 5 20 PareBlock{ 3 + 4 * PareBlock{2 + 1} } : from x y : way 360 PareBlock{5 + 4}
  114. /// </summary>
  115. /// <param name="line"></param>
  116. /// <returns></returns>
  117. static object[] GroupToPareBlock(object[] line)
  118. {
  119. var ret = new List<object>();
  120. var inner = new List<object>();
  121. int pareLevel = 0;
  122. bool addFlag;
  123. for (int i = 0; i < line.Length; i++)
  124. {
  125. object token = line[i];
  126. addFlag = true;
  127. if (Marks.PareOp.Equals(token))
  128. {
  129. if (pareLevel == 0)
  130. {
  131. addFlag = false;
  132. }
  133. pareLevel++;
  134. }
  135. if (Marks.PareCl.Equals(token))
  136. {
  137. pareLevel--;
  138. if (pareLevel == 0)
  139. {
  140. addFlag = false;
  141. var p = new PareBlock(inner.ToArray());
  142. ret.Add(p);
  143. inner.Clear();
  144. }
  145. }
  146. if (addFlag)
  147. {
  148. if (pareLevel == 0)
  149. {
  150. ret.Add(token);
  151. }
  152. else
  153. {
  154. inner.Add(token);
  155. }
  156. }
  157. Debug.Assert(pareLevel >= 0, "カッコの対応が不正");
  158. }
  159. Debug.Assert(pareLevel == 0, "カッコの対応が不正");
  160. return ret.ToArray();
  161. }
  162. /// <summary>
  163. /// 5 20 Pareblock : from x y : way 360 Pareblock
  164. /// 5 20 Pareblock OptionBlock{from, x, y}, OptionBlock{way 360, Pareblock}
  165. /// </summary>
  166. /// <param name="line"></param>
  167. /// <returns></returns>
  168. static object[] GroupToOptionBlock(object[] line)
  169. {
  170. var ret = new List<object>();
  171. var inner = new List<object>();
  172. bool inOption = false;
  173. Action generate = () =>
  174. {
  175. ret.Add(new OptionBlock(inner.ToArray()));
  176. inner.Clear();
  177. };
  178. for (int i = 0; i < line.Length; i++)
  179. {
  180. object token = line[i];
  181. if (Marks.Colon.Equals(token))
  182. {
  183. if (inOption)
  184. {
  185. generate();
  186. }
  187. else
  188. {
  189. inOption = true;
  190. }
  191. }
  192. else
  193. {
  194. if (inOption)
  195. {
  196. inner.Add(token);
  197. }
  198. else
  199. {
  200. ret.Add(token);
  201. }
  202. }
  203. }
  204. if (inOption)
  205. {
  206. generate();
  207. }
  208. return ret.ToArray();
  209. }
  210. /// <summary>
  211. /// 行がどれだけインデントされているか取得する
  212. /// </summary>
  213. /// <param name="line">調べる行</param>
  214. /// <returns>インデント数</returns>
  215. static int CheckBlockLevel(object[] line)
  216. {
  217. for (int i = 0; i < line.Length; i++)
  218. {
  219. if (!Marks.Tab.Equals(line[i]))
  220. {
  221. return i;
  222. }
  223. }
  224. return 0;
  225. }
  226. /// <summary>
  227. /// タブ、改行、コメントなどを取り除く
  228. /// </summary>
  229. /// <param name="line"></param>
  230. /// <returns></returns>
  231. static object[] CleanLine(object[] line)
  232. {
  233. var ret = new List<object>();
  234. foreach (var item in line)
  235. {
  236. if (!(item is Marks))
  237. {
  238. ret.Add(item);
  239. }
  240. else
  241. {
  242. var i = (Marks)item;
  243. if (i == Marks.Tab || i == Marks.Return)
  244. {
  245. }
  246. else if (i == Marks.Semicolon)
  247. {
  248. return ret.ToArray();
  249. }
  250. else
  251. {
  252. ret.Add(item);
  253. }
  254. }
  255. }
  256. return ret.ToArray();
  257. }
  258. static List<object[]> DivideLine(object[] tokens)
  259. {
  260. var ret = new List<object[]>();
  261. var line = new List<object>();
  262. bool comment = false;
  263. int index = 0;
  264. while (index < tokens.Length)
  265. {
  266. object token = tokens[index];
  267. if (Marks.Return.Equals(token))
  268. {
  269. ret.Add(line.ToArray());
  270. line.Clear();
  271. comment = false;
  272. }
  273. else
  274. {
  275. if (Marks.Semicolon.Equals(token))
  276. {
  277. comment = true;
  278. }
  279. if (!comment)
  280. {
  281. line.Add(token);
  282. }
  283. }
  284. index++;
  285. }
  286. //最終行が改行で終わっていなかった場合用
  287. ret.Add(line.ToArray());
  288. line.Clear();
  289. return ret;
  290. }
  291. }
  292. }