PageRenderTime 91ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/SimpleBrainFuck.Core/Core/SimpleTranslator.cs

http://ironbrainfuck.codeplex.com
C# | 365 lines | 322 code | 43 blank | 0 comment | 104 complexity | b4d758626fed703b4364e27c45463eea MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using ZackFlame.SimpleBrainFuck.Core.Statments;
  6. using ZackFlame.SimpleBrainFuck.TextResources;
  7. namespace ZackFlame.SimpleBrainFuck.Core
  8. {
  9. public sealed class SimpleTranslator
  10. {
  11. readonly List<BrainDef> SystemDefs = new List<BrainDef>();
  12. readonly BrainMacros[] SystemMacroses;
  13. public SimpleTranslator()
  14. {
  15. SystemDef("error", ms => SystemFunctions.Error(ms[0]), "text");
  16. SystemDef("shift", ms => SystemFunctions.Shift(int.Parse(ms[0])), "k");
  17. SystemDef("zero", ms => SystemFunctions.Zero());
  18. SystemDef("add", ms => SystemFunctions.Add(int.Parse(ms[0])), "k");
  19. SystemDef("sub", ms => SystemFunctions.Sub(int.Parse(ms[0])), "k");
  20. SystemDef("move", ms => SystemFunctions.Move(int.Parse(ms[0])), "k");
  21. SystemDef("copy", ms => SystemFunctions.Copy(int.Parse(ms[0]), int.Parse(ms[1])), "k", "t");
  22. SystemDef("repeat", ms => SystemFunctions.Repeat(ms[0], int.Parse(ms[1])), "text", "k");
  23. SystemDef("value", ms => SystemFunctions.Value(int.Parse(ms[0]), int.Parse(ms[1])), "k", "t");
  24. SystemDef("text", ms => SystemFunctions.Text(ms[0]), "text");
  25. SystemDef("type", ms => SystemFunctions.Type(ms[0], int.Parse(ms[1])), "text", "t");
  26. SystemDef("print", ms => SystemFunctions.Print(int.Parse(ms[0])), "k");
  27. SystemMacroses = new BrainMacros[]
  28. {
  29. new BrainMacros("__version__")
  30. {
  31. Value = typeof(SimpleParser).Assembly.GetName().Version.Major.ToString()
  32. },
  33. new BrainMacros("__platform__")
  34. {
  35. Value = Environment.OSVersion.Platform.ToString()
  36. },
  37. };
  38. }
  39. private void SystemDef(string name,
  40. Func<string[], string> function, params string[] macrosesNames)
  41. {
  42. SystemDefs.Add(new BrainDef(function)
  43. {
  44. Name = name,
  45. MacrosArgs = macrosesNames
  46. .Select(macrosName => new BrainMacros() { Name = macrosName })
  47. .ToList()
  48. });
  49. }
  50. public string RenderBrainFuck(IEnumerable<Statment> semanticTree)
  51. {
  52. Stack<BrainDef> callStack = new Stack<BrainDef>();
  53. return RenderTree(semanticTree, callStack, SystemMacroses, SystemDefs);
  54. }
  55. private string RenderTree(
  56. IEnumerable<Statment> semanticTree, Stack<BrainDef> callStack,
  57. IEnumerable<BrainMacros> macroses, IEnumerable<BrainDef> defs)
  58. {
  59. var currentDefs = new List<BrainDef>();
  60. var currentMacroses = new List<BrainMacros>();
  61. StringBuilder brainString = new StringBuilder();
  62. foreach (Statment statment in semanticTree)
  63. {
  64. if (statment is BrainDef)
  65. {
  66. BrainDef defStat = (BrainDef)statment;
  67. ProcessDef(defStat, currentDefs);
  68. }
  69. else if (statment is BrainIf)
  70. {
  71. BrainIf ifStat = (BrainIf)statment;
  72. var allMacroses = macroses.Concat(currentMacroses);
  73. var allDefs = defs.Concat(currentDefs);
  74. brainString.Append(RenderIf(ifStat, callStack, allMacroses, allDefs));
  75. }
  76. else if (statment is BrainCall)
  77. {
  78. BrainCall callStat = (BrainCall)statment;
  79. var allMacroses = macroses.Concat(currentMacroses);
  80. var allDefs = defs.Concat(currentDefs);
  81. BrainDef def = allDefs.LastOrDefault(d => d.Name == callStat.DefName);
  82. if (def == null)
  83. throw new CodeErrorException(callStat.Line, CodeErrors.Call_DefNotFound);
  84. if (callStack.Contains(def))
  85. throw new CodeErrorException(callStat.Line, CodeErrors.Call_CycleCall);
  86. if (callStat.CallArgs.Count != def.MacrosArgs.Count)
  87. throw new CodeErrorException(callStat.Line, CodeErrors.Call_InvalidCallArgsCount);
  88. brainString.Append(RenderCall(callStat, def, callStack, allMacroses, allDefs));
  89. }
  90. else if (statment is BrainMacrosAssignment)
  91. {
  92. var assignment = (BrainMacrosAssignment)statment;
  93. ProcessAssignment(assignment, macroses, currentMacroses);
  94. }
  95. else if (statment is BrainPureCode)
  96. {
  97. brainString.Append(((BrainPureCode)statment).Value);
  98. }
  99. brainString.AppendLineLF();
  100. }
  101. return brainString.ToString();
  102. }
  103. private void ProcessDef(BrainDef defStat, List<BrainDef> currentDefs)
  104. {
  105. if (!BrainDef.NameRegex.Match(defStat.Name).Success)
  106. throw new CodeErrorException(defStat.Line, CodeErrors.Def_InvalidName);
  107. if (currentDefs.Exists(def => def.Name == defStat.Name))
  108. throw new CodeErrorException(defStat.Line, CodeErrors.Def_DefWithNameAlreadyExists);
  109. var matches = from macro in defStat.MacrosArgs
  110. select BrainMacros.NameRegex.Match(macro.Name);
  111. if (!matches.All(m => m.Success))
  112. throw new CodeErrorException(defStat.Line, CodeErrors.Def_InvalidParameterName);
  113. if (defStat.MacrosArgs.Any(m => m.Negative))
  114. throw new CodeErrorException(defStat.Line, CodeErrors.Def_ParameterCantBeNegative);
  115. for (int i = 0; i < defStat.MacrosArgs.Count; i++)
  116. {
  117. var parameter = defStat.MacrosArgs[i];
  118. for (int j = i + 1; j < defStat.MacrosArgs.Count; j++)
  119. {
  120. if (parameter.Name == defStat.MacrosArgs[j].Name)
  121. throw new CodeErrorException(defStat.Line, CodeErrors.Def_ParameterAlreadyDefined);
  122. }
  123. }
  124. currentDefs.Add(defStat);
  125. }
  126. private string RenderIf(BrainIf ifStatment, Stack<BrainDef> callStack,
  127. IEnumerable<BrainMacros> macroses, IEnumerable<BrainDef> defs)
  128. {
  129. string parameter = CalculateArgument(ifStatment.Parameter, macroses);
  130. if (parameter == null)
  131. throw new CodeErrorException(ifStatment.Line, CodeErrors.Call_MacrosNotDefined);
  132. int tParam;
  133. if (!int.TryParse(parameter, out tParam))
  134. throw new CodeErrorException(ifStatment.Line, CodeErrors.Call_InvalidArgumentsFormat);
  135. StringBuilder result = new StringBuilder()
  136. .AppendLineLF(SystemFunctions.If(tParam))
  137. .Append(RenderTree(ifStatment.IfStatments, callStack, macroses, defs))
  138. .AppendLineLF(SystemFunctions.Else(tParam))
  139. .Append(RenderTree(ifStatment.ElseStatments, callStack, macroses, defs))
  140. .AppendLineLF(SystemFunctions.EndIf(tParam));
  141. return result.ToString();
  142. }
  143. private string RenderCall(BrainCall callStatment,
  144. BrainDef callTarget, Stack<BrainDef> callStack,
  145. IEnumerable<BrainMacros> macroses, IEnumerable<BrainDef> defs)
  146. {
  147. var callParameters = callStatment.CallArgs.Select(arg =>
  148. {
  149. string value = CalculateArgument(arg, macroses);
  150. if (value == null)
  151. throw new CodeErrorException(callStatment.Line, CodeErrors.Call_MacrosNotDefined);
  152. return value;
  153. });
  154. if (callTarget.CanRenderItself)
  155. {
  156. try
  157. {
  158. return callTarget.Render(callParameters.ToArray());
  159. }
  160. catch (FormatException)
  161. {
  162. throw new CodeErrorException(callStatment.Line, CodeErrors.Call_InvalidArgumentsFormat);
  163. }
  164. catch (ArgumentException ex)
  165. {
  166. throw new CodeErrorException(callStatment.Line, ex.Message);
  167. }
  168. catch (CodeErrorException ex)
  169. {
  170. ex.Line = callStatment.Line;
  171. throw ex;
  172. }
  173. }
  174. else
  175. {
  176. var definedMacroses = callTarget.MacrosArgs.Zip(callParameters,
  177. (m, p) => new BrainMacros() { Name = m.Name, Value = p });
  178. var allMacroses = macroses.Concat(definedMacroses);
  179. callStack.Push(callTarget);
  180. string rendered = RenderTree(callTarget.Statments, callStack, allMacroses, defs);
  181. callStack.Pop();
  182. return rendered;
  183. }
  184. }
  185. private void ProcessAssignment(BrainMacrosAssignment assignment,
  186. IEnumerable<BrainMacros> macroses, List<BrainMacros> currentMacroses)
  187. {
  188. if (!BrainMacros.NameRegex.Match(assignment.Name).Success)
  189. throw new CodeErrorException(assignment.Line, CodeErrors.Macro_InvalidName);
  190. var allMacroses = macroses.Concat(currentMacroses);
  191. string leftValue = CalculateArgument(assignment.Expression.Left, allMacroses);
  192. if (leftValue == null)
  193. throw new CodeErrorException(assignment.Line, CodeErrors.Call_MacrosNotDefined);
  194. string rightValue = CalculateArgument(assignment.Expression.Right, allMacroses);
  195. string result = null;
  196. string oper = assignment.Expression.Operator;
  197. if (oper == null)
  198. {
  199. result = leftValue;
  200. }
  201. else if (oper == "-" && assignment.Expression.Right == null)
  202. {
  203. int left;
  204. if (int.TryParse(leftValue, out left))
  205. {
  206. result = (-left).ToString();
  207. }
  208. else
  209. {
  210. throw new CodeErrorException(assignment.Line,
  211. CodeErrors.Assignment_InvalidArgumentsFormat);
  212. }
  213. }
  214. else if (oper == "+" || oper == "-" || oper == "*" || oper == "/"
  215. && assignment.Expression.Right != null)
  216. {
  217. if (rightValue == null)
  218. throw new CodeErrorException(assignment.Line, CodeErrors.Call_MacrosNotDefined);
  219. int left;
  220. int right;
  221. if (int.TryParse(leftValue, out left) &&
  222. int.TryParse(rightValue, out right))
  223. {
  224. int resultValue = 0;
  225. if (oper == "+")
  226. resultValue = left + right;
  227. else if (oper == "-")
  228. resultValue = left - right;
  229. else if (oper == "*")
  230. resultValue = left * right;
  231. else if (oper == "/")
  232. resultValue = left / right;
  233. result = resultValue.ToString();
  234. }
  235. else
  236. {
  237. throw new CodeErrorException(assignment.Line,
  238. CodeErrors.Assignment_InvalidArgumentsFormat);
  239. }
  240. }
  241. else if (oper == "." && assignment.Expression.Right != null)
  242. {
  243. if (rightValue == null)
  244. throw new CodeErrorException(assignment.Line, CodeErrors.Call_MacrosNotDefined);
  245. result = string.Concat(leftValue, rightValue);
  246. }
  247. else if (oper == "length" && assignment.Expression.Right == null)
  248. {
  249. result = leftValue.Length.ToString();
  250. }
  251. else if (oper == "take" || oper == "skip" && assignment.Expression.Right != null)
  252. {
  253. if (rightValue == null)
  254. throw new CodeErrorException(assignment.Line, CodeErrors.Call_MacrosNotDefined);
  255. int right;
  256. if (int.TryParse(rightValue, out right))
  257. {
  258. if (right < 0 || right > leftValue.Length)
  259. {
  260. throw new CodeErrorException(assignment.Line,
  261. CodeErrors.Operator_InvalidTakeOrSkipCount);
  262. }
  263. if (oper == "take")
  264. result = leftValue.Substring(0, right);
  265. else
  266. result = leftValue.Substring(right);
  267. }
  268. }
  269. else
  270. {
  271. throw new CodeErrorException(assignment.Line, CodeErrors.Assignment_InvalidOperator);
  272. }
  273. BrainMacros current = currentMacroses.FirstOrDefault(
  274. m => m.Name == assignment.Name);
  275. if (current != null)
  276. {
  277. current.Value = result;
  278. }
  279. else
  280. {
  281. currentMacroses.Add(new BrainMacros()
  282. {
  283. Name = assignment.Name,
  284. Value = result,
  285. });
  286. }
  287. }
  288. private string CalculateArgument(BrainArg argument, IEnumerable<BrainMacros> macroses)
  289. {
  290. if (argument == null)
  291. return null;
  292. BrainMacros macros = argument as BrainMacros;
  293. if (macros != null)
  294. {
  295. BrainMacros definition = macroses
  296. .LastOrDefault(m => m.Name == macros.Name);
  297. if (definition == null)
  298. {
  299. return null;
  300. }
  301. else
  302. {
  303. if (macros.Negative)
  304. {
  305. int value;
  306. if (!int.TryParse(definition.Value, out value))
  307. throw new CodeErrorException(argument.Line, CodeErrors.Macro_NotANumber);
  308. return (-value).ToString();
  309. }
  310. else
  311. {
  312. return definition.Value;
  313. }
  314. }
  315. }
  316. else
  317. {
  318. return argument.Value;
  319. }
  320. }
  321. }
  322. }