Lexer.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  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. public IEnumerable<Token> GetTokens()
  61. {
  62. while (true)
  63. {
  64. GetChar();
  65. while (lastChar == ' ' || lastChar == '\t' || lastChar == '\r')
  66. GetChar();
  67. TokenMarker = sourceMarker;
  68. if (char.IsLetter(lastChar))
  69. {
  70. Identifer = lastChar.ToString();
  71. while (char.IsLetterOrDigit(GetChar()) || lastChar == '_')
  72. Identifer += lastChar;
  73. if (TokenDictionary.ContainsKey(Identifer))
  74. {
  75. yield return TokenDictionary[Identifer];
  76. continue;
  77. }
  78. switch (Identifer.ToUpper())
  79. {
  80. case "DRAWLINEFORM":
  81. foreach (Token t in ReturnAsLine(Token.DrawLineForm))
  82. yield return t;
  83. continue;
  84. case "PRINTFORML":
  85. foreach (Token t in ReturnAsLine(Token.PrintFormL))
  86. yield return t;
  87. continue;
  88. case "PRINT":
  89. foreach (Token t in ReturnAsLine(Token.Print))
  90. yield return t;
  91. continue;
  92. case "REM":
  93. while (lastChar != '\n')
  94. GetChar();
  95. continue;
  96. default:
  97. {
  98. yield return Token.Identifer;
  99. sourceMarker.Pointer--;
  100. continue;
  101. }
  102. }
  103. }
  104. if (char.IsDigit(lastChar))
  105. {
  106. string num = "";
  107. do
  108. {
  109. num += lastChar;
  110. }
  111. while (char.IsDigit(GetChar()) || lastChar == '.');
  112. if (!double.TryParse(num, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out var real))
  113. throw new Exception("ERROR while parsing number");
  114. Value = new Value(real);
  115. yield return Token.Value;
  116. sourceMarker.Pointer--;
  117. continue;
  118. }
  119. switch (lastChar)
  120. {
  121. case '\n': yield return Token.NewLine; continue;
  122. case '@': yield return Token.Function; continue;
  123. case '#': yield return Token.Global; continue;
  124. case ':': yield return Token.Colon; continue;
  125. case ';': yield return Token.Semicolon; continue;
  126. case ',': yield return Token.Comma; continue;
  127. case '=': yield return Token.Equal; continue;
  128. case '+': yield return Token.Plus; continue;
  129. case '-': yield return Token.Minus; continue;
  130. case '/': yield return Token.Slash; continue;
  131. case '*': yield return Token.Asterisk; continue;
  132. case '^': yield return Token.Caret; continue;
  133. case '(': yield return Token.LParen; continue;
  134. case ')': yield return Token.RParen; continue;
  135. case '\'':
  136. while (lastChar != '\n')
  137. GetChar();
  138. continue;
  139. case '<':
  140. GetChar();
  141. if (lastChar == '>')
  142. yield return Token.NotEqual;
  143. else if (lastChar == '=')
  144. yield return Token.LessEqual;
  145. else
  146. yield return Token.Less;
  147. continue;
  148. case '>':
  149. GetChar();
  150. if (lastChar == '=')
  151. yield return Token.MoreEqual;
  152. else
  153. yield return Token.More;
  154. continue;
  155. case '"':
  156. string str = "";
  157. while (GetChar() != '"')
  158. {
  159. if (lastChar == '\\')
  160. {
  161. switch (char.ToLower(GetChar()))
  162. {
  163. case 'n': str += '\n'; break;
  164. case 't': str += '\t'; break;
  165. case '\\': str += '\\'; break;
  166. case '"': str += '"'; break;
  167. }
  168. }
  169. else
  170. {
  171. str += lastChar;
  172. }
  173. }
  174. Value = new Value(str);
  175. yield return Token.Value;
  176. continue;
  177. case (char)0:
  178. yield return Token.EOF;
  179. break;
  180. default:
  181. yield return Token.Unkown;
  182. continue;
  183. }
  184. break;
  185. }
  186. }
  187. public IEnumerable<Token> ReturnAsLine(Token token)
  188. {
  189. StringBuilder bodyBuilder = new StringBuilder();
  190. while (lastChar != '\n')
  191. bodyBuilder.Append(GetChar());
  192. yield return token;
  193. Value = new Value(bodyBuilder.ToString());
  194. yield return Token.Value;
  195. yield return Token.NewLine;
  196. }
  197. }
  198. }