Website Summary Files Commits Branches @commit @diff @stats
diff options
context:
space:
mode:
Commit93cd8cac8a616ae13bd42dcbee0bb9da278eb49d (patch)
Author
 Ralitsa Velikova <rherince@gmail.com>
2018-11-07 14:26:16 +0200
Committer
 dtrendafilov <trendafilov.dn@gmail.com>
2018-11-07 14:26:16 +0200
Filesce8b306af9c36d6443fe2fc34526223e44c804cf
IPL-93cd8cac8a616ae13bd42dcbee0bb9da278eb49d.tar
IPL-93cd8cac8a616ae13bd42dcbee0bb9da278eb49d.tar.gz
IPL-93cd8cac8a616ae13bd42dcbee0bb9da278eb49d.zip
Parent47e8c6f331525f01f150ba80a3557a18618b0cf5 (diff)
Solution to first homework (#25)
* Add token types enum for Lexer * Local test if I've correctly switched from https to ssh * Adds initial Lexer logic; Reuses JSImpl's Lexer logic; Still needs refactoring; * Adds README.md * Adds README.md * Adds tokenization of strings and a few operators * Adds some operators * Adds highlighter and sample for future testing * Adds runes & fixes double whitespace bug * API fix, premake, solution * Update README.md * Delete HL.sln * Delete HL.vcxproj
diff --git a/homework/81013/README.md b/homework/81013/README.md
new file mode 100644
index 0000000..b05d85f
--- /dev/null
+++ b/homework/81013/README.md
@@ -0,0 +1 @@
+Some Homeworks might live here.
diff --git a/homework/81013/go2html/CommonTypes.h b/homework/81013/go2html/CommonTypes.h
new file mode 100644
index 0000000..f279e34
--- /dev/null
+++ b/homework/81013/go2html/CommonTypes.h
@@ -0,0 +1,37 @@
+#pragma once
+#include <string>
+#include <vector>
+#include <unordered_map>
+#include <memory>
+
+template<typename T>
+using IPLVector = std::vector<T>;
+using IPLString = std::string;
+
+template <typename Key, typename T>
+using IPLUnorderedMap = std::unordered_map<Key, T>;
+
+template <typename T>
+using IPLSharedPtr = std::shared_ptr<T>;
+
+template <typename T, class... Args>
+inline IPLSharedPtr<T> IPLMakeSharePtr(Args&&... args) {
+ return std::make_shared<T, Args...>(args...);
+};
+
+template< typename T>
+using IPLEnableShared = std::enable_shared_from_this<T>;
+
+struct IPLError
+{
+ unsigned Row;
+ unsigned Column;
+ IPLString File;
+ IPLString What;
+};
+
+struct IPLResult
+{
+ bool IsSuccessful;
+ IPLError Error;
+}; \ No newline at end of file
diff --git a/homework/81013/go2html/Highlighter.cpp b/homework/81013/go2html/Highlighter.cpp
new file mode 100644
index 0000000..8295610
--- /dev/null
+++ b/homework/81013/go2html/Highlighter.cpp
@@ -0,0 +1,219 @@
+#include <iostream>
+#include <fstream>
+
+#include "CommonTypes.h"
+#include "Lexer.h"
+#include "Highlighter.h"
+
+#define KEYWORD_COLOR "#ad42f4"
+#define IDENTIFIER_COLOR "#41c4f4"
+#define OPERATOR_COLOR "#f49541"
+#define NUMBER_COLOR "#bab103"
+#define STRING_COLOR "#ff11bf"
+#define RUNE_COLOR "#5abc00"
+#define CONST_COLOR "#f94040"
+#define COMMENT_COLOR "#0b8700"
+
+namespace
+{
+ void PrintToken(
+ const Token& token,
+ const IPLString& className,
+ std::ostream& out)
+ {
+ switch (token.Type)
+ {
+ case TokenType::Whitespace:
+ out << token.Lexeme;
+ break;
+ case TokenType::Float:
+ case TokenType::DecimalInteger:
+ case TokenType::ImaginaryNum:
+ out << "<span class=\"" << className << "\">" << token.Number << "</span>";
+ default:
+ out << "<span class=\"" << className << "\">" << token.Lexeme << "</span>";
+ break;
+ }
+ }
+}
+
+IPLResult Highlight(std::ifstream& infile, std::ofstream& out)
+{
+ out <<
+ "<html>\n"
+ "<head>\n"
+ "<style>\n"
+ ".keyword {\n"
+ "color: " << KEYWORD_COLOR << ";\n"
+ "}\n"
+ "\n"
+ ".identifier {\n"
+ "color: " << IDENTIFIER_COLOR << ";\n"
+ "}\n"
+ "\n"
+ ".operator {\n"
+ "color: " << OPERATOR_COLOR << ";\n"
+ "}\n"
+ "\n"
+ ".number {\n"
+ "color: " << NUMBER_COLOR << ";\n"
+ "}\n"
+ "\n"
+ ".string {\n"
+ "color: " << STRING_COLOR << ";\n"
+ "}\n"
+ "\n"
+ ".rune {\n"
+ "color: " << RUNE_COLOR << ";\n"
+ "}\n"
+ "\n"
+ ".const {\n"
+ "color: " << CONST_COLOR << ";\n"
+ "}\n"
+ "\n"
+ ".comment {\n"
+ "color: " << COMMENT_COLOR << ";\n"
+ "}\n"
+ "</style>\n"
+ "</head>\n"
+ "<body><pre>\n" << std::endl;
+
+ std::string line;
+ std::vector<Token> tokens;
+
+ while (std::getline(infile, line))
+ {
+ IPLResult result = Tokenize(line.c_str(), tokens, {true, true});
+ if (!result.IsSuccessful)
+ {
+ return result;
+ }
+
+ for (auto& token : tokens)
+ {
+ switch (token.Type)
+ {
+ case TokenType::Break:
+ case TokenType::Default:
+ case TokenType::Func:
+ case TokenType::Interface:
+ case TokenType::Select:
+ case TokenType::Case:
+ case TokenType::Defer:
+ case TokenType::Go:
+ case TokenType::Map:
+ case TokenType::Struct:
+ case TokenType::Chan:
+ case TokenType::Else:
+ case TokenType::Goto:
+ case TokenType::Package:
+ case TokenType::Switch:
+ case TokenType::Const:
+ case TokenType::Fallthrough:
+ case TokenType::If:
+ case TokenType::Range:
+ case TokenType::Type:
+ case TokenType::Continue:
+ case TokenType::For:
+ case TokenType::Import:
+ case TokenType::Return:
+ case TokenType::Var:
+
+ PrintToken(token, "keyword", out);
+ break;
+
+ case TokenType::Plus:
+ case TokenType::Minus:
+ case TokenType::Star:
+ case TokenType::Division:
+ case TokenType::Modulo:
+
+ case TokenType::NOT:
+ case TokenType::BitwiseAND:
+ case TokenType::BitwiseOR:
+ case TokenType::BitwiseXOR:
+ case TokenType::BitClear:
+ case TokenType::ShiftLeft:
+ case TokenType::ShiftRight:
+
+ case TokenType::PlusEqual:
+ case TokenType::MinusEqual:
+ case TokenType::MultEqual:
+ case TokenType::DivEqual:
+ case TokenType::ModuloEqual:
+
+ case TokenType::ConditionalAND:
+ case TokenType::ConditionalOR:
+ case TokenType::ReceiveOperator:
+ case TokenType::PlusPlus:
+ case TokenType::MinusMinus:
+
+ case TokenType::Equal:
+ case TokenType::Less:
+ case TokenType::Greater:
+
+ case TokenType::NOTEqual:
+ case TokenType::LessEqual:
+ case TokenType::GreaterEqual:
+
+ case TokenType::Assignment:
+ case TokenType::ShortAssignment:
+
+ case TokenType::Variadic:
+
+ case TokenType::LeftParen:
+ case TokenType::RightParen:
+ case TokenType::LeftSquareBracket:
+ case TokenType::RightSquareBracket:
+ case TokenType::LeftBrace:
+ case TokenType::RightBrace:
+
+ case TokenType::Comma:
+ case TokenType::Dot:
+
+ case TokenType::Semicolon:
+ case TokenType::Colon:
+
+ PrintToken(token, "operator", out);
+ break;
+
+ case TokenType::Float:
+ case TokenType::DecimalInteger:
+ case TokenType::ImaginaryNum:
+
+ PrintToken(token, "number", out);
+ break;
+
+ case TokenType::String:
+
+ PrintToken(token, "string", out);
+ break;
+
+ case TokenType::True:
+ case TokenType::False:
+ case TokenType::Nil:
+
+ PrintToken(token, "const", out);
+ break;
+
+ case TokenType::Comment:
+
+ PrintToken(token, "comment", out);
+ break;
+
+ default:
+ PrintToken(token, "", out);
+ break;
+ }
+ }
+
+ out << std::endl;
+ tokens.clear();
+ }
+
+ out <<
+ "</pre></body>\n"
+ "</html>" << std::endl;
+
+ return {true, IPLError() };
+} \ No newline at end of file
diff --git a/homework/81013/go2html/Highlighter.h b/homework/81013/go2html/Highlighter.h
new file mode 100644
index 0000000..a40f5d0
--- /dev/null
+++ b/homework/81013/go2html/Highlighter.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include <fstream>
+
+#include "CommonTypes.h"
+#include "Lexer.h"
+
+IPLResult Highlight(std::ifstream& in, std::ofstream& out);
diff --git a/homework/81013/go2html/Lexer.cpp b/homework/81013/go2html/Lexer.cpp
new file mode 100644
index 0000000..fb53f89
--- /dev/null
+++ b/homework/81013/go2html/Lexer.cpp
@@ -0,0 +1,668 @@
+#include "Lexer.h"
+#include <utility>
+
+namespace
+{
+ inline bool IsUpperCase(char c)
+ {
+ return c >= 'A' && c <= 'Z';
+ }
+
+ inline bool IsLowerCase(char c)
+ {
+ return c >= 'a' && c <= 'z';
+ }
+
+ inline bool IsDigit(char c)
+ {
+ return c >= '0' && c <= '9';
+ }
+
+ inline bool IsValidIdentifierStartingChar(char c)
+ {
+ return IsUpperCase(c) || IsLowerCase(c) || c == '_';
+ }
+
+ inline bool IsValidIdentifierChar(char c)
+ {
+ return IsValidIdentifierStartingChar(c) || IsDigit(c);
+ }
+
+ inline bool IsStringBound(char c)
+ {
+ return c == '\'' || c == '"' || c == '`';
+ }
+
+ inline bool IsWhiteSpace(char c)
+ {
+ return c == ' ' || c == '\t';
+ }
+
+ inline bool IsNewLine(char c)
+ {
+ return c == '\n';
+ }
+
+ inline bool IsEnd(char c)
+ {
+ return c == '\0';
+ }
+}
+
+struct Keyword
+{
+ TokenType Type;
+ IPLString Content;
+};
+
+using Identifier = Keyword;
+
+enum class State : unsigned char
+{
+ Success,
+ Fail,
+ Error
+};
+
+#define RETURN_SUCCESS(ret) m_GenerationState = State::Success; return ret;
+#define RETURN_FAIL(ret) m_GenerationState = State::Fail; return ret;
+#define RETURN_ERROR(ret) m_GenerationState = State::Error; return ret;
+
+class Tokenizer
+{
+public:
+ Tokenizer(const char* code, const LexerSettings& settings);
+ IPLResult Tokenize(IPLVector<Token>& tokens);
+
+private:
+ Token NextToken();
+
+ inline Token ProduceSkipToken();
+ inline Token ProduceToken(TokenType type);
+ inline Token ProduceToken(TokenType type, IPLString lexeme, double number);
+ inline Token ProduceErrorToken();
+
+ IPLString ParseComment();
+ IPLString ParseWhitespaces();
+ double ParseNumber();
+ IPLString ParseString();
+ Keyword ParseKeyword();
+ Identifier ParseIdentifier();
+
+ inline bool IsStateSuccess() const;
+ inline bool IsStateError() const;
+
+ void SetError(const IPLString& what);
+
+ bool SkipWhiteSpaces();
+ bool SkipNewLine();
+ bool Match(const char c);
+
+ inline void NextSymbol();
+ inline void PreviousSymbol();
+ inline void NextLine();
+
+ unsigned m_Line;
+ unsigned m_Column;
+ unsigned m_Current;
+ const char* m_Code;
+
+ IPLError m_Error;
+ State m_GenerationState;
+
+ LexerSettings m_Settings;
+ bool m_SkipNextToken;
+
+ IPLUnorderedMap<IPLString, TokenType> m_KeyWordsTable;
+};
+
+IPLResult Tokenize(const char* code, IPLVector<Token>& tokens, const LexerSettings& settings)
+{
+ Tokenizer tokenizer(code, settings);
+
+ return tokenizer.Tokenize(tokens);
+}
+
+IPLResult Tokenize(const char* code, IPLVector<Token>& tokens)
+{
+ return Tokenize(code, tokens, { false, false });
+}
+
+Tokenizer::Tokenizer(const char* code, const LexerSettings& settings)
+ : m_Line(0)
+ , m_Column(0)
+ , m_Current(0)
+ , m_Code(code)
+ , m_Error()
+ , m_GenerationState(State::Success)
+ , m_Settings(settings)
+ , m_SkipNextToken(false)
+{
+ m_KeyWordsTable["break"] = TokenType::Break;
+ m_KeyWordsTable["default"] = TokenType::Default;
+ m_KeyWordsTable["func"] = TokenType::Func;
+ m_KeyWordsTable["interface"] = TokenType::Interface;
+ m_KeyWordsTable["select"] = TokenType::Select;
+ m_KeyWordsTable["case"] = TokenType::Case;
+ m_KeyWordsTable["defer"] = TokenType::Defer;
+ m_KeyWordsTable["go"] = TokenType::Go;
+ m_KeyWordsTable["map"] = TokenType::Map;
+ m_KeyWordsTable["struct"] = TokenType::Struct;
+ m_KeyWordsTable["chan"] = TokenType::Chan;
+ m_KeyWordsTable["else"] = TokenType::Else;
+ m_KeyWordsTable["goto"] = TokenType::Goto;
+ m_KeyWordsTable["package"] = TokenType::Package;
+ m_KeyWordsTable["switch"] = TokenType::Switch;
+ m_KeyWordsTable["const"] = TokenType::Const;
+ m_KeyWordsTable["fallthrough"] = TokenType::Fallthrough;
+ m_KeyWordsTable["if"] = TokenType::If;
+ m_KeyWordsTable["range"] = TokenType::Range;
+ m_KeyWordsTable["type"] = TokenType::Type;
+ m_KeyWordsTable["continue"] = TokenType::Continue;
+ m_KeyWordsTable["for"] = TokenType::For;
+ m_KeyWordsTable["import"] = TokenType::Import;
+ m_KeyWordsTable["return"] = TokenType::Return;
+ m_KeyWordsTable["var"] = TokenType::Var;
+
+ m_KeyWordsTable["true"] = TokenType::True;
+ m_KeyWordsTable["false"] = TokenType::False;
+
+ m_KeyWordsTable["nil"] = TokenType::Nil;
+}
+
+IPLResult Tokenizer::Tokenize(IPLVector<Token>& tokens)
+{
+ do
+ {
+ Token token;
+ do
+ {
+ token = NextToken();
+ } while (m_SkipNextToken);
+
+ if (m_GenerationState == State::Error)
+ {
+ return IPLResult{ false, IPLError(m_Error) };
+ }
+
+ tokens.emplace_back(token);
+ } while (tokens.back().Type != TokenType::Eof);
+
+ return IPLResult{ true, IPLError() };
+}
+
+bool Tokenizer::SkipWhiteSpaces()
+{
+ const auto old = m_Current;
+ while (IsWhiteSpace(m_Code[m_Current]))
+ {
+ NextSymbol();
+ }
+ return old != m_Current;
+}
+
+bool Tokenizer::SkipNewLine()
+{
+ const auto old = m_Current;
+ while (IsNewLine(m_Code[m_Current]))
+ {
+ NextLine();
+ }
+ return old != m_Current;
+}
+
+bool Tokenizer::Match(const char c)
+{
+ if (c == m_Code[m_Current])
+ {
+ if (!IsNewLine(m_Code[m_Current]))
+ {
+ NextSymbol();
+ }
+ else
+ {
+ NextLine();
+ }
+
+ return true;
+ }
+ return false;
+}
+
+inline void Tokenizer::NextSymbol()
+{
+ ++m_Current;
+ ++m_Column;
+}
+
+inline void Tokenizer::PreviousSymbol()
+{
+ --m_Current;
+ --m_Column;
+}
+
+inline void Tokenizer::NextLine()
+{
+ ++m_Current;
+ ++m_Line;
+ m_Column = 0;
+}
+
+inline Token Tokenizer::ProduceToken(TokenType type, IPLString lexeme, double number = 0.0)
+{
+ return Token{ type, m_Line, std::move(lexeme), number };
+}
+
+inline Token Tokenizer::ProduceSkipToken()
+{
+ return ProduceToken(TokenType::Invalid, "");
+}
+
+inline Token Tokenizer::ProduceToken(TokenType type)
+{
+ switch (type)
+ {
+ case TokenType::Plus:
+ return ProduceToken(type, "+");
+ case TokenType::Minus:
+ return ProduceToken(type, "-");
+ case TokenType::Star:
+ return ProduceToken(type, "*");
+ case TokenType::Division:
+ return ProduceToken(type, "/");
+ case TokenType::Modulo:
+ return ProduceToken(type, "%");
+
+ case TokenType::PlusEqual:
+ return ProduceToken(type, "+=");
+ case TokenType::MinusEqual:
+ return ProduceToken(type, "-=");
+ case TokenType::MultEqual:
+ return ProduceToken(type, "*=");
+ case TokenType::DivEqual:
+ return ProduceToken(type, "/=");
+ case TokenType::ModuloEqual:
+ return ProduceToken(type, "%=");
+
+ case TokenType::NOT:
+ return ProduceToken(type, "!");
+ case TokenType::BitwiseAND:
+ return ProduceToken(type, "&");
+ case TokenType::BitwiseOR:
+ return ProduceToken(type, "|");
+ case TokenType::BitwiseXOR:
+ return ProduceToken(type, "^");
+ case TokenType::BitClear:
+ return ProduceToken(type, "^&");
+ case TokenType::ShiftLeft:
+ return ProduceToken(type, "<<");
+ case TokenType::ShiftRight:
+ return ProduceToken(type, ">>");
+
+ case TokenType::ConditionalAND:
+ return ProduceToken(type, "&&");
+ case TokenType::ConditionalOR:
+ return ProduceToken(type, "||");
+
+ case TokenType::ReceiveOperator:
+ return ProduceToken(type, "<-");
+
+ case TokenType::PlusPlus:
+ return ProduceToken(type, "++");
+ case TokenType::MinusMinus:
+ return ProduceToken(type, "--");
+
+ case TokenType::Equal:
+ return ProduceToken(type, "==");
+ case TokenType::Less:
+ return ProduceToken(type, "<");
+ case TokenType::Greater:
+ return ProduceToken(type, ">");
+
+ case TokenType::NOTEqual:
+ return ProduceToken(type, "!=");
+ case TokenType::LessEqual:
+ return ProduceToken(type, "<=");
+ case TokenType::GreaterEqual:
+ return ProduceToken(type, ">=");
+
+ case TokenType::Assignment:
+ return ProduceToken(type, "=");
+ case TokenType::ShortAssignment:
+ return ProduceToken(type, ":=");
+
+ case TokenType::Variadic:
+ return ProduceToken(type, "...");
+
+ case TokenType::LeftBrace:
+ return ProduceToken(type, "{");
+ case TokenType::RightBrace:
+ return ProduceToken(type, "}");
+ case TokenType::RightSquareBracket:
+ return ProduceToken(type, "]");
+ case TokenType::LeftSquareBracket:
+ return ProduceToken(type, "[");
+ case TokenType::LeftParen:
+ return ProduceToken(type, "(");
+ case TokenType::RightParen:
+ return ProduceToken(type, ")");
+
+ case TokenType::Comma:
+ return ProduceToken(type, ",");
+ case TokenType::Dot:
+ return ProduceToken(type, ".");
+
+ default: // EoF Invalid
+ break;
+ }
+ return ProduceToken(type, "");
+}
+
+inline Token Tokenizer::ProduceErrorToken()
+{
+ return ProduceToken(TokenType::Invalid, "");
+}
+
+IPLString Tokenizer::ParseComment()
+{
+ auto start = m_Current;
+
+ if (!Match('/'))
+ {
+ RETURN_FAIL(IPLString());
+ }
+
+ if (Match('/'))
+ {
+ while (!IsEnd(m_Code[m_Current]) && !IsNewLine(m_Code[m_Current]))
+ {
+ NextSymbol();
+ }
+ RETURN_SUCCESS(IPLString(m_Code + start, m_Code + m_Current))
+ }
+ else if (!Match('*'))
+ {
+ PreviousSymbol();
+ RETURN_FAIL(IPLString());
+ }
+
+ while (!IsEnd(m_Code[m_Current]))
+ {
+ if (Match('*') && Match('/'))
+ {
+ RETURN_SUCCESS(IPLString(m_Code + start, m_Code + m_Current));
+ }
+ NextSymbol();
+ }
+
+ SetError("unterminated comment");
+ RETURN_ERROR(IPLString());
+}
+
+IPLString Tokenizer::ParseWhitespaces()
+{
+ if (!IsWhiteSpace(m_Code[m_Current]) && !IsNewLine(m_Code[m_Current]))
+ {
+ RETURN_FAIL(IPLString());
+ }
+
+ auto start = m_Current;
+ while (true)
+ {
+ if (IsWhiteSpace(m_Code[m_Current]))
+ {
+ NextSymbol();
+ }
+ else if (IsNewLine(m_Code[m_Current]))
+ {
+ NextLine();
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ RETURN_SUCCESS(IPLString(m_Code + start, m_Code + m_Current));
+}
+
+// todo - many Go numbers, much refactoring
+double Tokenizer::ParseNumber()
+{
+ if (!IsDigit(m_Code[m_Current]))
+ {
+ RETURN_FAIL(0.0);
+ }
+
+ size_t parsedBytes = 0;
+ double number = std::stod(m_Code + m_Current, &parsedBytes);
+ m_Current += static_cast<unsigned>(parsedBytes);
+ m_Column += static_cast<unsigned>(parsedBytes);
+
+ RETURN_SUCCESS(number);
+}
+
+IPLString Tokenizer::ParseString()
+{
+ if (!IsStringBound(m_Code[m_Current]))
+ {
+ RETURN_FAIL(IPLString());
+ }
+
+ char bound = m_Code[m_Current];
+ auto start = m_Current;
+
+ // skip first " or ', or `
+ NextSymbol();
+ while (m_Code[m_Current] != bound && !IsEnd(m_Code[m_Current]) && !IsNewLine(m_Code[m_Current]))
+ {
+ NextSymbol();
+ }
+
+ if (IsEnd(m_Code[m_Current]) || IsNewLine(m_Code[m_Current]))
+ {
+ SetError("\"\" string literal contains an unescaped line break");
+ RETURN_ERROR(IPLString());
+ }
+
+ // skip second " or ', or `
+ NextSymbol();
+
+ RETURN_SUCCESS(IPLString(m_Code + start, m_Code + m_Current));
+}
+
+Keyword Tokenizer::ParseKeyword()
+{
+ if (!IsLowerCase(m_Code[m_Current]))
+ {
+ RETURN_FAIL(Keyword());
+ }
+
+ auto start = m_Current;
+
+ NextSymbol();
+ while (IsLowerCase(m_Code[m_Current]))
+ {
+ NextSymbol();
+ }
+
+ auto key = IPLString(m_Code + start, m_Code + m_Current);
+ auto keyword = m_KeyWordsTable.find(key);
+
+ if (keyword == m_KeyWordsTable.end())
+ {
+ // It's not a keyword so we must revert current counter
+ m_Column = m_Column + start - m_Current;
+ m_Current = start;
+
+ RETURN_FAIL(Keyword());
+ }
+
+ RETURN_SUCCESS((Keyword{ keyword->second, keyword->first }));
+}
+
+Identifier Tokenizer::ParseIdentifier()
+{
+ if (!IsValidIdentifierStartingChar(m_Code[m_Current]))
+ {
+ RETURN_FAIL(Identifier());
+ }
+
+ auto start = m_Current;
+
+ NextSymbol();
+ while (IsValidIdentifierChar(m_Code[m_Current]))
+ {
+ NextSymbol();
+ }
+
+ RETURN_SUCCESS((Identifier{ TokenType::Identifier, IPLString(m_Code + start, m_Code + m_Current) }));
+}
+
+inline bool Tokenizer::IsStateSuccess() const
+{
+ return m_GenerationState == State::Success;
+}
+
+inline bool Tokenizer::IsStateError() const
+{
+ return m_GenerationState == State::Error;
+}
+
+void Tokenizer::SetError(const IPLString& what)
+{
+ m_Error = IPLError{ m_Line, m_Column, "", "Syntax error: " + what };
+}
+
+Token Tokenizer::NextToken()
+{
+ m_SkipNextToken = false;
+
+ if (IsEnd(m_Code[m_Current]))
+ {
+ return ProduceToken(TokenType::Eof);
+ }
+
+ const auto& comment = ParseComment();
+ if (IsStateError())
+ {
+ return ProduceErrorToken();
+ }
+ if (IsStateSuccess())
+ {
+ if (!m_Settings.CreateCommentTokens)
+ {
+ m_SkipNextToken = true;
+ return ProduceSkipToken();
+ }
+ return ProduceToken(TokenType::Comment, comment);
+ }
+
+ const auto& spaces = ParseWhitespaces();
+ if (IsStateSuccess())
+ {
+ if (!m_Settings.CreateWhitespaceTokens)
+ {
+ m_SkipNextToken = true;
+ return ProduceSkipToken();
+ }
+ return ProduceToken(TokenType::Whitespace, spaces);
+ }
+
+ // Single Char
+ switch (m_Code[m_Current])
+ {
+ case '-': NextSymbol(); return Match('-') ? ProduceToken(TokenType::MinusMinus) :
+ Match('=') ? ProduceToken(TokenType::MinusEqual) :
+ ProduceToken(TokenType::Minus);
+ case '+': NextSymbol(); return Match('+') ? ProduceToken(TokenType::PlusPlus) :
+ Match('=') ? ProduceToken(TokenType::PlusEqual) :
+ ProduceToken(TokenType::Plus);
+
+ case '*': NextSymbol(); return Match('=') ? ProduceToken(TokenType::MultEqual) :
+ ProduceToken(TokenType::Star);
+ case '/': NextSymbol(); return Match('=') ? ProduceToken(TokenType::DivEqual) :
+ ProduceToken(TokenType::Division);
+ case '%': NextSymbol(); return Match('=') ? ProduceToken(TokenType::ModuloEqual) :
+ ProduceToken(TokenType::Modulo);
+
+ case '&': NextSymbol(); return Match('&') ? ProduceToken(TokenType::ConditionalAND) :
+ ProduceToken(TokenType::BitwiseAND);
+
+ case '|': NextSymbol(); return Match('|') ? ProduceToken(TokenType::ConditionalOR) :
+ ProduceToken(TokenType::BitwiseOR);
+
+ case '^': NextSymbol(); return Match('&') ? ProduceToken(TokenType::BitClear) :
+ ProduceToken(TokenType::BitwiseXOR);
+
+ case '=': NextSymbol(); return Match('=') ? ProduceToken(TokenType::Equal) :
+ ProduceToken(TokenType::Assignment);
+
+ case '>': NextSymbol(); return Match('=') ? ProduceToken(TokenType::GreaterEqual) :
+ Match('>') ? ProduceToken(TokenType::ShiftRight) :
+ ProduceToken(TokenType::Greater);
+
+ case '<': NextSymbol(); return Match('=') ? ProduceToken(TokenType::LessEqual) :
+ Match('<') ? ProduceToken(TokenType::ShiftLeft) :
+ Match('-') ? ProduceToken(TokenType::ReceiveOperator) :
+ ProduceToken(TokenType::Less);
+
+ case '!': NextSymbol(); return Match('=') ? ProduceToken(TokenType::NOTEqual) : ProduceToken(TokenType::NOT);
+
+ case '(': NextSymbol(); return ProduceToken(TokenType::LeftParen);
+ case ')': NextSymbol(); return ProduceToken(TokenType::RightParen);
+ case '[': NextSymbol(); return ProduceToken(TokenType::LeftSquareBracket);
+ case ']': NextSymbol(); return ProduceToken(TokenType::RightSquareBracket);
+ case '{': NextSymbol(); return ProduceToken(TokenType::LeftBrace);
+ case '}': NextSymbol(); return ProduceToken(TokenType::RightBrace);
+
+ case ',': NextSymbol(); return ProduceToken(TokenType::Comma);
+ case '.': NextSymbol(); return Match('.') ? Match('.') ? ProduceToken(TokenType::Variadic) :
+ ProduceToken(TokenType::Invalid, "") :
+ ProduceToken(TokenType::Dot);
+
+ case ';': NextSymbol(); return ProduceToken(TokenType::Semicolon);
+ case ':': NextSymbol(); return Match('=') ? ProduceToken(TokenType::ShortAssignment) :
+ ProduceToken(TokenType::Colon);
+
+ // todo: see if I've left some go-specific single chars out
+
+ default:
+ break;
+ }
+
+ const auto& number = ParseNumber();
+ if (IsStateSuccess())
+ {
+ // todo - add integers and perhaps imaginary numbers
+ return ProduceToken(TokenType::Float, "", number);
+ }
+
+ const auto& string = ParseString();
+ if (IsStateSuccess())
+ {
+ if (string.length() != 1)
+ {
+ return ProduceToken(TokenType::String, string);
+ }
+ return ProduceToken(TokenType::Rune, string);
+ }
+ else if (IsStateError())
+ {
+ return ProduceErrorToken();
+ }
+
+ const auto& keyword = ParseKeyword();
+ if (IsStateSuccess())
+ {
+ return ProduceToken(keyword.Type, keyword.Content);
+ }
+
+ const auto& identifier = ParseIdentifier();
+ if (IsStateSuccess())
+ {
+ return ProduceToken(identifier.Type, identifier.Content);
+ }
+
+ return ProduceErrorToken();
+}
diff --git a/homework/81013/go2html/Lexer.h b/homework/81013/go2html/Lexer.h
new file mode 100644
index 0000000..d1b5ae6
--- /dev/null
+++ b/homework/81013/go2html/Lexer.h
@@ -0,0 +1,125 @@
+#pragma once
+
+#include "CommonTypes.h"
+
+enum class TokenType
+{
+ // Keywords
+ Break,
+ Default,
+ Func,
+ Interface,
+ Select,
+ Case,
+ Defer,
+ Go,
+ Map,
+ Struct,
+ Chan,
+ Else,
+ Goto,
+ Package,
+ Switch,
+ Const,
+ Fallthrough,
+ If,
+ Range,
+ Type,
+ Continue,
+ For,
+ Import,
+ Return,
+ Var,
+
+ // Operators & Punctuation
+ Plus,
+ Minus,
+ Star,
+ Division,
+ Modulo,
+
+ NOT,
+ BitwiseAND,
+ BitwiseOR,
+ BitwiseXOR,
+ BitClear,
+ ShiftLeft,
+ ShiftRight,
+
+ PlusEqual,
+ MinusEqual,
+ MultEqual,
+ DivEqual,
+ ModuloEqual,
+
+ // -> skipped bitwise + equal stuff
+
+ ConditionalAND,
+ ConditionalOR,
+ ReceiveOperator,
+ PlusPlus,
+ MinusMinus,
+
+ Equal,
+ Less,
+ Greater,
+
+ NOTEqual,
+ LessEqual,
+ GreaterEqual,
+
+ Assignment,
+ ShortAssignment,
+
+ Variadic,
+
+ LeftParen,
+ RightParen,
+ LeftSquareBracket,
+ RightSquareBracket,
+ LeftBrace,
+ RightBrace,
+
+ Comma,
+ Dot,
+
+ Semicolon,
+ Colon,
+
+ // Identifier
+ Identifier,
+
+ // Types
+ DecimalInteger, // not really supported ATM
+ Float,
+ ImaginaryNum, // not really supported ATM
+ String,
+ Rune,
+
+ True,
+ False,
+ Nil,
+
+ Whitespace,
+ Comment,
+
+ Eof,
+ Invalid
+};
+
+struct Token
+{
+ TokenType Type;
+ unsigned Line;
+ IPLString Lexeme;
+ double Number;
+};
+
+struct LexerSettings
+{
+ bool CreateWhitespaceTokens;
+ bool CreateCommentTokens;
+};
+
+IPLResult Tokenize(const char* code, IPLVector<Token>& tokens, const LexerSettings& settings);
+IPLResult Tokenize(const char* code, IPLVector<Token>& tokens); \ No newline at end of file
diff --git a/homework/81013/go2html/README.md b/homework/81013/go2html/README.md
new file mode 100644
index 0000000..10e233f
--- /dev/null
+++ b/homework/81013/go2html/README.md
@@ -0,0 +1,8 @@
+Usage:
+
+Make solution via premake5
+> premake5 vs2015 premake5.lua
+
+Open the vs solution, add two command line arguments - the go file that you want to highlight and the html in which you would like to write
+
+example arguments: sample.go res.html
diff --git a/homework/81013/go2html/main.cpp b/homework/81013/go2html/main.cpp
new file mode 100644
index 0000000..4a8d65a
--- /dev/null
+++ b/homework/81013/go2html/main.cpp
@@ -0,0 +1,19 @@
+#include <iostream>
+#include <string>
+#include <fstream>
+#include <sstream>
+
+#include "CommonTypes.h"
+#include "Lexer.h"
+#include "Highlighter.h"
+
+int main(int argc, char **argv)
+{
+ // todo: WRITE PROPER TESTS
+ std::ifstream infile(argv[1]);
+ std::ofstream offile(argv[2]);
+
+ Highlight(infile, offile);
+
+ return 0;
+}
diff --git a/homework/81013/go2html/premake5.lua b/homework/81013/go2html/premake5.lua
new file mode 100644
index 0000000..17131f7
--- /dev/null
+++ b/homework/81013/go2html/premake5.lua
@@ -0,0 +1,22 @@
+solution 'HL'
+ configurations {'debug', 'release'}
+ flags {
+ 'FatalWarnings'
+ }
+
+ local root = 'build/'
+
+ configuration 'debug'
+ targetdir(root .. 'bin/debug')
+ objdir(root .. 'obj/debug')
+
+ configuration 'release'
+ targetdir(root .. 'bin/release')
+ objdir(root .. 'obj/release')
+ configuration '*'
+
+ project 'HL'
+ kind 'ConsoleApp'
+ language 'C++'
+ files '*.cpp'
+ files '*.h'
diff --git a/homework/81013/go2html/sample.go b/homework/81013/go2html/sample.go
new file mode 100644
index 0000000..9c76ea2
--- /dev/null
+++ b/homework/81013/go2html/sample.go
@@ -0,0 +1,308 @@
+package main
+
+import (
+ "encoding/json"
+ "errors"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "regexp"
+ "strconv"
+ "sync"
+ //"fmt"
+)
+
+type Cell struct {
+ X int64 `json:"x"`
+ Y int64 `json:"y"`
+}
+
+type GameOfLifeHandler struct {
+ Generation int
+ Board map[Cell]bool
+ Neighbours map[Cell]int
+ RespWriter http.ResponseWriter
+ BoardMutex sync.Mutex
+ NeighboursMutex sync.Mutex
+ RequestMutex sync.RWMutex
+}
+
+/* util functions */
+func (g *GameOfLifeHandler) sendError(err error) {
+ g.RespWriter.Header().Set("Error", err.Error())
+ g.RespWriter.WriteHeader(http.StatusBadRequest)
+}
+
+func (g *GameOfLifeHandler) parseXYQuery(r *http.Request) (result Cell, err error) {
+ values, err := url.ParseQuery(r.URL.RawQuery)
+ if err != nil {
+ g.sendError(err)
+ return
+ }
+
+ // validate
+ if len(values) != 2 {
+ err = errors.New("Invalid parameters query - invalid number of query parameters")
+ g.sendError(err)
+ return
+ }
+
+ xString := values.Get("x")
+ yString := values.Get("y")
+
+ if xString == "" || yString == "" {
+ err = errors.New("Invalid parameters query - x or y not set")
+ g.sendError(err)
+ return
+ }
+
+ x, err := strconv.ParseInt(xString, 10, 64)
+ if err != nil {
+ g.sendError(err)
+ return
+ }
+ y, err := strconv.ParseInt(yString, 10, 64)
+ if err != nil {
+ g.sendError(err)
+ return
+ }
+
+ result.X = x
+ result.Y = y
+
+ return
+}
+
+func updateNeighbours(cell Cell, neighbours *map[Cell]int) {
+ var neighbour Cell
+
+ for i := cell.X - 1; i <= cell.X+1; i++ {
+ for j := cell.Y - 1; j <= cell.Y+1; j++ {
+
+ if i == cell.X && j == cell.Y {
+ continue
+ }
+
+ neighbour.X = i
+ neighbour.Y = j
+
+ (*neighbours)[neighbour]++
+ }
+ }
+}
+
+/* url handlers */
+func (g *GameOfLifeHandler) addNewCells(r *http.Request) (err error) {
+ var cellSlice []Cell
+ cellSlice = make([]Cell, 0, 2)
+
+ jsonBytes, err := ioutil.ReadAll(r.Body)
+ if err != nil {
+ g.sendError(err)
+ return
+ }
+
+ err = json.Unmarshal(jsonBytes, &cellSlice)
+ if err != nil {
+ g.sendError(err)
+ return
+ }
+
+ for _, cell := range cellSlice {
+ g.BoardMutex.Lock()
+ g.Board[cell] = true
+ g.BoardMutex.Unlock()
+
+ g.NeighboursMutex.Lock()
+ updateNeighbours(cell, &g.Neighbours)
+ g.NeighboursMutex.Unlock()
+ }
+
+ return err
+}
+
+// writes json as a response
+func (g *GameOfLifeHandler) checkCell(w http.ResponseWriter, cell Cell) (err error) {
+ alive := make(map[string]bool)
+ alive["alive"] = g.Board[cell]
+
+ output, err := json.Marshal(alive)
+ if err != nil {
+ g.sendError(err)
+ return
+ }
+
+ w.WriteHeader(http.StatusOK)
+ w.Write(output)
+ return
+}
+
+// writes json as a response
+func (g *GameOfLifeHandler) generation(w http.ResponseWriter) {
+ generation := make([]Cell, 0, len(g.Board))
+
+ for cell := range g.Board {
+ generation = append(generation, cell)
+ }
+
+ currentGeneration := make(map[string]interface{})
+ currentGeneration["generation"] = strconv.Itoa(g.Generation)
+ currentGeneration["living"] = generation
+
+ output, err := json.Marshal(currentGeneration)
+ if err != nil {
+ g.sendError(err)
+ return
+ }
+
+ w.WriteHeader(http.StatusOK)
+ w.Write(output)
+}
+
+func (g *GameOfLifeHandler) evolve(w http.ResponseWriter) {
+ newBoard := make(map[Cell]bool)
+ newNeighbourBoard := make(map[Cell]int)
+
+ for cell, neighbours := range g.Neighbours {
+ if g.Board[cell] {
+ if neighbours == 2 || neighbours == 3 {
+ newBoard[cell] = true
+
+ updateNeighbours(cell, &newNeighbourBoard)
+ }
+ } else {
+ if neighbours == 3 {
+ newBoard[cell] = true
+
+ updateNeighbours(cell, &newNeighbourBoard)
+ }
+ }
+ }
+
+ g.BoardMutex.Lock()
+ g.Board = newBoard
+ g.BoardMutex.Unlock()
+
+ g.NeighboursMutex.Lock()
+ g.Neighbours = newNeighbourBoard
+ g.NeighboursMutex.Unlock()
+
+ g.Generation++
+
+ w.WriteHeader(http.StatusNoContent)
+}
+
+func (g *GameOfLifeHandler) reset(w http.ResponseWriter) {
+ g.Generation = 0
+
+ g.BoardMutex.Lock()
+ g.Board = make(map[Cell]bool)
+ g.BoardMutex.Unlock()
+
+ w.WriteHeader(http.StatusNoContent)
+}
+
+func (g *GameOfLifeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ path := r.URL.Path
+
+ switch {
+ case regexp.MustCompile(`/generation/evolve/`).MatchString(path):
+ // POST
+ if r.Method != "POST" {
+ w.WriteHeader(http.StatusMethodNotAllowed)
+ } else {
+ g.RequestMutex.Lock()
+
+ g.evolve(w)
+
+ g.RequestMutex.Unlock()
+ }
+
+ case regexp.MustCompile(`/generation/`).MatchString(path):
+ // GET
+ if r.Method != "GET" {
+ w.WriteHeader(http.StatusMethodNotAllowed)
+ } else {
+ g.RequestMutex.RLock()
+
+ g.generation(w)
+
+ g.RequestMutex.RUnlock()
+ }
+
+ case regexp.MustCompile(`/cell/status/`).MatchString(path):
+ // GET
+ if r.Method != "GET" {
+ w.WriteHeader(http.StatusMethodNotAllowed)
+ } else {
+ g.RequestMutex.RLock()
+
+ cell, err := g.parseXYQuery(r)
+ if err == nil {
+ g.checkCell(w, cell)
+ }
+
+ g.RequestMutex.RUnlock()
+ }
+
+ case regexp.MustCompile(`/cells/`).MatchString(path):
+ // POST
+ if r.Method != "POST" {
+ w.WriteHeader(http.StatusMethodNotAllowed)
+ } else {
+ g.RequestMutex.Lock()
+
+ if err := g.addNewCells(r); err == nil {
+ w.WriteHeader(http.StatusCreated)
+ }
+
+ g.RequestMutex.Unlock()
+ }
+
+ case regexp.MustCompile(`/reset/`).MatchString(path):
+ // POST
+ if r.Method != "POST" {
+ w.WriteHeader(http.StatusMethodNotAllowed)
+ } else {
+ g.RequestMutex.Lock()
+
+ g.reset(w)
+
+ g.RequestMutex.Unlock()
+ }
+
+ default:
+ w.WriteHeader(http.StatusNotFound)
+ }
+}
+
+func NewGameOfLifeHandler(cells [][2]int64) *GameOfLifeHandler {
+ board := make(map[Cell]bool)
+ neighbours := make(map[Cell]int)
+
+ var newCell Cell
+
+ for _, cell := range cells {
+ newCell.X = cell[0]
+ newCell.Y = cell[1]
+
+ board[newCell] = true
+
+ updateNeighbours(newCell, &neighbours)
+ }
+
+ gameOfLifeHandler := &GameOfLifeHandler{Generation: 0, Board: board, Neighbours: neighbours}
+
+ return gameOfLifeHandler
+}
+
+func main() {
+ gofh := NewGameOfLifeHandler([][2]int64{
+ {2, 3},
+ {2, 4},
+ {-20, 4},
+ {-42, 0},
+ })
+
+ http.ListenAndServe(":8080", gofh)
+}