Lexer.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. namespace NTERA.Interpreter
  5. {
  6. public class Lexer
  7. {
  8. private readonly string source;
  9. private Marker sourceMarker;
  10. private char lastChar;
  11. public Marker TokenMarker { get; set; }
  12. public string Identifer { get; set; }
  13. public Value Value { get; set; }
  14. public Lexer(string input)
  15. {
  16. source = input;
  17. sourceMarker = new Marker(-1, 1, 0);
  18. }
  19. public void GoTo(Marker marker)
  20. {
  21. sourceMarker = marker;
  22. }
  23. char GetChar()
  24. {
  25. sourceMarker.Column++;
  26. sourceMarker.Pointer++;
  27. if (sourceMarker.Pointer >= source.Length)
  28. return lastChar = (char)0;
  29. if ((lastChar = source[sourceMarker.Pointer]) == '\n')
  30. {
  31. sourceMarker.Column = 1;
  32. sourceMarker.Line++;
  33. }
  34. return lastChar;
  35. }
  36. private readonly Dictionary<string, Token> TokenDictionary = new Dictionary<string, Token>(StringComparer.InvariantCultureIgnoreCase)
  37. {
  38. //["PRINT"] = Token.Print,
  39. //["PRINTL"] = Token.PrintL,
  40. ["DRAWLINE"] = Token.DrawLine,
  41. //["DRAWLINEFORM"] = Token.DrawLineForm,
  42. ["DIM"] = Token.Dim,
  43. ["CONST"] = Token.Const,
  44. ["IF"] = Token.If,
  45. ["ENDIF"] = Token.EndIf,
  46. ["THEN"] = Token.Then,
  47. ["ELSE"] = Token.Else,
  48. ["FOR"] = Token.For,
  49. ["TO"] = Token.To,
  50. ["NEXT"] = Token.Next,
  51. ["INPUT"] = Token.Input,
  52. ["LET"] = Token.Let,
  53. ["GOSUB"] = Token.Gosub,
  54. ["RETURN"] = Token.Return,
  55. ["END"] = Token.End,
  56. ["OR"] = Token.Or,
  57. ["AND"] = Token.And,
  58. ["TIMES"] = Token.Times,
  59. };
  60. private readonly Dictionary<string, Token> TokenLineDictionary = new Dictionary<string, Token>(StringComparer.InvariantCultureIgnoreCase)
  61. {
  62. ["PRINT"] = Token.Print,
  63. ["PRINTL"] = Token.PrintL,
  64. ["DRAWLINEFORM"] = Token.DrawLineForm,
  65. ["PRINTFORML"] = Token.PrintFormL,
  66. ["PRINT_IMG"] = Token.PrintImg,
  67. ["PRINTBUTTON"] = Token.PrintButton,
  68. };
  69. public IEnumerable<Token> GetTokens()
  70. {
  71. while (true)
  72. {
  73. GetChar();
  74. while (lastChar == ' ' || lastChar == '\t' || lastChar == '\r')
  75. GetChar();
  76. TokenMarker = sourceMarker;
  77. if (char.IsLetter(lastChar))
  78. {
  79. Identifer = lastChar.ToString();
  80. while (char.IsLetterOrDigit(GetChar()) || lastChar == '_')
  81. Identifer += lastChar;
  82. if (TokenDictionary.TryGetValue(Identifer, out Token token))
  83. {
  84. yield return token;
  85. continue;
  86. }
  87. if (TokenLineDictionary.TryGetValue(Identifer, out token))
  88. {
  89. foreach (Token t in ReturnAsLine(token))
  90. yield return t;
  91. continue;
  92. }
  93. switch (Identifer.ToUpper())
  94. {
  95. case "REM":
  96. while (lastChar != '\n')
  97. GetChar();
  98. continue;
  99. default:
  100. {
  101. yield return Token.Identifer;
  102. sourceMarker.Pointer--;
  103. continue;
  104. }
  105. }
  106. }
  107. if (char.IsDigit(lastChar))
  108. {
  109. string num = "";
  110. do
  111. {
  112. num += lastChar;
  113. }
  114. while (char.IsDigit(GetChar()) || lastChar == '.');
  115. if (!double.TryParse(num, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out var real))
  116. throw new Exception("ERROR while parsing number");
  117. Value = new Value(real);
  118. yield return Token.Value;
  119. sourceMarker.Pointer--;
  120. continue;
  121. }
  122. switch (lastChar)
  123. {
  124. case '\n': yield return Token.NewLine; continue;
  125. case '@': yield return Token.Function; continue;
  126. case '#': yield return Token.Global; continue;
  127. case ':': yield return Token.Colon; continue;
  128. case ';': yield return Token.Semicolon; continue;
  129. case ',': yield return Token.Comma; continue;
  130. case '=': yield return Token.Equal; continue;
  131. case '+': yield return Token.Plus; continue;
  132. case '-': yield return Token.Minus; continue;
  133. case '/': yield return Token.Slash; continue;
  134. case '*': yield return Token.Asterisk; continue;
  135. case '^': yield return Token.Caret; continue;
  136. case '(': yield return Token.LParen; continue;
  137. case ')': yield return Token.RParen; continue;
  138. case '\'':
  139. while (lastChar != '\n')
  140. GetChar();
  141. continue;
  142. case '<':
  143. GetChar();
  144. if (lastChar == '>')
  145. yield return Token.NotEqual;
  146. else if (lastChar == '=')
  147. yield return Token.LessEqual;
  148. else
  149. yield return Token.Less;
  150. continue;
  151. case '>':
  152. GetChar();
  153. if (lastChar == '=')
  154. yield return Token.MoreEqual;
  155. else
  156. yield return Token.More;
  157. continue;
  158. case '"':
  159. string str = "";
  160. while (GetChar() != '"')
  161. {
  162. if (lastChar == '\\')
  163. {
  164. switch (char.ToLower(GetChar()))
  165. {
  166. case 'n': str += '\n'; break;
  167. case 't': str += '\t'; break;
  168. case '\\': str += '\\'; break;
  169. case '"': str += '"'; break;
  170. }
  171. }
  172. else
  173. {
  174. str += lastChar;
  175. }
  176. }
  177. Value = new Value(str);
  178. yield return Token.Value;
  179. continue;
  180. case (char)0:
  181. yield return Token.EOF;
  182. break;
  183. default:
  184. yield return Token.Unkown;
  185. continue;
  186. }
  187. break;
  188. }
  189. }
  190. public IEnumerable<Token> ReturnAsLine(Token token)
  191. {
  192. StringBuilder bodyBuilder = new StringBuilder();
  193. while (lastChar != '\n')
  194. bodyBuilder.Append(GetChar());
  195. yield return token;
  196. Value = new Value(bodyBuilder.ToString().TrimEnd());
  197. yield return Token.Value;
  198. yield return Token.NewLine;
  199. }
  200. }
  201. }