Website Summary Files Commits Branches @commit @diff @stats
path: root / JSImpl / src / Lexer.cpp
diff options
context:
space:
mode:
1 files changed, 198 insertions, 0 deletions
-rw-r--r--JSImpl/src/Lexer.cpp198
diff --git a/JSImpl/src/Lexer.cpp b/JSImpl/src/Lexer.cpp
new file mode 100644
index 0000000..797af26
--- /dev/null
+++ b/JSImpl/src/Lexer.cpp
@@ -0,0 +1,198 @@
+#include "Lexer.h"
+
+class Tokenizer
+{
+public:
+ Tokenizer(const char* code, const std::function<void()>& onError);
+ IPLVector<Token> Tokenize();
+
+private:
+ Token NextToken();
+
+ Token ProduceToken(TokenType type);
+ Token ProduceToken(TokenType type, IPLString lexeme);
+ double ParseNumber();
+ IPLString ParseString();
+ bool SkipWhiteSpaces();
+ bool SkipNewLine();
+ bool Match(const char c);
+
+ std::function<void()> OnError;
+ unsigned m_Line;
+ unsigned m_Current;
+ const char* m_Code;
+
+ IPLUnorderedMap<IPLString, TokenType> m_KeyWordsTable;
+};
+
+IPLVector<Token> Tokenize(const char* code, const std::function<void()>& onError)
+{
+ Tokenizer tokenizer(code, onError);
+
+ return tokenizer.Tokenize();
+}
+
+Tokenizer::Tokenizer(const char* code, const std::function<void()>& onError)
+ : OnError(onError)
+ , m_Line(0)
+ , m_Current(0)
+ , m_Code(code)
+{
+ m_KeyWordsTable["break"] = TokenType::Break;
+ m_KeyWordsTable["case"] = TokenType::Case;
+ m_KeyWordsTable["catch"] = TokenType::Catch;
+ m_KeyWordsTable["class"] = TokenType::Class;
+ m_KeyWordsTable["const"] = TokenType::Const;
+ m_KeyWordsTable["continue"] = TokenType::Continue;
+ m_KeyWordsTable["debugger"] = TokenType::Debugger;
+ m_KeyWordsTable["default"] = TokenType::Default;
+ m_KeyWordsTable["delete"] = TokenType::Delete;
+ m_KeyWordsTable["do"] = TokenType::Do;
+ m_KeyWordsTable["else"] = TokenType::Else;
+ m_KeyWordsTable["export"] = TokenType::Export;
+ m_KeyWordsTable["extends"] = TokenType::Extends;
+ m_KeyWordsTable["finally"] = TokenType::Finally;
+ m_KeyWordsTable["for"] = TokenType::For;
+ m_KeyWordsTable["function"] = TokenType::Function;
+ m_KeyWordsTable["if"] = TokenType::If;
+ m_KeyWordsTable["import"] = TokenType::Import;
+ m_KeyWordsTable["in"] = TokenType::In;
+ m_KeyWordsTable["instanceof"] = TokenType::Instanceof;
+ m_KeyWordsTable["new"] = TokenType::New;
+ m_KeyWordsTable["return"] = TokenType::Return;
+ m_KeyWordsTable["super"] = TokenType::Super;
+ m_KeyWordsTable["switch"] = TokenType::Switch;
+ m_KeyWordsTable["this"] = TokenType::This;
+ m_KeyWordsTable["throw"] = TokenType::Throw;
+ m_KeyWordsTable["try"] = TokenType::Try;
+ m_KeyWordsTable["typeof"] = TokenType::Typeof;
+ m_KeyWordsTable["var"] = TokenType::Var;
+ m_KeyWordsTable["void"] = TokenType::Void;
+ m_KeyWordsTable["while"] = TokenType::While;
+ m_KeyWordsTable["with"] = TokenType::With;
+ m_KeyWordsTable["yield"] = TokenType::Yield;
+}
+
+IPLVector<Token> Tokenizer::Tokenize()
+{
+ IPLVector<Token> result;
+ do
+ {
+ result.push_back(NextToken());
+ } while (result.back().Type != TokenType::Eof);
+
+ return result;
+}
+
+bool Tokenizer::SkipWhiteSpaces()
+{
+ const auto old = m_Current;
+ while (m_Code[m_Current] == ' ' || m_Code[m_Current] == '\t')
+ {
+ m_Current++;
+ }
+ return old != m_Current;
+}
+
+bool Tokenizer::SkipNewLine()
+{
+ const auto old = m_Current;
+ while (m_Code[m_Current] == '\n')
+ {
+ m_Current++;
+ m_Line++;
+ }
+ return old != m_Current;
+}
+
+bool Tokenizer::Match(const char c)
+{
+ if (c == m_Code[m_Current])
+ {
+ ++m_Current;
+ return true;
+ }
+ return false;
+}
+
+Token Tokenizer::ProduceToken(TokenType type)
+{
+ return { type, m_Line, "" };
+}
+
+Token Tokenizer::ProduceToken(TokenType type, IPLString lexeme)
+{
+ return{ type, m_Line, lexeme };
+}
+
+Token Tokenizer::NextToken()
+{
+ if (m_Code[m_Current] == '\0')
+ {
+ return Token{ TokenType::Eof, 0, "" };
+ }
+
+ while (SkipWhiteSpaces() || SkipNewLine());
+
+ // Single Char
+ switch (m_Code[m_Current])
+ {
+ case '(': m_Current++; return ProduceToken(TokenType::LeftParen);
+ case ')': m_Current++; return ProduceToken(TokenType::RightParen);
+ case '{': m_Current++; return ProduceToken(TokenType::LeftBrace);
+ case '}': m_Current++; return ProduceToken(TokenType::RightBrace);
+ case ',': m_Current++; return ProduceToken(TokenType::Comma);
+ case '.': m_Current++; return ProduceToken(TokenType::Dot);
+ case '-': m_Current++; return ProduceToken(TokenType::Minums);
+ case '+': m_Current++; return ProduceToken(TokenType::Plus);
+ case ';': m_Current++; return ProduceToken(TokenType::Semicolon);
+ case '*': m_Current++; return ProduceToken(TokenType::Star);
+ case '=': m_Current++; return Match('=') ? ProduceToken(TokenType::EqualEqual) : ProduceToken(TokenType::Equal);
+ case '!': m_Current++; return Match('=') ? ProduceToken(TokenType::BangEqual) : ProduceToken(TokenType::Bang);
+ case '>': m_Current++; return Match('=') ? ProduceToken(TokenType::GreaterEqual) : ProduceToken(TokenType::Greater);
+ case '<': m_Current++; return Match('=') ? ProduceToken(TokenType::LessEqual) : ProduceToken(TokenType::Less);
+ default:
+ break;
+ }
+ // Number
+ const char c = m_Code[m_Current];
+ if (c >= '0' && c < '9')
+ {
+ size_t parsedBytes = 0;
+ const double number = std::stod(m_Code + m_Current, &parsedBytes);
+ m_Current += (unsigned)parsedBytes;
+ return Token{ TokenType::Number, m_Line, "", number };
+ }
+
+ // String
+ if (c == '"')
+ {
+ auto start = m_Current;
+ m_Current++; // skip first '"'
+ while (m_Code[m_Current] != '"')
+ {
+ ++m_Current;
+ }
+ m_Current++; // skip second '"'
+ return Token{ TokenType::String, m_Line, IPLString(m_Code + start, m_Code + m_Current) };
+ }
+
+ // Key words
+ if (c >= 'a' && c <= 'z')
+ {
+ auto start = m_Current;
+ m_Current++;
+ while (m_Code[m_Current] >= 'a' && m_Code[m_Current] <= 'z')
+ {
+ ++m_Current;
+ }
+ auto key = IPLString(m_Code + start, m_Code + m_Current);
+ auto word = m_KeyWordsTable.find(key);
+ if (word != m_KeyWordsTable.end())
+ {
+ return Token{ word->second, m_Line, key };
+ }
+
+ }
+ return Token{ TokenType::Eof, m_Line, "" };
+} \ No newline at end of file