parser.h 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. #pragma once
  2. #include <string_view>
  3. #include <cstring>
  4. #include <queue>
  5. #include "obj.h"
  6. typedef uint8_t _TokenType;
  7. constexpr const char* __TOKENS[] = {
  8. "@error", "@eof", "@eol", "@sof",
  9. ".", ",", ":", ";", "#", "(", ")", "[", "]", "{", "}", "%",
  10. "+", "-", "*", "/", "//", "**", "=", ">", "<",
  11. "==", "!=", ">=", "<=",
  12. "+=", "-=", "*=", "/=", "//=",
  13. /** KW_BEGIN **/
  14. "class", "import", "as", "def", "lambda", "pass", "del",
  15. "None", "in", "is", "and", "or", "not", "True", "False",
  16. "while", "for", "if", "elif", "else", "break", "continue", "return", "assert", "raise",
  17. /** KW_END **/
  18. "is not", "not in",
  19. "@id", "@num", "@str",
  20. "@indent", "@dedent"
  21. };
  22. const _TokenType __TOKENS_LEN = sizeof(__TOKENS) / sizeof(__TOKENS[0]);
  23. constexpr _TokenType __tokenIndex(const char* token) {
  24. for(int k=0; k<__TOKENS_LEN; k++){
  25. const char* i = __TOKENS[k];
  26. const char* j = token;
  27. while(*i && *j && *i == *j){
  28. i++; j++;
  29. }
  30. if(*i == *j) return k;
  31. }
  32. return 0;
  33. }
  34. #define TK(s) __tokenIndex(s)
  35. #define TK_STR(t) __TOKENS[t]
  36. const _TokenType __KW_BEGIN = __tokenIndex("class");
  37. const _TokenType __KW_END = __tokenIndex("raise");
  38. const std::unordered_map<std::string_view, _TokenType> __KW_MAP = [](){
  39. std::unordered_map<std::string_view, _TokenType> map;
  40. for(int k=__KW_BEGIN; k<=__KW_END; k++) map[__TOKENS[k]] = k;
  41. return map;
  42. }();
  43. struct Token{
  44. _TokenType type;
  45. const char* start; //< Begining of the token in the source.
  46. int length; //< Number of chars of the token.
  47. int line; //< Line number of the token (1 based).
  48. PyVar value; //< Literal value of the token.
  49. const _Str str() const {
  50. return _Str(start, length);
  51. }
  52. };
  53. enum Precedence {
  54. PREC_NONE,
  55. PREC_LOWEST,
  56. PREC_LOGICAL_OR, // or
  57. PREC_LOGICAL_AND, // and
  58. PREC_EQUALITY, // == !=
  59. PREC_TEST, // in is
  60. PREC_COMPARISION, // < > <= >=
  61. PREC_TERM, // + -
  62. PREC_FACTOR, // * / %
  63. PREC_UNARY, // - not
  64. PREC_EXPONENT, // **
  65. PREC_CALL, // ()
  66. PREC_SUBSCRIPT, // []
  67. PREC_ATTRIB, // .index
  68. PREC_PRIMARY,
  69. };
  70. // The context of the parsing phase for the compiler.
  71. struct Parser {
  72. const char* source; //< Currently compiled source.
  73. const char* token_start; //< Start of the currently parsed token.
  74. const char* current_char; //< Current char position in the source.
  75. const char* line_start; //< Start of the current line.
  76. int current_line = 1;
  77. Token previous, current;
  78. std::queue<Token> nexts;
  79. std::stack<int> indents;
  80. Token nextToken(){
  81. if(nexts.empty()) return makeErrToken();
  82. Token t = nexts.front();
  83. if(t.type == TK("@eof") && indents.size()>1){
  84. nexts.pop();
  85. indents.pop();
  86. return Token{TK("@dedent"), token_start, 0, current_line};
  87. }
  88. nexts.pop();
  89. return t;
  90. }
  91. char peekChar() {
  92. return *current_char;
  93. }
  94. char peekNextChar() {
  95. if (peekChar() == '\0') return '\0';
  96. return *(current_char + 1);
  97. }
  98. int eatSpaces(){
  99. int count = 0;
  100. while (true) {
  101. switch (peekChar()) {
  102. case ' ': count++; break;
  103. case '\t': count+=4; break;
  104. default: return count;
  105. }
  106. eatChar();
  107. }
  108. }
  109. bool eatIndentation(){
  110. int spaces = eatSpaces();
  111. // https://docs.python.org/3/reference/lexical_analysis.html#indentation
  112. if(spaces > indents.top()){
  113. indents.push(spaces);
  114. nexts.push(Token{TK("@indent"), token_start, 0, current_line});
  115. } else if(spaces < indents.top()){
  116. while(spaces < indents.top()){
  117. indents.pop();
  118. nexts.push(Token{TK("@dedent"), token_start, 0, current_line});
  119. }
  120. if(spaces != indents.top()){
  121. return false;
  122. }
  123. }
  124. return true;
  125. }
  126. char eatChar() {
  127. char c = peekChar();
  128. if(c == '\n') throw std::runtime_error("eatChar() cannot consume a newline");
  129. current_char++;
  130. return c;
  131. }
  132. char eatCharIncludeNewLine() {
  133. char c = peekChar();
  134. current_char++;
  135. if (c == '\n'){
  136. current_line++;
  137. line_start = current_char;
  138. }
  139. return c;
  140. }
  141. void eatName() {
  142. char c = peekChar();
  143. while (isalpha(c) || c=='_' || isdigit(c)) {
  144. eatChar();
  145. c = peekChar();
  146. }
  147. const char* name_start = token_start;
  148. int length = (int)(current_char - name_start);
  149. std::string_view name(name_start, length);
  150. if(__KW_MAP.count(name)){
  151. if(name == "not"){
  152. if(strncmp(current_char, " in", 3) == 0){
  153. current_char += 3;
  154. setNextToken(TK("not in"));
  155. return;
  156. }
  157. }else if(name == "is"){
  158. if(strncmp(current_char, " not", 4) == 0){
  159. current_char += 4;
  160. setNextToken(TK("is not"));
  161. return;
  162. }
  163. }
  164. setNextToken(__KW_MAP.at(name));
  165. } else {
  166. setNextToken(TK("@id"));
  167. }
  168. }
  169. void skipLineComment() {
  170. char c;
  171. while ((c = peekChar()) != '\0') {
  172. if (c == '\n') return;
  173. eatChar();
  174. }
  175. }
  176. // If the current char is [c] consume it and advance char by 1 and returns
  177. // true otherwise returns false.
  178. bool matchChar(char c) {
  179. if (peekChar() != c) return false;
  180. eatCharIncludeNewLine();
  181. return true;
  182. }
  183. // Returns an error token from the current position for reporting error.
  184. Token makeErrToken() {
  185. return Token{TK("@error"), token_start, (int)(current_char - token_start), current_line};
  186. }
  187. // Initialize the next token as the type.
  188. void setNextToken(_TokenType type, PyVar value=nullptr) {
  189. nexts.push( Token{
  190. type,
  191. token_start,
  192. (int)(current_char - token_start),
  193. current_line - ((type == TK("@eol")) ? 1 : 0),
  194. value
  195. });
  196. }
  197. void setNextTwoCharToken(char c, _TokenType one, _TokenType two) {
  198. if (matchChar(c)) setNextToken(two);
  199. else setNextToken(one);
  200. }
  201. Parser(const char* source) {
  202. this->source = source;
  203. this->token_start = source;
  204. this->current_char = source;
  205. this->line_start = source;
  206. this->nexts.push(Token{TK("@sof"), token_start, 0, current_line});
  207. this->indents.push(0);
  208. }
  209. };