PageRenderTime 58ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/ScriptEngine/ExpressionTreeMaker.cs

https://bitbucket.org/masakimuto/masalibs
C# | 1121 lines | 800 code | 85 blank | 236 comment | 139 complexity | cb462c23092c979041f32267b6dc2a6f MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Linq.Expressions;
  5. using System.Reflection;
  6. using System.Reflection.Emit;
  7. using Masa.Lib;
  8. //using ScriptEngineEx.Environment = ScriptEngineEx.Environment;
  9. namespace Masa.ScriptEngine
  10. {
  11. using Value = System.Single;
  12. using Vector2 = Microsoft.Xna.Framework.Vector2;
  13. using Vector3 = Microsoft.Xna.Framework.Vector3;
  14. public class ExpressionTreeMaker : IExpressionTreeMaker
  15. {
  16. public Action<ScriptEngine.Environment> Statement { get; private set; }
  17. public Action<ScriptEngine.Environment> InitStatement { get; private set; }
  18. public int GlobalVarNumber//Enviromentにあげる
  19. {
  20. get { return GlobalVarList.Count; }
  21. }
  22. Dictionary<string, Action<Environment>> LabelStatementCashe;
  23. Expression[] TotalBlock;
  24. List<string> GlobalVarList;
  25. Dictionary<string, ParameterExpression> VarDict;
  26. Dictionary<string, Expression> LabelDict;
  27. Dictionary<string, MethodInfo> MethodDict;
  28. Dictionary<string, PropertyInfo> PropertyDict;
  29. ParameterExpression Environment;
  30. string[] NameValueTable;
  31. Line[] Lines;
  32. Type TargetType;
  33. static readonly LabelTarget ExitLabel = Expression.Label("_ScriptExit");
  34. static readonly Type ValueType = typeof(Value);
  35. static readonly Expression ZeroExpression = Expression.Constant(0f, ValueType);
  36. static readonly Expression OneExpression = Expression.Constant(1f, ValueType);
  37. static readonly Expression NanExpression = Expression.Constant(Value.NaN, ValueType);
  38. //static readonly LabelTarget LOOPEND = Expression.Label("EndLoop");
  39. static readonly Dictionary<string, FieldInfo> EnvironmentField = GetEnvironmentFieldInfo();
  40. static readonly Dictionary<string, PropertyInfo> EnvironmentProperty = GetEnvironmentPropertyInfo();
  41. static readonly Dictionary<string, MethodInfo> StaticMethodDict = GetStaticMethodInfo();
  42. static readonly Dictionary<Type, ClassReflectionInfo> ReflectionCashe = new Dictionary<Type, ClassReflectionInfo>();
  43. static readonly Dictionary<string, Expression> ConstantValueDict = new Dictionary<string, Expression>()
  44. {
  45. {"PI2", Expression.Constant((Value)Math.PI * 2f, ValueType)},
  46. {"PI", Expression.Constant((Value)Math.PI, ValueType)},
  47. };
  48. static readonly Dictionary<string, Type> TypeNameDictionary = new Dictionary<string, Type>()
  49. {
  50. {"float", typeof(float)},
  51. {"float2", typeof(Vector2)},
  52. {"float3", typeof(Vector3)},
  53. {"double", typeof(double)},
  54. {"int", typeof(int)},
  55. };
  56. public ExpressionTreeMaker(object[] token, Type targetType)
  57. : this(token, targetType, null)
  58. {
  59. }
  60. /// <summary>
  61. /// 文字列列挙付き
  62. /// </summary>
  63. /// <param name="token"></param>
  64. /// <param name="targetType"></param>
  65. /// <param name="nameValueTable"></param>
  66. public ExpressionTreeMaker(object[] token, Type targetType, string[] nameValueTable)
  67. {
  68. NameValueTable = nameValueTable;
  69. TargetType = targetType;
  70. VarDict = new Dictionary<string, ParameterExpression>();
  71. LabelDict = new Dictionary<string, Expression>();
  72. Environment = Expression.Parameter(typeof(ScriptEngine.Environment));
  73. GlobalVarList = new List<string>();
  74. MethodDict = new Dictionary<string, MethodInfo>();
  75. PropertyDict = new Dictionary<string, PropertyInfo>();
  76. GetTargetInfo();
  77. Parse(token);
  78. }
  79. #region 準備系
  80. static Dictionary<string, FieldInfo> GetEnvironmentFieldInfo()
  81. {
  82. var ret = new Dictionary<string, FieldInfo>();
  83. foreach (var item in typeof(Environment).GetFields())
  84. {
  85. var atr = item.GetCustomAttributes(typeof(ScriptMemberAttribute), true);
  86. foreach (ScriptMemberAttribute a in atr)
  87. {
  88. ret.Add(a.Name, item);
  89. }
  90. }
  91. return ret;
  92. }
  93. static Dictionary<string, PropertyInfo> GetEnvironmentPropertyInfo()
  94. {
  95. var ret = new Dictionary<string, PropertyInfo>();
  96. foreach (var item in typeof(Environment).GetProperties())
  97. {
  98. var atr = item.GetCustomAttributes(typeof(ScriptMemberAttribute), true);
  99. foreach (ScriptMemberAttribute a in atr)
  100. {
  101. ret.Add(a.Name, item);
  102. }
  103. }
  104. return ret;
  105. }
  106. /// <summary>
  107. /// 用意されたメソッドの定義
  108. /// </summary>
  109. /// <returns></returns>
  110. static Dictionary<string, MethodInfo> GetStaticMethodInfo()
  111. {
  112. var ret = new Dictionary<string, MethodInfo>();
  113. var mu = typeof(Masa.Lib.MathUtil);
  114. var xmath = typeof(Masa.Lib.XNA.MathUtilXNA);
  115. var vals = typeof(ValueCreaterFunctions);
  116. Type vec = typeof(Vector2);
  117. Type[] vecs = new[]{vec};
  118. Type[][] args = Enumerable.Range(0, 4).Select(x => Enumerable.Repeat(ValueType, x).ToArray()).ToArray();
  119. ret.Add("cos", mu.GetMethod("Cos"));
  120. ret.Add("sin", mu.GetMethod("Sin"));
  121. ret.Add("tan", mu.GetMethod("Tan"));
  122. ret.Add("atan", mu.GetMethod("Atan2"));
  123. ret.Add("pow", typeof(Masa.Lib.MathUtil).GetMethod("Pow", new[] { ValueType, ValueType }));
  124. ret.Add("abs", typeof(Math).GetMethod("Abs", new[] { ValueType }));
  125. ret.Add("max", typeof(Math).GetMethod("Max", new[] { ValueType, ValueType }));
  126. ret.Add("min", typeof(Math).GetMethod("Min", new[] { ValueType, ValueType }));
  127. ret["float2"] = vals.GetMethod("MakeVector2", args[2]);
  128. ret["float3"] = vals.GetMethod("MakeVector3", args[3]);
  129. ret["float"] = vals.GetMethod("MakeFloat", args[1]);
  130. ret["double"] = vals.GetMethod("MakeDouble", args[1]);
  131. ret["int"] = vals.GetMethod("MakeInteger", args[1]);
  132. ret["float2arc"] = xmath.GetMethod("GetVector", args[2]);
  133. ret["float2ang"] = xmath.GetMethod("Angle", vecs);
  134. ret["float2len"] = vals.GetMethod("GetVectorLength", vecs);
  135. ret["float2len2"] = vals.GetMethod("GetVectorLengthSquared", vecs);
  136. return ret;
  137. }
  138. /// <summary>
  139. /// 型情報のキャッシュを作る。明示的に呼ばなくても必要なら作られる
  140. /// </summary>
  141. public static void MakeTargetInfoCache(Type target)
  142. {
  143. if (ReflectionCashe.ContainsKey(target)) return;
  144. var md = new Dictionary<string, MethodInfo>();
  145. var pd = new Dictionary<string, PropertyInfo>();
  146. foreach (var item in target.GetMembers(BindingFlags.NonPublic | BindingFlags.Public
  147. | BindingFlags.Instance | BindingFlags.Static
  148. //| BindingFlags.SetProperty | BindingFlags.GetProperty
  149. | BindingFlags.InvokeMethod | BindingFlags.FlattenHierarchy))
  150. {
  151. var atr = item.GetCustomAttributes(typeof(ScriptMemberAttribute), true).OfType<ScriptMemberAttribute>();
  152. int num = atr.Count();
  153. if (num == 0)
  154. {
  155. continue;
  156. }
  157. if (num > 1)
  158. {
  159. throw new Exception(item.Name + ":同一のメンバに対し複数のScriptMemberAttributeが定義されている。\n" + atr.Select(a => a.Name).Aggregate("", (x, sum) => sum + "\n" + x));
  160. }
  161. var atribute = atr.First();
  162. if (item.MemberType == MemberTypes.Method)
  163. {
  164. md.Add(atribute.Name, (MethodInfo)item);
  165. }
  166. if (item.MemberType == MemberTypes.Property)
  167. {
  168. pd.Add(atribute.Name, (PropertyInfo)item);
  169. }
  170. }
  171. ReflectionCashe.Add(target, new ClassReflectionInfo(md, pd));
  172. }
  173. void GetTargetInfo()
  174. {
  175. if (!ReflectionCashe.ContainsKey(TargetType))
  176. {
  177. MakeTargetInfoCache(TargetType);
  178. }
  179. MethodDict = ReflectionCashe[TargetType].MethodDict;
  180. PropertyDict = ReflectionCashe[TargetType].PropertyDict;
  181. }
  182. public string OutputClassInformation()
  183. {
  184. return DocumentCreater.OutputClass(MethodDict, PropertyDict);
  185. }
  186. #endregion
  187. /// <summary>
  188. /// ラベルの塊をActionとして返す。ラベルの初回読み込みでは式木をコンパイルするので速度に注意。以降はキャッシュされる
  189. /// </summary>
  190. /// <param name="label"></param>
  191. /// <returns>ラベルが存在しなければnull</returns>
  192. public Action<Environment> GetLabelStatement(string label)
  193. {
  194. if (LabelStatementCashe == null)
  195. {
  196. LabelStatementCashe = new Dictionary<string, Action<Environment>>();
  197. }
  198. if (!LabelStatementCashe.ContainsKey(label))
  199. {
  200. if (LabelDict.ContainsKey(label))
  201. {
  202. LabelStatementCashe.Add(label, Expression.Lambda<Action<ScriptEngine.Environment>>(Expression.Block(VarDict.Values, LabelDict[label], Expression.Label(ExitLabel)), Environment).Compile());
  203. }
  204. else
  205. {
  206. return null;
  207. }
  208. }
  209. return LabelStatementCashe[label];
  210. }
  211. #region Compiler
  212. /// <summary>
  213. /// スクリプト全体をコンパイルする
  214. /// </summary>
  215. /// <param name="mtd">出力先</param>
  216. public void Compile(System.Reflection.Emit.MethodBuilder mtd)
  217. {
  218. var lambda = Expression.Lambda<Action<ScriptEngine.Environment>>(Expression.Block(VarDict.Values, TotalBlock), Environment);
  219. lambda.CompileToMethod(mtd);
  220. }
  221. public bool CompileLabel(string label, System.Reflection.Emit.MethodBuilder mtd)
  222. {
  223. if (!LabelDict.ContainsKey(label)) return false;
  224. var lambda = Expression.Lambda<Action<ScriptEngine.Environment>>(Expression.Block(VarDict.Values, LabelDict[label]), Environment);
  225. lambda.CompileToMethod(mtd);
  226. return true;
  227. }
  228. public Type CompileToClass(Type original, string className, System.Reflection.Emit.ModuleBuilder mb)
  229. {
  230. TypeBuilder tp = mb.DefineType(className, TypeAttributes.Public, original);
  231. Func<string, MethodBuilder> define = (n) => tp.DefineMethod(n, MethodAttributes.Family | MethodAttributes.HideBySig | MethodAttributes.ReuseSlot | MethodAttributes.Virtual, null, new[] { typeof(Environment) });
  232. Compile(define("ScriptMain"));
  233. //foreach (var item in original.GetMethods().Where(m=>m.GetCustomAttributes(typeof(ScriptDefinedMethodAttribute), true).Count() > 0))
  234. //{
  235. // if (LabelExist(item.Name))
  236. // {
  237. // CompileLabel(item.Name, define(item.Name));
  238. // }
  239. //}
  240. foreach (var item in LabelDict)
  241. {
  242. CompileLabel(item.Key, define("Script" + item.Key));
  243. }
  244. var getter = tp.DefineMethod("get_GlobalVarNumber", MethodAttributes.Family | MethodAttributes.SpecialName | MethodAttributes.HideBySig);
  245. Expression.Lambda<Func<int>>(Expression.Constant(GlobalVarNumber)).CompileToMethod(getter);
  246. tp.DefineProperty("GlobalVarNumber", PropertyAttributes.None, typeof(int), null).SetGetMethod(getter);
  247. //tp.DefineField("GlobalVarNumber", typeof(int), FieldAttributes.Private | FieldAttributes.Literal).SetConstant(GlobalVarNumber);
  248. return tp.CreateType();
  249. }
  250. #endregion
  251. public bool LabelExist(string label)
  252. {
  253. return LabelDict.ContainsKey(label);
  254. }
  255. #region Parse
  256. void Parse(object[] tokens)
  257. {
  258. var topStatements = new List<Expression>();
  259. Lines = Parser.Parse(tokens);
  260. for (int i = 0; i < Lines.Length; i++)
  261. {
  262. if (Lines[i].Level == 0)
  263. {
  264. var e = ProcessStatement(Lines[i]);
  265. if (e != null) topStatements.Add(e);
  266. }
  267. }
  268. //var returnTarget = Expression.Label(ExitLabel);
  269. topStatements.Add(Expression.Label(ExitLabel));
  270. TotalBlock = topStatements.ToArray();
  271. var lambda = Expression.Lambda<Action<ScriptEngine.Environment>>(Expression.Block(VarDict.Values, TotalBlock), Environment);
  272. //Console.WriteLine(lambda.ToString());
  273. Statement = lambda.Compile();
  274. InitStatement = GetLabelStatement("init");
  275. }
  276. /// <summary>
  277. /// 文字列を変数(内部変数、Global変数、外部変数、列挙文字列すべて)としてパース。
  278. /// </summary>
  279. /// <param name="id"></param>
  280. /// <returns></returns>
  281. Expression ParseVariable(string id)
  282. {
  283. if (id[0] == Scanner.StringLiteralMark)
  284. {
  285. return RegistLiteral(id.Substring(1));
  286. }
  287. ParameterExpression prm;
  288. if (VarDict.TryGetValue(id, out prm))
  289. {
  290. return prm;
  291. }
  292. PropertyInfo prp;
  293. if (PropertyDict.TryGetValue(id, out prp))
  294. {
  295. return Expression.Property(Expression.Convert(Expression.Field(Environment, ScriptEngine.Environment.Info_TargetObject), TargetType), prp);
  296. }
  297. if (EnvironmentField.ContainsKey(id))
  298. {
  299. return Expression.Field(Environment, EnvironmentField[id]);
  300. }
  301. if (EnvironmentProperty.ContainsKey(id))
  302. {
  303. return Expression.Property(Environment, EnvironmentProperty[id]);
  304. }
  305. if (ConstantValueDict.ContainsKey(id))
  306. {
  307. return ConstantValueDict[id];
  308. }
  309. if (NameValueTable != null && NameValueTable.Contains(id))
  310. {
  311. return MakeConstantExpression(Array.FindIndex(NameValueTable, s => s == id));
  312. }
  313. int gvar = GlobalVarList.FindIndex(k => k == id);
  314. if (gvar != -1)
  315. {
  316. return Expression.Property(Environment, ScriptEngine.Environment.Info_Item, Expression.Constant(gvar, typeof(int)));
  317. }
  318. return RegistLiteral(id);
  319. }
  320. Expression RegistLiteral(string id)
  321. {
  322. return Expression.Constant(id, typeof(string));
  323. //int i = StringLiterals.FindIndex(s => s == id);
  324. //if (i == -1)
  325. //{
  326. // StringLiterals.Add(id);
  327. // return MakeConstantExpression(StringLiterals.Count - 1);
  328. //}
  329. //else
  330. //{
  331. // return MakeConstantExpression(i);
  332. //}
  333. }
  334. static ConstantExpression MakeConstantExpression(float value)
  335. {
  336. return Expression.Constant(value, ValueType);
  337. }
  338. /// <summary>
  339. ///
  340. /// </summary>
  341. /// <param name="line">引数とオプションがくっついたトークン列</param>
  342. /// <returns></returns>
  343. Option[] GetOptions(object[] line)
  344. {
  345. return line.OfType<OptionBlock>().Select((o) => ParseOptionBlock(o)).ToArray();
  346. }
  347. Option ParseOptionBlock(OptionBlock opt)
  348. {
  349. return new Option(opt.Name, GetArgs(opt.Tokens));
  350. }
  351. #endregion
  352. #region 式の生成
  353. Expression ProcessStatement(Line line)
  354. {
  355. //line.Tokens = ParseLine(line.Tokens);//括弧やオプションをまとめる
  356. string id = (string)line.Tokens[0];
  357. if (id == "var")
  358. {
  359. return DefineVariable(line);
  360. }
  361. if (id == "varg")
  362. {
  363. if (line.Tokens.Length > 2)
  364. {
  365. throw new ParseException("varg宣言の後に無効なトークン(初期化不可能)", line);
  366. }
  367. //return new GlobalVar((string)line[1]);
  368. GlobalVarList.Add((string)line.Tokens[1]);
  369. return null;
  370. }
  371. if (line.Tokens.Length > 1 && line.Tokens[1] is Marks)
  372. {
  373. Marks m = (Marks)line.Tokens[1];
  374. if (m == Marks.Sub || m == Marks.SubNeg || m == Marks.SubPos || m == Marks.SubMul || m == Marks.SubDiv || m == Marks.Inc || m == Marks.Dec)
  375. {
  376. return ProcessAssign(line);
  377. }
  378. else//line[1]がMarkかつ代入系でない→ありえない
  379. {
  380. //throw new Exception("トークンの2番目が不正なマーク Line" + line.Number + ":" + m.ToString());
  381. throw new ParseException("トークンの2番目が不正なマーク", line);
  382. }
  383. }
  384. //line[1]がMarkでない && var系でない
  385. return ProcessNormalStatement(line);
  386. }
  387. Expression DefineVariable(Line line)
  388. {
  389. ParameterExpression v;
  390. string name = (string)line.Tokens[1];
  391. Type type = typeof(Value);
  392. if (line.Tokens.Length >= 4 && line.Tokens[2].Equals(Marks.Dollar))// var hoge $ float2
  393. {
  394. type = TypeNameDictionary[line.Tokens[3] as string];
  395. }
  396. v = Expression.Parameter(type, name);
  397. VarDict.Add(name, v);
  398. if (line.Tokens.Length >= 4)// var hoge = 1
  399. {
  400. int rightPos = Array.FindIndex(line.Tokens, x => x.Equals(Marks.Sub));
  401. if (rightPos == -1)
  402. {
  403. return null;//初期化なし
  404. }
  405. return Expression.Assign(v, ParsePareBlock(new PareBlock(line.Tokens.Skip(rightPos + 1).ToArray())));
  406. }
  407. else
  408. {
  409. return null; //初期化なし
  410. }
  411. }
  412. /// <summary>
  413. /// 代入処理
  414. /// </summary>
  415. /// <param name="line"></param>
  416. /// <returns></returns>
  417. Expression ProcessAssign(Line line)
  418. {
  419. Marks m = (Marks)line.Tokens[1];
  420. Expression target = ParseVariable((string)line.Tokens[0]);//代入先の変数/プロパティ
  421. if (m == Marks.Inc)
  422. {
  423. //return Expression.Assign(target, Expression.Increment(target));
  424. return Expression.PostIncrementAssign(target);
  425. }
  426. else if (m == Marks.Dec)
  427. {
  428. //return Expression.Assign(target, Expression.Decrement(target));
  429. return Expression.PostDecrementAssign(target);
  430. }
  431. else
  432. {
  433. Expression r = ParsePareBlock(new PareBlock(line.Tokens.Skip(2).ToArray()));//代入される値
  434. //Expression r = ParsePareBlock(new PareBlock(new[] { line.Tokens[2] }));
  435. switch (m)
  436. {
  437. case Marks.Sub:
  438. return Expression.Assign(target, r);
  439. case Marks.SubPos:
  440. return Expression.AddAssign(target, r);
  441. case Marks.SubNeg:
  442. return Expression.SubtractAssign(target, r);
  443. case Marks.SubMul:
  444. return Expression.MultiplyAssign(target, r);
  445. case Marks.SubDiv:
  446. return Expression.DivideAssign(target, r);
  447. }
  448. }
  449. //throw new Exception("Line " + line.Number + "がおかしい");
  450. throw new ParseException("おかしい", line);
  451. }
  452. /// <summary>
  453. /// Global Varの処理
  454. /// </summary>
  455. /// <param name="key"></param>
  456. /// <returns></returns>
  457. //IndexExpression GetEnvironmentValue(string key)
  458. //{
  459. // int i = GlobalVarList.FindIndex((k) => k == key);
  460. // if (i == -1)
  461. // {
  462. // throw new ScriptEngine.ParseException("変数" + key + "は未定義");
  463. // }
  464. // return Expression.Property(Environment, ScriptEngine.Environment.Info_Item, Expression.Constant(i, typeof(int)));
  465. //}
  466. /// <summary>
  467. /// if ループ stateなどの制御文や、fireなどの外部命令を処理
  468. /// 一般文、つまり代入文や宣言文以外の、「識別子 引数リスト・・・」となる文
  469. /// </summary>
  470. /// <param name="line">PareBlockやOptionBlockに整形済みのトークン行</param>
  471. /// <returns></returns>
  472. Expression ProcessNormalStatement(Line line)
  473. {
  474. string id = (string)line.Tokens[0];
  475. Func<Expression[]> args = () => GetArgs(line.Tokens.Skip(1));
  476. switch (id)
  477. {
  478. case "if":
  479. return MakeIfStatement(line, Expression.NotEqual(args()[0], ZeroExpression));
  480. //return Expression.IfThen(Expression.NotEqual(args()[0], ZERO), GetBlock(line));
  481. case "else"://if - else の流れで処理するため単体ではスルー
  482. return null;
  483. case "while":
  484. goto case "repeat";
  485. case "repeat":
  486. LabelTarget label = Expression.Label(line.Number.ToString());
  487. return Expression.Loop(GetBlockWithBreak(line, Expression.Equal(args()[0], ZeroExpression), label), label);
  488. case "loop":
  489. return MakeLoopStatement(line);
  490. case "goto":
  491. var assign = Expression.Assign(Expression.Property(Environment, ScriptEngine.Environment.Info_State), args()[0]);
  492. return Expression.Block(assign, Expression.Return(ExitLabel));
  493. //return Expression.Assign(Expression.Property(Environment, ScriptEngine.Environment.Info_State), args()[0]);
  494. case "state":
  495. return Expression.IfThen(Expression.Equal(Expression.Property(Environment, ScriptEngine.Environment.Info_State), args()[0]), GetBlock(line));
  496. case "label":
  497. LabelDict.Add((string)line.Tokens[1], GetBlock(line));
  498. return null;
  499. case "jump":
  500. string s = (string)line.Tokens[1];
  501. if (LabelDict.ContainsKey(s))
  502. {
  503. return LabelDict[s];
  504. }
  505. else
  506. {
  507. throw new ParseException("未定義のラベル " + s, line);
  508. }
  509. case "blank":
  510. //return null;
  511. return Expression.Empty();
  512. default:
  513. if (StaticMethodDict.ContainsKey(id))//オプション無しの前提
  514. {
  515. return Expression.Call(StaticMethodDict[id], args());
  516. }
  517. if (MethodDict.ContainsKey(id))
  518. {
  519. return CallExternalMethod(id, line.Tokens.Slice(1, line.Tokens.Length - 1));
  520. }
  521. throw new ParseException(": 未定義のステートメント " + id, line);
  522. }
  523. }
  524. Expression MakeIfStatement(Line line, Expression pred)
  525. {
  526. var index = line.Index;
  527. var ifBlock = GetBlock(line);
  528. //return Expression.IfThen(Expression.NotEqual(args()[0], ZERO), GetBlock(line));
  529. if (Lines.Length > index + 2)
  530. {
  531. //var elseLine = Lines.Skip(index).FirstOrDefault(l => l.Level == line.Level && l.Tokens.Length == 1 && l.Tokens[0].Equals("else"));
  532. var elseLine = Lines.Skip(index + 1).FirstOrDefault(l => l.Level == line.Level && l.Tokens.Length >= 1);
  533. if (elseLine != null && elseLine.Tokens[0].Equals("else"))
  534. {
  535. return Expression.IfThenElse(pred, ifBlock, GetBlock(elseLine));
  536. }
  537. }
  538. return Expression.IfThen(pred, ifBlock);
  539. }
  540. //loop (times) (freq) (from)
  541. Expression MakeLoopStatement(Line line)
  542. {
  543. string id = (string)line.Tokens[0];
  544. Option[] opt = GetOptions(line.Tokens.Slice(1, line.Tokens.Length - 1));
  545. var arg = GetArgs(line.Tokens.Slice(1, line.Tokens.Length - 1));
  546. var times = arg[0];
  547. var freq = arg[1];
  548. var from = arg[2];
  549. Expression frame = null;
  550. Expression last = null;
  551. Expression firstSentence;
  552. if (opt.Length == 0)
  553. {
  554. frame = Expression.Field(Environment, ScriptEngine.Environment.Info_StateFrame);
  555. }
  556. else
  557. {
  558. Option o = opt.FirstOrDefault((op) => op.Name == "counter");
  559. if (o == null)
  560. {
  561. throw new ParseException("Loop文に無効なオプション " + opt.First().Name + "が指定された", line);
  562. }
  563. if (o.Args.Length > 2)
  564. {
  565. throw new ParseException("Loop文のcounterオプションの引数の数が変", line);
  566. }
  567. frame = o.Args[0];
  568. if (o.Args.Length == 2)
  569. {
  570. last = o.Args[1];
  571. }
  572. else
  573. {
  574. last = Expression.Subtract(frame, Expression.Constant(1f, ValueType));
  575. }
  576. }
  577. firstSentence = Expression.AndAlso
  578. (
  579. Expression.GreaterThanOrEqual(frame, from),
  580. Expression.OrElse
  581. (
  582. Expression.Equal(times, ZeroExpression),
  583. Expression.LessThan(frame, Expression.Add(from, Expression.Multiply(freq, times)))
  584. )
  585. );
  586. if (opt.Length == 0)
  587. {
  588. //Expression fr = Expression.Field(Environment, ScriptEngine.Environment.Info_StateFrame);
  589. //if ((stateFrame >= from &&
  590. //(times == 0 || stateFrame < from + freq * times)) &&
  591. //((stateFrame - from) % freq == 0))
  592. return Expression.IfThen(Expression.AndAlso
  593. (
  594. firstSentence,
  595. Expression.Equal(Expression.Modulo(Expression.Subtract(frame, from), freq), ZeroExpression)
  596. ),
  597. GetBlock(line));
  598. }
  599. else
  600. {
  601. //Option o = opt.FirstOrDefault((op) => op.Name == "counter");
  602. //if (o == null)
  603. //{
  604. // throw new ParseException("Loop文に無効なオプション " + opt.First().Name + "が指定された", line);
  605. //}
  606. //if (o.Args.Length != 2)
  607. //{
  608. // throw new ParseException("Loop文のcounterオプションの引数の数が変", line);
  609. //}
  610. //Expression fr = o.Args[0];
  611. //Expression lfr = o.Args[1];
  612. //times = arg[0], freq = arg[1], from = arg[2]
  613. //if (
  614. // fr >= from &&
  615. // (times == 0 || freq * times > (fr - from)) &&
  616. // (
  617. // lafr < from
  618. // ||
  619. // ((int)((fr - from) / freq) > (int)((lafr - from) / freq))
  620. // )
  621. // )
  622. Func<Expression, Expression> div = (counter) => Expression.Convert
  623. (
  624. Expression.Divide
  625. (
  626. Expression.Subtract(Expression.Subtract(counter, Expression.Constant(0.1f, typeof(float))), from),
  627. freq
  628. ),
  629. typeof(int)
  630. );
  631. return Expression.IfThen
  632. (
  633. Expression.AndAlso
  634. (
  635. firstSentence,
  636. Expression.OrElse
  637. (
  638. Expression.LessThan(last, from),
  639. Expression.GreaterThan(div(frame), div(last))
  640. )
  641. ),
  642. //Expression.AndAlso
  643. //(
  644. // Expression.GreaterThanOrEqual(fr, from),
  645. // Expression.AndAlso
  646. // (
  647. // Expression.OrElse
  648. // (
  649. // Expression.Equal(arg[0], ZERO),
  650. // Expression.GreaterThan(Expression.Multiply(freq, times), Expression.Subtract(fr, from))
  651. // ),
  652. // Expression.OrElse
  653. // (
  654. // Expression.LessThan(lfr, from),
  655. // Expression.GreaterThan(
  656. // Expression.Convert(
  657. // Expression.Divide(
  658. // Expression.Subtract(fr, from),
  659. // freq
  660. // ),
  661. // typeof(int)
  662. // ),
  663. // Expression.Convert(
  664. // Expression.Divide(
  665. // Expression.Subtract(lfr, arg[2]),
  666. // arg[1]
  667. // ),
  668. // typeof(int)
  669. // )
  670. // )
  671. // )
  672. // )
  673. //),
  674. GetBlock(line)
  675. );
  676. }
  677. }
  678. /// <summary>
  679. /// Targetの持つメソッドや関数を呼ぶ
  680. /// </summary>
  681. /// <param name="id">メソッド名</param>
  682. /// <param name="l">名前を除いたトークン列</param>
  683. /// <returns></returns>
  684. Expression CallExternalMethod(string id, object[] l)
  685. {
  686. //object[] l = line.Tokens.Slice(1, line.Tokens.Length - 1);
  687. List<Expression> args = GetArgs(l).ToList();
  688. Option[] options = GetOptions(l);
  689. ScriptMemberAttribute atrribute = (ScriptMemberAttribute)MethodDict[id].GetCustomAttributes(typeof(ScriptMemberAttribute), true).First();
  690. if (atrribute.OptionName != null)//オプションが定義されていれば
  691. {
  692. string[] name = atrribute.OptionName;
  693. int[] num = atrribute.OptionArgNum;
  694. var less = options.Select(o => o.Name).Except(name);
  695. if (less.Any())
  696. {
  697. throw new ParseException(id + "メソッド呼び出しに無効なオプション指定 : " + less.Aggregate((src, dst) => dst + ", " + src));
  698. }
  699. for (int i = 0; i < name.Length; i++)
  700. {
  701. Option op = options.FirstOrDefault(o => o.Name == name[i]);
  702. if (op == null)//オプションが指定されていなければNaNで埋める
  703. {
  704. args.AddRange(Enumerable.Repeat(NanExpression, num[i]));
  705. }
  706. else
  707. {
  708. IEnumerable<Expression> addition = op.Args.ToArray();
  709. if (op.Args.Count() < num[i])//不足はNaNで埋める
  710. {
  711. addition = addition.Concat(Enumerable.Repeat(NanExpression, num[i] - addition.Count()));
  712. }
  713. args.AddRange(addition.Take(num[i]));
  714. }
  715. }
  716. }
  717. var method = MethodDict[id];
  718. var param = method.GetParameters();
  719. if (param.Length != args.Count)
  720. {
  721. throw new ParseException("外部メソッド呼び出しで引数とパラメータの数の不一致\n" + method.ToString() + String.Format(" need {0} params but {1} args.", param.Length, args.Count));
  722. }
  723. for (int i = 0; i < param.Length; i++)
  724. {
  725. if (!param[i].ParameterType.IsAssignableFrom(args[i].Type))
  726. {
  727. args[i] = Expression.Convert(args[i], param[i].ParameterType);
  728. }
  729. }
  730. return Expression.Call(Expression.Convert(Expression.Field(Environment, ScriptEngine.Environment.Info_TargetObject), TargetType), method, args);
  731. }
  732. /// <summary>
  733. ///
  734. /// </summary>
  735. /// <param name="user">ブロックを必要としているLine</param>
  736. /// <returns></returns>
  737. Expression GetBlock(Line user)
  738. {
  739. //if (Lines[user.Index + 1].Level != user.Level + 1)
  740. //{
  741. // throw new ParseException("ブロックの書き方が不正", user);
  742. //}
  743. //var ret = new List<Expression>();
  744. //for (int i = user.Index + 1; i < Lines.Length; i++)
  745. //{
  746. // if (Lines[i].Level <= user.Level) break;
  747. // if (Lines[i].Level == user.Level + 1)
  748. // {
  749. // var e = ProcessStatement(Lines[i]);
  750. // if (e != null) ret.Add(e);
  751. // }
  752. //}
  753. //return Expression.Block(ret.ToArray());
  754. return InnerGetBlock(new List<Expression>(), user);
  755. }
  756. /// <summary>
  757. ///
  758. /// </summary>
  759. /// <param name="user">whileなどのExpression</param>
  760. /// <param name="test">ループの終了条件</param>
  761. /// <param name="label"></param>
  762. /// <returns></returns>
  763. Expression GetBlockWithBreak(Line user, Expression test, LabelTarget label)
  764. {
  765. //if (Lines[user.Index + 1].Level != user.Level + 1)
  766. //{
  767. // throw new ParseException("ブロックの書き方が不正", user);
  768. //}
  769. //var ret = new List<Expression>();
  770. //ret.Add(Expression.IfThen(test, Expression.Break(label)));
  771. //for (int i = user.Index + 1; i < Lines.Length; i++)
  772. //{
  773. // if (Lines[i].Level <= user.Level) break;
  774. // if (Lines[i].Level == user.Level + 1)
  775. // {
  776. // var e = ProcessStatement(Lines[i]);
  777. // if (e != null)
  778. // {
  779. // ret.Add(e);
  780. // }
  781. // }
  782. //}
  783. //return Expression.Block(ret.ToArray());
  784. var list = new List<Expression>();
  785. list.Add(Expression.IfThen(test, Expression.Break(label)));
  786. return InnerGetBlock(list, user);
  787. }
  788. Expression InnerGetBlock(List<Expression> list, Line user)
  789. {
  790. if (Lines[user.Index + 1].Level != user.Level + 1)
  791. {
  792. throw new ParseException("ブロックの書き方が不正", user);
  793. }
  794. for (int i = user.Index + 1; i < Lines.Length; i++)
  795. {
  796. if (Lines[i].Level <= user.Level) break;
  797. if (Lines[i].Level == user.Level + 1)
  798. {
  799. var e = ProcessStatement(Lines[i]);
  800. if (e != null) list.Add(e);
  801. }
  802. }
  803. return Expression.Block(list.ToArray());
  804. }
  805. static readonly Marks[] UnaryOperators = new[] { Marks.Pos, Marks.Neg, Marks.Not };
  806. /// <summary>
  807. /// 単項演算子を処理してExpressionにする
  808. /// </summary>
  809. /// <param name="tokens">ExpressionとMarkの混合物</param>
  810. /// <returns>単項演算子処理済みのExpressionと(二項演算子であるはずの)Markの混合物</returns>
  811. object[] ProcessUnaryExpression(object[] tokens)
  812. {
  813. List<object> l = new List<object>();
  814. for (int i = 0; i < tokens.Length; i++)
  815. {
  816. if (tokens[i] is Marks)
  817. {
  818. Marks m = (Marks)tokens[i];
  819. if (UnaryOperators.Contains(m) && (i == 0 || tokens[i - 1] is Marks))//単項演算子であれば式の先頭、もしくはその前がExpressionでない
  820. {
  821. if (!(tokens[i + 1] is Expression))
  822. {
  823. throw new ParseException("単項演算子の直後が式でない");
  824. }
  825. Expression e = (Expression)tokens[i + 1];
  826. i++;//一つ後の項は処理済み
  827. switch (m)
  828. {
  829. case Marks.Pos:
  830. l.Add(Expression.UnaryPlus(e));
  831. continue;
  832. case Marks.Neg:
  833. l.Add(Expression.Negate(e));
  834. continue;
  835. case Marks.Not:
  836. l.Add(BoolToFloat(Expression.Equal(e, ZeroExpression)));//val == 0 => 1, val != 0 => 0
  837. continue;
  838. }
  839. }
  840. }
  841. l.Add(tokens[i]);
  842. }
  843. return l.ToArray();
  844. }
  845. /// <summary>
  846. /// 戻り値は全てfloat型
  847. /// </summary>
  848. /// <param name="m"></param>
  849. /// <param name="l"></param>
  850. /// <param name="r"></param>
  851. /// <returns></returns>
  852. Expression MakeBinaryExpression(Marks m, Expression l, Expression r)
  853. {
  854. switch (m)
  855. {
  856. case Marks.Pos:
  857. return Expression.Add(l, r);
  858. case Marks.Neg:
  859. return Expression.Subtract(l, r);
  860. case Marks.Mul:
  861. return Expression.Multiply(l, r);
  862. case Marks.Div:
  863. return Expression.Divide(l, r);
  864. case Marks.Mod:
  865. return Expression.Modulo(l, r);
  866. case Marks.And:
  867. return BoolToFloat(Expression.AndAlso(FloatToBool(l), FloatToBool(r)));
  868. case Marks.Or:
  869. return BoolToFloat(Expression.OrElse(FloatToBool(l), FloatToBool(r)));
  870. case Marks.Equal:
  871. return BoolToFloat(Expression.Equal(l, r));
  872. case Marks.NotEqual:
  873. return BoolToFloat(Expression.NotEqual(l, r));
  874. case Marks.Big:
  875. return BoolToFloat(Expression.GreaterThan(l, r));
  876. case Marks.BigEqual:
  877. return BoolToFloat(Expression.GreaterThanOrEqual(l, r));
  878. case Marks.Small:
  879. return BoolToFloat(Expression.LessThan(l, r));
  880. case Marks.SmallEqual:
  881. return BoolToFloat(Expression.LessThanOrEqual(l, r));
  882. }
  883. throw new ParseException("ありえない");
  884. }
  885. #endregion
  886. /// <summary>
  887. ///
  888. /// </summary>
  889. /// <param name="line">引数とオプションがくっついたトークン列</param>
  890. /// <returns></returns>
  891. Expression[] GetArgs(IEnumerable<object> line)
  892. {
  893. var ret = new List<Expression>();
  894. foreach (var item in line)
  895. {
  896. if (item is PareBlock)
  897. {
  898. ret.Add(ParsePareBlock((PareBlock)item));
  899. }
  900. else if (item is OptionBlock)
  901. {
  902. }
  903. else if (item is string)//変数か文字列?
  904. {
  905. ret.Add(ParseVariable((string)item));
  906. }
  907. else if (item is Value)//数値リテラル
  908. {
  909. ret.Add(Expression.Constant(item, typeof(float)));
  910. }
  911. else if (item is Marks)
  912. {
  913. throw new ParseException("引数リスト内に不正なMark " + Enum.GetName(typeof(Marks), item));
  914. }
  915. else
  916. {
  917. throw new ParseException("予期せぬエラー");
  918. }
  919. }
  920. return ret.ToArray();
  921. }
  922. class Option
  923. {
  924. public string Name;
  925. public Expression[] Args;
  926. public Option(string name, Expression[] arg)
  927. {
  928. Name = name;
  929. Args = arg;
  930. }
  931. }
  932. /// <summary>
  933. /// PareBlockをパースしてひとつの値のExpressionにする
  934. /// </summary>
  935. /// <param name="pare"></param>
  936. /// <returns></returns>
  937. Expression ParsePareBlock(PareBlock pare)
  938. {
  939. object[] l = pare.tokens;
  940. if (l[0] is string)
  941. {
  942. if (StaticMethodDict.ContainsKey((string)l[0]))
  943. {
  944. Expression[] args = GetArgs(l.Slice(1, l.Length - 1));
  945. return Expression.Call(StaticMethodDict[(string)l[0]], args);
  946. }
  947. if (MethodDict.ContainsKey((string)l[0]))//関数の時
  948. {
  949. return CallExternalMethod((string)l[0], l.Slice(1, l.Length - 1));
  950. //Expression[] args = GetArgs(l.Slice(1, l.Length - 1));
  951. //return Expression.Call(Expression.Field(Environment, ScriptEngineEx.Environment.Info_TargetObject), MethodDict[(string)l[0]], args);
  952. }
  953. //関数を実行するExpression
  954. }
  955. return ParseArithExpression(pare);//多項式の時
  956. }
  957. static readonly Marks[][] OperatorPriorityList = new[]
  958. {
  959. new[]{ Marks.Mul, Marks.Div, Marks.Mod },
  960. new[]{ Marks.Pos, Marks.Neg },
  961. new[]{ Marks.Equal, Marks.NotEqual, Marks.Big, Marks.BigEqual, Marks.Small, Marks.SmallEqual },
  962. new[]{Marks.And, Marks.Or, },
  963. };
  964. Expression ParseArithExpression(PareBlock p)
  965. {
  966. //多項式構築
  967. List<object>[] list = new List<object>[2];
  968. int ind = 0;
  969. list[0] = new List<object>(ProcessToExpressionAndMark(p));
  970. list[1] = new List<object>();
  971. for (int i = 0; i < OperatorPriorityList.Length; i++)
  972. {
  973. for (int j = 0; j < list[ind].Count; j++)
  974. {
  975. if (list[ind][j] is Marks && Array.Exists(OperatorPriorityList[i], (mk) => (Marks)list[ind][j] == mk))
  976. {
  977. Expression e = MakeBinaryExpression((Marks)list[ind][j], (Expression)list[1 - ind][list[1 - ind].Count - 1], (Expression)list[ind][j + 1]);
  978. list[1 - ind].RemoveAt(list[1 - ind].Count - 1);//1つ前の項は使用済み
  979. j++;
  980. list[1 - ind].Add(e);
  981. }
  982. else
  983. {
  984. list[1 - ind].Add(list[ind][j]);
  985. }
  986. }
  987. //リストをスワップ
  988. list[ind].Clear();
  989. ind = 1 - ind;
  990. }
  991. return (Expression)list[ind][0];
  992. }
  993. static Expression FloatToBool(Expression val)
  994. {
  995. return Expression.NotEqual(val, ZeroExpression);
  996. }
  997. static Expression BoolToFloat(Expression val)
  998. {
  999. return Expression.Condition(val, OneExpression, ZeroExpression);
  1000. }
  1001. /// <summary>
  1002. /// 括弧やOptionを分離済みのトークンをExpressionと2項演算子Markの塊に変換
  1003. /// </summary>
  1004. /// <param name="p"></param>
  1005. /// <returns></returns>
  1006. object[] ProcessToExpressionAndMark(PareBlock p)
  1007. {
  1008. object[] l = p.tokens;
  1009. var tmp = new List<object>();
  1010. for (int i = 0; i < l.Length; i++)
  1011. {
  1012. if (l[i] is Value)
  1013. {
  1014. tmp.Add(Expression.Constant(l[i], ValueType));
  1015. }
  1016. else if (l[i] is Marks)
  1017. {
  1018. tmp.Add(l[i]);
  1019. }
  1020. else if (l[i] is PareBlock)
  1021. {
  1022. tmp.Add(ParsePareBlock((PareBlock)l[i]));
  1023. }
  1024. else if (l[i] is string)
  1025. {
  1026. tmp.Add(ParseVariable((string)l[i]));
  1027. }
  1028. else//OptionBlockなど?
  1029. {
  1030. throw new ParseException("予期せぬトークンが多項式構築中に出現");
  1031. }
  1032. }
  1033. return ProcessUnaryExpression(tmp.ToArray());
  1034. }
  1035. }
  1036. }