PageRenderTime 63ms CodeModel.GetById 35ms RepoModel.GetById 0ms app.codeStats 0ms

/SimpleBrainFuck.Core/Core/SimpleParser.cs

http://ironbrainfuck.codeplex.com
C# | 359 lines | 338 code | 21 blank | 0 comment | 67 complexity | 024d4ca7dde9460ef089ed22276488a3 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Text.RegularExpressions;
  6. using ZackFlame.SimpleBrainFuck.Core.Statments;
  7. using ZackFlame.SimpleBrainFuck.TextResources;
  8. namespace ZackFlame.SimpleBrainFuck.Core
  9. {
  10. public sealed class SimpleParser
  11. {
  12. private string Preprocess(string simpleCode)
  13. {
  14. if (simpleCode.Length == 0)
  15. return string.Empty;
  16. StringBuilder result = new StringBuilder();
  17. bool oneLineComment = false;
  18. bool literal = false;
  19. int i = 0;
  20. while (i < simpleCode.Length - 1)
  21. {
  22. char ch = simpleCode[i];
  23. if (!literal && ch == '/' && simpleCode[i + 1] == '/')
  24. {
  25. oneLineComment = true;
  26. i++;
  27. continue;
  28. }
  29. else if (oneLineComment)
  30. {
  31. if (ch == '\n')
  32. {
  33. oneLineComment = false;
  34. }
  35. else
  36. {
  37. i++;
  38. continue;
  39. }
  40. }
  41. else if (ch == '\"' && i != 0 && simpleCode[i - 1] != '\\')
  42. {
  43. literal = !literal;
  44. }
  45. result.Append(ch);
  46. i++;
  47. }
  48. if (!oneLineComment)
  49. result.Append(simpleCode[i]);
  50. return result.ToString();
  51. }
  52. private string[] GetTokens(string line)
  53. {
  54. if (line.Length == 0)
  55. return new string[] { };
  56. List<string> tokens = new List<string>();
  57. StringBuilder currentToken = new StringBuilder();
  58. const int nothing = 0, token = 1, literal = 2;
  59. string singleTokens = ".=";
  60. int state = nothing;
  61. int i = 0;
  62. while (i < line.Length)
  63. {
  64. char ch = line[i];
  65. bool last = (i == line.Length - 1);
  66. if ((state == token && (ch == '\"' || ch == '\\')) ||
  67. (state == literal && ch == '\\' && last) ||
  68. (state == nothing && ch == '\\'))
  69. {
  70. return null;
  71. }
  72. if (state == nothing)
  73. {
  74. if (ch == '\"')
  75. {
  76. state = literal;
  77. }
  78. else if (singleTokens.Contains(ch))
  79. {
  80. tokens.Add(ch.ToString());
  81. }
  82. else if (!char.IsWhiteSpace(ch))
  83. {
  84. state = token;
  85. currentToken.Append(ch);
  86. }
  87. }
  88. else if (state == token)
  89. {
  90. if (char.IsWhiteSpace(ch))
  91. {
  92. state = nothing;
  93. tokens.Add(currentToken.ToString());
  94. currentToken.Length = 0;
  95. }
  96. else if (singleTokens.Contains(ch))
  97. {
  98. state = nothing;
  99. tokens.Add(currentToken.ToString());
  100. tokens.Add(ch.ToString());
  101. currentToken.Length = 0;
  102. }
  103. else
  104. {
  105. currentToken.Append(ch);
  106. }
  107. }
  108. else // if state == literal
  109. {
  110. if (ch == '\"')
  111. {
  112. if (last)
  113. {
  114. state = nothing;
  115. tokens.Add(currentToken.ToString());
  116. }
  117. else
  118. {
  119. char next = line[i + 1];
  120. if (char.IsWhiteSpace(next) || singleTokens.Contains(next))
  121. {
  122. state = nothing;
  123. tokens.Add(currentToken.ToString());
  124. currentToken.Length = 0;
  125. }
  126. else
  127. {
  128. return null;
  129. }
  130. }
  131. }
  132. else if (ch == '\\')
  133. {
  134. char unescaped;
  135. if (!TryUnescapeChar(line[i + 1], out unescaped))
  136. return null;
  137. currentToken.Append(unescaped);
  138. i++;
  139. }
  140. else
  141. {
  142. currentToken.Append(ch);
  143. }
  144. }
  145. i++;
  146. }
  147. if (state == token)
  148. tokens.Add(currentToken.ToString());
  149. else if (state == literal)
  150. return null;
  151. return tokens.ToArray();
  152. }
  153. private bool TryUnescapeChar(char escaped, out char value)
  154. {
  155. switch (escaped)
  156. {
  157. case 'n':
  158. value = '\n';
  159. return true;
  160. case 't':
  161. value = '\t';
  162. return true;
  163. case '"':
  164. value = '\"';
  165. return true;
  166. case '\\':
  167. value = '\\';
  168. return true;
  169. case '0':
  170. value = '\0';
  171. return true;
  172. default:
  173. value = '\0';
  174. return false;
  175. }
  176. }
  177. public static BrainArg ParseParameter(string token)
  178. {
  179. if (token.Length > 1 && token[0] == '@')
  180. {
  181. var match = BrainMacros.NameRegex.Match(token.Substring(1));
  182. if (match.Success)
  183. {
  184. return new BrainMacros()
  185. {
  186. Name = match.Groups[2].Value,
  187. Negative = match.Groups[1].Success
  188. };
  189. }
  190. }
  191. return new BrainArg() { Value = token };
  192. }
  193. public IEnumerable<Statment> BuildSemanticTree(string simpleCode)
  194. {
  195. simpleCode = Preprocess(simpleCode.Replace(Environment.NewLine, "\n"));
  196. string[] lines = simpleCode.Split(new[] { '\n' },
  197. StringSplitOptions.None);
  198. var body = new LinkedList<Statment>();
  199. var bodyStack = new Stack<LinkedList<Statment>>();
  200. for (int i = 0; i < lines.Length; i++)
  201. {
  202. string[] tokens = GetTokens(lines[i]);
  203. if (tokens == null)
  204. throw new CodeErrorException(i, CodeErrors.Preprocess_InvalidLine);
  205. if (tokens.Length > 0)
  206. {
  207. string first = tokens[0];
  208. if (first == "end")
  209. {
  210. if (bodyStack.Count == 0)
  211. throw new CodeErrorException(i, CodeErrors.General_SuddenCloseOfStatment);
  212. body = bodyStack.Pop();
  213. }
  214. else if (first == "def")
  215. {
  216. if (tokens.Length < 2)
  217. throw new CodeErrorException(i, CodeErrors.Def_IncorrectDeclaration);
  218. BrainDef function = new BrainDef() { Name = tokens[1], Line = i };
  219. for (int j = 2; j < tokens.Length; j++)
  220. {
  221. function.MacrosArgs.Add(
  222. new BrainMacros() { Name = tokens[j] });
  223. }
  224. body.AddLast(function);
  225. bodyStack.Push(body);
  226. body = function.Statments;
  227. }
  228. else if (first == "if")
  229. {
  230. if (tokens.Length != 2)
  231. throw new CodeErrorException(i, CodeErrors.If_IncorrectDeclaration);
  232. BrainIf brainIf = new BrainIf() { Line = i };
  233. brainIf.Parameter = ParseParameter(tokens[1]);
  234. body.AddLast(brainIf);
  235. bodyStack.Push(body);
  236. body = brainIf.IfStatments;
  237. }
  238. else if (first == "else")
  239. {
  240. Statment statment = bodyStack.Peek().Last();
  241. BrainIf brainIf = statment as BrainIf;
  242. if (brainIf == null)
  243. {
  244. throw new CodeErrorException(i, CodeErrors.If_IfNotDeclarated);
  245. }
  246. else
  247. {
  248. body = brainIf.ElseStatments;
  249. }
  250. }
  251. else if (first.StartsWith("$"))
  252. {
  253. if (first.Length < 2)
  254. throw new CodeErrorException(i, CodeErrors.Call_IncorrectCall);
  255. string defName = first.Substring(1);
  256. BrainCall brainCall = new BrainCall() { DefName = defName, Line = i };
  257. for (int j = 1; j < tokens.Length; j++)
  258. {
  259. brainCall.CallArgs.Add(ParseParameter(tokens[j]));
  260. }
  261. body.AddLast(brainCall);
  262. }
  263. else if (first.StartsWith("@"))
  264. {
  265. if (first.Length < 2 ||
  266. tokens.Length < 3 || tokens.Length > 5 ||
  267. tokens[1] != "=")
  268. {
  269. throw new CodeErrorException(i, CodeErrors.Assignment_IncorrectAssignment);
  270. }
  271. BrainMacros newMacros = (BrainMacros)ParseParameter(tokens[0]);
  272. if (newMacros.Negative)
  273. throw new CodeErrorException(i, CodeErrors.Assignment_CantBeNegative);
  274. var brainExpression = new BrainMacrosExpression();
  275. switch (tokens.Length - 2) // tokens after '='
  276. {
  277. case 1:
  278. brainExpression.Left = ParseParameter(tokens[2]);
  279. break;
  280. case 2:
  281. brainExpression.Left = ParseParameter(tokens[3]);
  282. brainExpression.Operator = tokens[2];
  283. break;
  284. case 3:
  285. brainExpression.Left = ParseParameter(tokens[2]);
  286. brainExpression.Operator = tokens[3];
  287. brainExpression.Right = ParseParameter(tokens[4]);
  288. break;
  289. }
  290. body.AddLast(new BrainMacrosAssignment()
  291. {
  292. Name = newMacros.Name,
  293. Expression = brainExpression,
  294. Line = i,
  295. });
  296. }
  297. else
  298. {
  299. BrainPureCode code = new BrainPureCode()
  300. {
  301. Value = lines[i].Trim(' ', '\t'),
  302. Line = i
  303. };
  304. body.AddLast(code);
  305. }
  306. }
  307. }
  308. if (bodyStack.Count > 0)
  309. throw new CodeErrorException(lines.Length - 1, CodeErrors.General_UnclosedStatment);
  310. return body;
  311. }
  312. }
  313. }