diff options
| Commit | 1384d7ea4e80eaa6b195195af81de974575b2bb0 (patch) | |
|---|---|---|
| Author | 2018-10-21 17:36:32 +0300 | |
| Committer | 2018-10-21 17:36:32 +0300 | |
| Files | 4e73618753e1d42f9cfa60a247a8bcf35c483db7 /homework/45183/CodeFormatting | |
| IPL-1384d7ea4e80eaa6b195195af81de974575b2bb0.tar IPL-1384d7ea4e80eaa6b195195af81de974575b2bb0.tar.gz IPL-1384d7ea4e80eaa6b195195af81de974575b2bb0.zip | ||
| Parent | 7509dce0686d9a418975f73b3721ecaa2d534f16 (diff) | |
Lexer/homework (#15)
* Initial commit
* code is formatted
* Homework finished
* Genie project generation.
* Updated code with new constructors
7 files changed, 654 insertions, 0 deletions
| -rw-r--r-- | homework/45183/CodeFormatting/README.md | 16 | ||||
| -rw-r--r-- | homework/45183/CodeFormatting/genie.lua | 25 | ||||
| -rw-r--r-- | homework/45183/CodeFormatting/input1.js | 127 | ||||
| -rw-r--r-- | homework/45183/CodeFormatting/input2.js | 71 | ||||
| -rw-r--r-- | homework/45183/CodeFormatting/input3.js | 9 | ||||
| -rw-r--r-- | homework/45183/CodeFormatting/input4.js | 1 | ||||
| -rw-r--r-- | homework/45183/CodeFormatting/main.cpp | 405 |
diff --git a/homework/45183/CodeFormatting/README.md b/homework/45183/CodeFormatting/README.md new file mode 100644 index 0000000..51e8155 --- /dev/null +++ b/homework/45183/CodeFormatting/README.md @@ -0,0 +1,16 @@ +# Overview +The code here takes `.js` files as input and produces color formatted JavaScript code in an `.html` file. + +# Prerequisites +* Windows OS +* VisualStudio 2015 or 2017 + +# How to use +* Download GENie from https://github.com/bkaradzic/GENie +* Execute `./genie.exe vs2017` to generate a Visual Studio 2017 project. +* Open the solution to Build and Run. +* The output files should be created in the same folder as the `.js` files and they should open in your browser. + +# Known issues +* Lexer can't parse RegExp. +* Lexer can't parse Template literals.
\ No newline at end of file diff --git a/homework/45183/CodeFormatting/genie.lua b/homework/45183/CodeFormatting/genie.lua new file mode 100644 index 0000000..74b3734 --- /dev/null +++ b/homework/45183/CodeFormatting/genie.lua @@ -0,0 +1,25 @@ +PROJ_DIR = path.getabsolute(".") + +solution 'CodeFormatting' + configurations { 'Debug' } + configuration 'Windows' + flags { + 'FatalWarnings', + 'ExtraWarnings', + 'Symbols', + 'FullSymbols' + } + startproject 'CodeFormatting' + +configuration 'Debug' + targetdir(path.join(PROJ_DIR, 'bin', 'debug')) + objdir(path.join(PROJ_DIR, 'bin', 'debug')) + +project 'CodeFormatting' + kind 'ConsoleApp' + language 'C++' + files { + path.join(PROJ_DIR, "main.cpp"), + path.join(PROJ_DIR, "..", "..", "..", "JSImpl", "src", "Lexer.h"), + path.join(PROJ_DIR, "..", "..", "..", "JSImpl", "src", "Lexer.cpp") + }
\ No newline at end of file diff --git a/homework/45183/CodeFormatting/input1.js b/homework/45183/CodeFormatting/input1.js new file mode 100644 index 0000000..1c241e4 --- /dev/null +++ b/homework/45183/CodeFormatting/input1.js @@ -0,0 +1,127 @@ +true; +false; +undefined; +null; + +import CoherentUtilities from "../CoherentUtilities"; +import Select from "../components/Select/Select"; +import { SUPPORTED_UNIT_TYPES } from './UnitTypes'; + +const DEFAULT_OPTION = Object.freeze({ + value: 'default', + displayed: 'Use default units' +}); + +const SUPPORTED_UNIT_TYPES_POSITIONS = Object.assign([], SUPPORTED_UNIT_TYPES); +SUPPORTED_UNIT_TYPES_POSITIONS.push(DEFAULT_OPTION); +Object.freeze(SUPPORTED_UNIT_TYPES_POSITIONS); + +const SUPPORTED_UNITS_TYPE_FONT_SIZE = Object.assign([], SUPPORTED_UNIT_TYPES_POSITIONS); +for (let i = 0; i < SUPPORTED_UNITS_TYPE_FONT_SIZE.length; ++i) { + if (SUPPORTED_UNITS_TYPE_FONT_SIZE[i].value === 'percents') { + SUPPORTED_UNITS_TYPE_FONT_SIZE.splice(i, 1); + break; + } +} +Object.freeze(SUPPORTED_UNITS_TYPE_FONT_SIZE); + +const AUTO_OPTION = Object.freeze({ + value: 'auto', + displayed: 'auto' +}); + +const SUPPORTED_UNITS_TYPE_DIMENSIONS = Object.assign([], SUPPORTED_UNIT_TYPES); +SUPPORTED_UNITS_TYPE_DIMENSIONS.push(AUTO_OPTION); +SUPPORTED_UNITS_TYPE_DIMENSIONS.push(DEFAULT_OPTION); +Object.freeze(SUPPORTED_UNITS_TYPE_DIMENSIONS); + +const DEFAULT_UNIT_SELECTION_INDEX = SUPPORTED_UNIT_TYPES_POSITIONS.length - 1; +const DEFAULT_UNITS_SELECTION_VALUE = SUPPORTED_UNIT_TYPES_POSITIONS[DEFAULT_UNIT_SELECTION_INDEX].value; + +const UNITS_SELECT_CONFIGURATIONS = Object.freeze({ + WIDTH: Object.freeze({ + sSelector: '#widthUnits', + aOptionObj: SUPPORTED_UNITS_TYPE_DIMENSIONS, + nSelected: DEFAULT_UNIT_SELECTION_INDEX + }), + HEIGHT: Object.freeze({ + sSelector: '#heightUnits', + aOptionObj: SUPPORTED_UNITS_TYPE_DIMENSIONS, + nSelected: DEFAULT_UNIT_SELECTION_INDEX + }), + TOP: Object.freeze({ + sSelector: '#topUnits', + aOptionObj: SUPPORTED_UNIT_TYPES_POSITIONS, + nSelected: DEFAULT_UNIT_SELECTION_INDEX + }), + LEFT: Object.freeze({ + sSelector: '#leftUnits', + aOptionObj: SUPPORTED_UNIT_TYPES_POSITIONS, + nSelected: DEFAULT_UNIT_SELECTION_INDEX + }), + FONTSIZE: Object.freeze({ + sSelector: '#fontSizeUnits', + aOptionObj: SUPPORTED_UNITS_TYPE_FONT_SIZE, + nSelected: 0 + }) +}); + +export default class UnitsTab { + constructor() { + /** + * @type {Object.<string, Select>} + */ + UnitsTab.units = { + width: new Select(UNITS_SELECT_CONFIGURATIONS.WIDTH), + height: new Select(UNITS_SELECT_CONFIGURATIONS.HEIGHT), + top: new Select(UNITS_SELECT_CONFIGURATIONS.TOP), + left: new Select(UNITS_SELECT_CONFIGURATIONS.LEFT), + fontSize: new Select(UNITS_SELECT_CONFIGURATIONS.FONTSIZE) + } + UnitsTab.addEventHandlers(); + } + + /** + * Sets all values of the Unit Selects and writes the new values to the current selection. + * + * @param {Object} metadata - UnitsTab types for some item. + * @param {boolean} disable - Whether the units Select should be disabled. + */ + static deserializeUnitsPrefs(metadata, disable) { + const toWriteValues = []; + const toWriteTypes = []; + + for (let unitsType in UnitsTab.units) { + let metaValue = metadata.units[unitsType]; + + // Backwards compatability. + if (metaValue === '' || metaValue === undefined) { + metaValue = DEFAULT_UNITS_SELECTION_VALUE; + } + if (metaValue === 'px') { + metaValue = 'pixels'; + } + + UnitsTab.units[unitsType].setValue(metaValue, true); + UnitsTab.units[unitsType].setDisabled(disable); + + toWriteValues.push(metaValue); + toWriteTypes.push(unitsType); + } + } + + /** + * Attaches a change event listener to each UnitsTab Select that writes + * the new units type to the current selection. + */ + static addEventHandlers() { + for (let unitsType in UnitsTab.units) { + UnitsTab.units[unitsType].addEventListener('change', event => { + CoherentUtilities.fl_changeDataProperty( + unitsType, + "units", + event.detail.value); + }); + } + } +} diff --git a/homework/45183/CodeFormatting/input2.js b/homework/45183/CodeFormatting/input2.js new file mode 100644 index 0000000..c1c377c --- /dev/null +++ b/homework/45183/CodeFormatting/input2.js @@ -0,0 +1,71 @@ +export default class Queue { + constructor() { + this._firstNode = null; + this._lastNode = null; + } + + /** + * Retrieves the first element in the queue. + * It also removes it from the queue. + * @returns {*} + */ + dequeue() { + if (!this._firstNode) { + return this._firstNode; + } + + const payload = this._firstNode.payload; + + this._firstNode = this._firstNode.next; + + return payload; + } + + /** + * Adds an item to the queue + * @param {*} item + */ + enqueue(item) { + const newNode = { + payload: item, + next: null + }; + + if (this._firstNode === null) { + this._firstNode = newNode; + this._lastNode = newNode; + + } else { + this._lastNode.next = newNode; + this._lastNode = newNode; + } + } + + /** + * Empties the queue + */ + clear() { + this._firstNode = null; + this._lastNode = null; + } + + /** + * Gets the first element of the queue + * @returns {*} + */ + top() { + if (!this._firstNode) { + return this._firstNode; + } + + return this._firstNode.payload; + } + + /** + * Checks whether the queue is empty or not + * @returns {boolean} + */ + isEmpty() { + return !this._firstNode; + } +}
\ No newline at end of file diff --git a/homework/45183/CodeFormatting/input3.js b/homework/45183/CodeFormatting/input3.js new file mode 100644 index 0000000..11de05a --- /dev/null +++ b/homework/45183/CodeFormatting/input3.js @@ -0,0 +1,9 @@ +/** + * @type {Object.<string, Select>} + */ + +if (1 < 2) { + console.log(false); +} + +console.log("<>");
\ No newline at end of file diff --git a/homework/45183/CodeFormatting/input4.js b/homework/45183/CodeFormatting/input4.js new file mode 100644 index 0000000..9c342c9 --- /dev/null +++ b/homework/45183/CodeFormatting/input4.js @@ -0,0 +1 @@ +/regexp/i
\ No newline at end of file diff --git a/homework/45183/CodeFormatting/main.cpp b/homework/45183/CodeFormatting/main.cpp new file mode 100644 index 0000000..9498bff --- /dev/null +++ b/homework/45183/CodeFormatting/main.cpp @@ -0,0 +1,405 @@ +#include "../../../JSImpl/src/Lexer.h" +#include <Windows.h> +#include <fstream> +#include <iostream> +#include <sstream> +#include <cassert> + +// Theme +#define IDENTIFIER_COLOR "#e2c071" +#define SINGLE_KEYWORD_COLOR "#b9b8b9" +#define KEYWORD_COLOR "#bd6fd6" +#define OPERATOR_KEYWORD_COLOR "#5faee8" +#define NUMBER_COLOR "#e2c071" +#define STRING_COLOR "#8daf56" +#define BACKGROUND_COLOR "#1e1e1e" +#define COMMENT_COLOR "#5d6371" +#define MISC_COLOR "#dd7076" +#define BOLD "font-weight: bold;" + +void FormatCode(const IPLString& in, const IPLString& out); + +IPLString GetSolutionDir(); +IPLString GetInputCode(const IPLString& inputName); +bool DumpFormattedCode(const IPLVector<Token>& tokens, const IPLString& outName); +void PreviewFormattedCode(const IPLString& outName); + +void DumpFileBeginning(std::ofstream& out); +void DumpTokens(std::ofstream& out, const IPLVector<Token>& tokens); +void DumpFileEnd(std::ofstream& out); + +inline void DumpClassWithValue(std::ofstream& out, const IPLString& cssClass, const IPLString& lexeme); +inline void DumpClassWithValue(std::ofstream& out, const IPLString& cssClass, double number); +inline void DumpClassWithFormattedLexeme(std::ofstream& out, const IPLString& cssClass, const IPLString& lexeme); +void DumpCompatibleHTML(std::ofstream& out, const IPLString& text); + +inline void DumpOperatorKeyword(std::ofstream& out, const Token& token); +inline void DumpSingleKeyword(std::ofstream& out, const Token& token); +inline void DumpKeyword(std::ofstream& out, const Token& token); +inline void DumpString(std::ofstream& out, const Token& token); +inline void DumpMisc(std::ofstream& out, const Token& token); +inline void DumpNumber(std::ofstream& out, const Token& token); +inline void DumpIdentifier(std::ofstream& out, const Token& token); +inline void DumpComment(std::ofstream& out, const Token& token); + +int main() +{ + const IPLVector<IPLString> inputs = { "input1.js","input2.js", "input3.js", "input4.js"}; + const IPLString outputFilePrefix = "output"; + + for (std::size_t i = 0; i < inputs.size(); ++i) + { + FormatCode(inputs[i], outputFilePrefix + std::to_string(i + 1) + ".html"); + } + + return 0; +} + +IPLString GetSolutionDir() +{ + TCHAR* buffer = new TCHAR[MAX_PATH]; + int nSize = GetModuleFileName(NULL, buffer, MAX_PATH); + + if (nSize != 0) + { + IPLString ret(buffer); + delete[] buffer; + + auto dirDelim = ret.rfind("\\bin"); + + if (dirDelim != IPLString::npos) + { + ret.resize(dirDelim + 1); + return ret; + } + } + + return IPLString(); +} + +IPLString GetInputCode(const IPLString& inputName) +{ + auto dir = GetSolutionDir(); + if (!dir.empty()) + { + std::ifstream input(dir + inputName); + if (input.good()) + { + std::stringstream buffer; + buffer << input.rdbuf(); + input.close(); + return buffer.str(); + } + } + + std::cerr << "Input file error.\n"; + return IPLString(); +} + +void DumpFileBeginning(std::ofstream& out) +{ + out << "<html>\n\n" + << "<head>\n" + << "\t<style>\n" + << "\t\tdiv {\n\t\t\twhite-space: pre-wrap;\n\t\t}\n\n" + << "\t\tbody {\n" + << "\t\t\tbackground-color: " << BACKGROUND_COLOR << ";\n" + << "\t\t\tfont-size: 18px;\n" + << "\t\t\tfont-family: 'Consolas';\n" + << "\t\t}\n\n" + << "\t\t.identifier {\n\t\t\tcolor: " << IDENTIFIER_COLOR << ";\n\t\t}\n\n" + << "\t\t.single-keyword {\n\t\t\tcolor: " << SINGLE_KEYWORD_COLOR << ";\n\t\t}\n\n" + << "\t\t.keyword {\n \t\t\tcolor: " << KEYWORD_COLOR << ";\n\t\t}\n\n" + << "\t\t.operator-keyword {\n\t\t\tcolor: " << OPERATOR_KEYWORD_COLOR << "; " << BOLD << "\n\t\t}\n\n" + << "\t\t.number {\n\t\t\tcolor: " << NUMBER_COLOR << ";\n\t\t}\n\n" + << "\t\t.string {\n\t\t\tcolor: " << STRING_COLOR << ";\n\t\t}\n\n" + << "\t\t.comment {\n\t\t\tcolor: " << COMMENT_COLOR << ";\n\t\t}\n\n" + << "\t\t.misc {\n\t\t\tcolor: " << MISC_COLOR << ";\n\t\t}\n" + << "\t</style>\n" + << "</head>\n\n" + << "<body>\n" + << "\t<div id='text'></div>\n" + << "\t<script>\n" + << "\t\tdocument.getElementById('text').innerHTML = '"; +} + +void DumpFileEnd(std::ofstream& out) +{ + out << "';\n" + << "\t</script>\n" + << "</body>\n\n" + << "</html>"; +} + +void DumpCompatibleHTML(std::ofstream& out, const IPLString& text) +{ + for (auto& ch : text) + { + if (ch == '\n') + { + out << "\\n"; + } + else if (ch == '\t') + { + out << "\\t"; + } + else if (ch == '<') + { + out << "<"; + } + else if (ch == '>') + { + out << ">"; + } + else + { + out << ch; + } + } +} + +inline void DumpClassWithValue(std::ofstream& out, const IPLString& cssClass, const IPLString& lexeme) +{ + out << "<span class=\"" << cssClass << "\">" << lexeme << "</span>"; +} + +inline void DumpClassWithValue(std::ofstream& out, const IPLString& cssClass, double number) +{ + out << "<span class=\"" << cssClass << "\">" << number << "</span>"; +} + +inline void DumpClassWithFormattedLexeme(std::ofstream& out, const IPLString& cssClass, const IPLString& lexeme) +{ + out << "<span class=\"" << cssClass << "\">"; + DumpCompatibleHTML(out, lexeme); + out << "</span>"; +} + +inline void DumpComment(std::ofstream& out, const Token& token) +{ + DumpClassWithFormattedLexeme(out, "comment", token.Lexeme); +} + +inline void DumpIdentifier(std::ofstream& out, const Token& token) +{ + DumpClassWithFormattedLexeme(out, "identifier", token.Lexeme); +} + +inline void DumpNumber(std::ofstream& out, const Token& token) +{ + DumpClassWithValue(out, "number", token.Number); +} + +inline void DumpMisc(std::ofstream& out, const Token& token) +{ + DumpClassWithValue(out, "misc", token.Lexeme); +} + +inline void DumpString(std::ofstream& out, const Token& token) +{ + auto str = token.Lexeme; + str.insert(str.begin(), '\\'); + str.insert(str.end() - 1, '\\'); + DumpClassWithValue(out, "string", str); +} + +inline void DumpKeyword(std::ofstream& out, const Token& token) +{ + DumpClassWithValue(out, "keyword", token.Lexeme); +} + +inline void DumpSingleKeyword(std::ofstream& out, const Token& token) +{ + DumpClassWithValue(out, "single-keyword", token.Lexeme); +} + +inline void DumpOperatorKeyword(std::ofstream& out, const Token& token) +{ + DumpClassWithFormattedLexeme(out, "operator-keyword", token.Lexeme); +} + +void DumpTokens(std::ofstream& out, const IPLVector<Token>& tokens) +{ + for (auto& token : tokens) + { + assert(token.Type != TokenType::Invalid); + + switch (token.Type) + { + case TokenType::Comment: + DumpComment(out, token); + break; + case TokenType::Whitespace: + DumpCompatibleHTML(out, token.Lexeme); + break; + case TokenType::Identifier: + DumpIdentifier(out, token); + break; + case TokenType::Null: + case TokenType::Undefined: + case TokenType::True: + case TokenType::False: + DumpMisc(out, token); + break; + case TokenType::Number: + DumpNumber(out, token); + break; + case TokenType::String: + DumpString(out, token); + break; + case TokenType::Break: + case TokenType::Case: + case TokenType::Catch: + case TokenType::Class: + case TokenType::Continue: + case TokenType::Debugger: + case TokenType::Default: + case TokenType::Delete: + case TokenType::Do: + case TokenType::Else: + case TokenType::Export: + case TokenType::Extends: + case TokenType::Finally: + case TokenType::For: + case TokenType::Function: + case TokenType::If: + case TokenType::Import: + case TokenType::In: + case TokenType::Instanceof: + case TokenType::New: + case TokenType::Return: + case TokenType::Super: + case TokenType::Switch: + case TokenType::This: + case TokenType::Throw: + case TokenType::Try: + case TokenType::Typeof: + case TokenType::Void: + case TokenType::While: + case TokenType::With: + case TokenType::Yield: + case TokenType::Const: + case TokenType::Var: + case TokenType::Let: + case TokenType::Dot: + DumpKeyword(out, token); + break; + case TokenType::LeftParen: + case TokenType::RightParen: + case TokenType::LeftBrace: + case TokenType::RightBrace: + case TokenType::Comma: + case TokenType::Semicolon: + case TokenType::QuestionMark: + case TokenType::Colon: + case TokenType::LeftSquareBracket: + case TokenType::RightSquareBracket: + DumpSingleKeyword(out, token); + break; + case TokenType::PlusEqual: + case TokenType::MinusEqual: + case TokenType::Minus: + case TokenType::Plus: + case TokenType::Star: + case TokenType::Division: + case TokenType::Modulo: + case TokenType::BitwiseNot: + case TokenType::BitwiseAnd: + case TokenType::BitwiseXor: + case TokenType::BitwiseOr: + case TokenType::Bang: + case TokenType::BangEqual: + case TokenType::Equal: + case TokenType::EqualEqual: + case TokenType::StrictEqual: + case TokenType::StrictNotEqual: + case TokenType::MinusMinus: + case TokenType::PlusPlus: + case TokenType::LogicalAnd: + case TokenType::LogicalOr: + case TokenType::StarEqual: + case TokenType::DivideEqual: + case TokenType::ModuloEqual: + case TokenType::BitwiseAndEqual: + case TokenType::BitwiseXorEqual: + case TokenType::BitwiseOrEqual: + case TokenType::Greater: + case TokenType::GreaterEqual: + case TokenType::LessEqual: + case TokenType::Less: + case TokenType::RightShift: + case TokenType::LeftShift: + case TokenType::LeftShiftEqual: + case TokenType::RightShiftEqual: + DumpOperatorKeyword(out, token); + break; + case TokenType::Eof: + return; + default: + DumpMisc(out, token); + break; + } + } +} + +bool DumpFormattedCode(const IPLVector<Token>& tokens, const IPLString& outName) +{ + auto dir = GetSolutionDir(); + if (!dir.empty()) + { + std::ofstream out(dir + outName); + if (out.good()) + { + DumpFileBeginning(out); + DumpTokens(out, tokens); + DumpFileEnd(out); + out.close(); + return true; + } + } + + std::cerr << "Output file error.\n"; + return false; +} + +void PreviewFormattedCode(const IPLString& outName) +{ + auto dir = GetSolutionDir(); + if (!dir.empty()) + { + auto res = system((dir + outName).c_str()); + if (res == -1) + { + switch (errno) + { + case ENOENT: + case E2BIG: + case ENOEXEC: + case ENOMEM: + std::cerr << "Unable to preview output file.\n"; + default: + break; + } + } + } +} + +void FormatCode(const IPLString& in, const IPLString& out) +{ + IPLString code = GetInputCode(in); + if (!code.empty()) + { + IPLVector<Token> tokens; + const auto& res = Tokenize(code.c_str(), tokens, { true, true }); + if (!res.IsSuccessful) + { + std::cerr << "Lexer error at row " << res.Error.Row << " column " << res.Error.Column << ": " << res.Error.What; + return; + } + + if (DumpFormattedCode(tokens, out)) + { + PreviewFormattedCode(out); + } + } +}
\ No newline at end of file |
