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

/ScriptEngine/Scanner.cs

https://bitbucket.org/masakimuto/masalibs
C# | 366 lines | 290 code | 37 blank | 39 comment | 45 complexity | e758aa4b7e16b763f84d67be1db85fa2 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. namespace Masa.ScriptEngine
  6. {
  7. using Value = System.Single;
  8. enum Marks
  9. {
  10. No,///なし
  11. Pos,/// + 演算子にも符号にも
  12. Neg,/// -
  13. Mul,/// *
  14. Div,/// /
  15. Mod,/// %
  16. Not,/// !
  17. And,/// &
  18. Or,/// |
  19. Equal,///比較等号 ==
  20. NotEqual,/// !=
  21. Big, /// >
  22. BigEqual,/// >=
  23. Small, /// <
  24. SmallEqual, /// <=
  25. Sub,///代入 =
  26. Inc, /// ++
  27. Dec, /// --
  28. SubPos, /// +=
  29. SubNeg, /// -=
  30. SubMul, /// *=
  31. SubDiv, /// /=
  32. PareOp,/// (
  33. PareCl,/// )
  34. Colon, /// : オプション文マーカー
  35. Dollar, /// $ varでの型指定
  36. Tab,
  37. Return,
  38. Semicolon,///コメント
  39. Define,//#define
  40. Include,//#include
  41. }
  42. /// <summary>
  43. /// スクリプトコードを読み込んで単語単位にばらしてデータ化するひと。
  44. /// </summary>
  45. public sealed class Scanner
  46. {
  47. /// <summary>
  48. /// string,Mark,floatのどれか
  49. /// </summary>
  50. public List<object> Tokens
  51. {
  52. get;
  53. private set;
  54. }
  55. readonly Dictionary<string, string> headerDictionary;
  56. static readonly Dictionary<string, Marks> MarkNameDict;
  57. public const char StringLiteralMark = '@';
  58. static Scanner()
  59. {
  60. MarkNameDict = new Dictionary<string, Marks>();
  61. MarkNameDict.Add("\t", Marks.Tab);
  62. MarkNameDict.Add("\n", Marks.Return);
  63. MarkNameDict.Add(":", Marks.Colon);
  64. MarkNameDict.Add("+", Marks.Pos);
  65. MarkNameDict.Add("-", Marks.Neg);
  66. MarkNameDict.Add("*", Marks.Mul);
  67. MarkNameDict.Add("/", Marks.Div);
  68. MarkNameDict.Add("%", Marks.Mod);
  69. MarkNameDict.Add("!", Marks.Not);
  70. MarkNameDict.Add("&", Marks.And);
  71. MarkNameDict.Add("|", Marks.Or);
  72. MarkNameDict.Add("=", Marks.Sub);
  73. MarkNameDict.Add("!=", Marks.NotEqual);
  74. MarkNameDict.Add("<", Marks.Small);
  75. MarkNameDict.Add("<=", Marks.SmallEqual);
  76. MarkNameDict.Add(">", Marks.Big);
  77. MarkNameDict.Add(">=", Marks.BigEqual);
  78. MarkNameDict.Add("++", Marks.Inc);
  79. MarkNameDict.Add("--", Marks.Dec);
  80. MarkNameDict.Add("==", Marks.Equal);
  81. MarkNameDict.Add("+=", Marks.SubPos);
  82. MarkNameDict.Add("-=", Marks.SubNeg);
  83. MarkNameDict.Add("*=", Marks.SubMul);
  84. MarkNameDict.Add("/=", Marks.SubDiv);
  85. MarkNameDict.Add("(", Marks.PareOp);
  86. MarkNameDict.Add(")", Marks.PareCl);
  87. MarkNameDict["#define"] = Marks.Define;
  88. MarkNameDict["#include"] = Marks.Include;
  89. MarkNameDict["$"] = Marks.Dollar;
  90. }
  91. //TODO 例外が出たところのファイル名や行を表示できるようにする
  92. public Scanner(string code)
  93. : this(code, null)
  94. {
  95. }
  96. public Scanner(string code, Dictionary<string, string> headerDict)
  97. {
  98. this.headerDictionary = headerDict;
  99. Tokens = new List<object>();
  100. Scan(code);
  101. }
  102. /// <summary>
  103. /// 単語に分割する(;含めてコメント除去、@@リテラルを@始まりの1単語に)
  104. /// </summary>
  105. /// <param name="code"></param>
  106. /// <returns></returns>
  107. string[] Split(string code)
  108. {
  109. var ret = new List<string>();
  110. bool isLiteral = false;
  111. bool isComment = false;
  112. StringBuilder builder = new StringBuilder(256);
  113. Action enter = () =>
  114. {
  115. if (builder.Length != 0)
  116. {
  117. ret.Add(builder.ToString());
  118. builder.Clear();
  119. }
  120. };
  121. code += "\n";//末行処理
  122. code = code.Replace(System.Environment.NewLine, "\n");
  123. for (int i = 0; i < code.Length; i++)
  124. {
  125. if (isComment)
  126. {
  127. if (code[i] == '\n')
  128. {
  129. isComment = false;
  130. ret.Add("\n");
  131. }
  132. continue;
  133. }
  134. if (isLiteral)
  135. {
  136. if (code[i] == StringLiteralMark)
  137. {
  138. isLiteral = false;
  139. enter();
  140. }
  141. else
  142. {
  143. builder.Append(code[i]);
  144. }
  145. continue;
  146. }
  147. switch (code[i])
  148. {
  149. case ' ':
  150. enter();
  151. break;
  152. case ';':
  153. enter();
  154. isComment = true;
  155. break;
  156. case '\n':
  157. goto case ')';
  158. case '\t':
  159. goto case ')';
  160. case '(':
  161. goto case ')';
  162. case ')':
  163. enter();
  164. ret.Add(code[i].ToString());
  165. break;
  166. case StringLiteralMark:
  167. enter();
  168. isLiteral = true;
  169. builder.Append(StringLiteralMark);
  170. break;
  171. default:
  172. builder.Append(code[i].ToString());
  173. break;
  174. }
  175. }
  176. return ret.ToArray();
  177. }
  178. //string Include(string code)
  179. //{
  180. // var lines = code.Split('\n');
  181. // string[] tokens;
  182. // foreach (var item in lines)
  183. // {
  184. // tokens = item.Split(null);
  185. // if (tokens.Length == 2 && tokens[0] == "#define")
  186. // {
  187. // }
  188. // }
  189. //}
  190. string[] Include(string[] tokens)
  191. {
  192. List<string> result = null;
  193. int head = 0;
  194. for (int i = 0; i < tokens.Length; i++)
  195. {
  196. if (tokens[i] == "#include")
  197. {
  198. if (result == null)
  199. {
  200. result = new List<string>(tokens.Length);
  201. }
  202. result.AddRange(tokens.Skip(head).Take(i - head));
  203. string value;
  204. if (headerDictionary.TryGetValue(tokens[i + 1], out value))
  205. {
  206. result.AddRange(Split(value));
  207. }
  208. else
  209. {
  210. throw new ParseException("インクルードに失敗:" + tokens[i + 1]);
  211. }
  212. i += 2;
  213. head = i;
  214. }
  215. }
  216. if (result != null)
  217. {
  218. result.AddRange(tokens.Skip(head).Take(tokens.Length - head));
  219. return result.ToArray();
  220. }
  221. else
  222. {
  223. return tokens;
  224. }
  225. }
  226. void Scan(string code)
  227. {
  228. //if (headerDictionary != null)
  229. //{
  230. // code = Include(code);
  231. //}
  232. var t = Split(code);
  233. if (headerDictionary != null)
  234. {
  235. t = Include(t);
  236. }
  237. foreach (var item in t)
  238. {
  239. if (item.Length == 0)
  240. {
  241. }
  242. else if (MarkNameDict.ContainsKey(item))
  243. {
  244. Tokens.Add(MarkNameDict[item]);
  245. }
  246. else if (IsIdLetter(item[0]) || item[0] == StringLiteralMark)//general string
  247. {
  248. Tokens.Add(item);
  249. }
  250. else if (Char.IsDigit(item[0]))
  251. {
  252. try
  253. {
  254. Tokens.Add(Value.Parse(item));
  255. }
  256. catch
  257. {
  258. throw new Exception("不正な文字列 " + item);
  259. }
  260. }
  261. else if (IsSingleMark(item[0]))
  262. {
  263. if (item.Length > 1 && IsIdLetter(item[1]))//単項演算子 -hoge
  264. {
  265. Tokens.Add(MarkNameDict[item[0].ToString()]);
  266. Tokens.Add(item.Substring(1));
  267. }
  268. else//-1 etc.
  269. {
  270. try
  271. {
  272. Tokens.Add(Single.Parse(item));
  273. }
  274. catch
  275. {
  276. throw new Exception("不正な文字列 " + item);
  277. }
  278. }
  279. }
  280. else
  281. {
  282. throw new Exception("不正な文字列 " + item);
  283. }
  284. }
  285. }
  286. void ProcessDefine()
  287. {
  288. var definition = new Dictionary<string, List<object>>();
  289. for (int i = 0; i < Tokens.Count; i++)
  290. {
  291. if (Tokens[i].Equals(Marks.Define))
  292. {
  293. var list = new List<object>();
  294. for (int j = i + 2; true; j++)
  295. {
  296. if (Tokens[j].Equals(Marks.Return))
  297. {
  298. break;
  299. }
  300. else
  301. {
  302. list.Add(Tokens[j]);
  303. }
  304. }
  305. definition[Tokens[i + 1] as string] = list;
  306. }
  307. }
  308. }
  309. /// <summary>
  310. /// 単項演算子か
  311. /// </summary>
  312. /// <param name="i"></param>
  313. /// <returns></returns>
  314. bool IsSingleMark(char i)
  315. {
  316. return i == '+' || i == '-' || i == '!';
  317. //return (i == '+' || i == '-' || i == '!');
  318. }
  319. /// <summary>
  320. /// 識別子の1文字目として使える文字か
  321. /// </summary>
  322. /// <param name="i"></param>
  323. /// <returns></returns>
  324. bool IsIdLetter(char i)
  325. {
  326. return (char.IsLetter(i) || i == '_');
  327. }
  328. }
  329. }