diff --git a/.gitignore b/.gitignore index 34127a7..9600c3f 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ # Development environment .idea cmake-build-debug/ +.vscode/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 802304e..d32de7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,4 +3,4 @@ project(rpp) set(CMAKE_CXX_STANDARD 11) -add_executable(rpp src/main.cpp src/Lexer.cpp src/Lexer.h src/Parser.cpp src/Parser.h src/Interpreter.cpp src/Interpreter.h src/Hebrew.cpp src/Hebrew.h src/BuiltIns.cpp src/BuiltIns.h) \ No newline at end of file +add_executable(rpp src/main.cpp src/Lexer.cpp src/Lexer.h src/Parser.cpp src/Parser.h src/Interpreter.cpp src/Interpreter.h src/Hebrew.cpp src/Hebrew.h src/BuiltIns.cpp src/BuiltIns.h src/Exception.h src/IO.h src/IO.cpp) \ No newline at end of file diff --git a/README.md b/README.md index 8177e85..c1637cd 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Interpreted, untyped, object-oriented and super cool. A precompiled version for Win32 is available in the [release](https://github.com/daniel-shimon/rpp/releases/) section ```cmd - g++ -std=c++11 -static-libgcc -static-libstdc++ src/*.h src/*.cpp -o rpp.exe + g++ -std=c++11 -static src/*.h src/*.cpp -o rpp.exe ``` diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..b19e287 --- /dev/null +++ b/main.cpp @@ -0,0 +1,730 @@ +// +// Created by Dan The Man on 9/16/2018. +// + +#ifndef _WIN32 +#warning Bi-directional io is only supported on Windows. + +#else + +#include +#include +#include + +#define BACKSPACE 8 +#define TAB 9 +#define ENTER 13 +#define SPACE 32 + +using namespace std; + +CONSOLE_SCREEN_BUFFER_INFO cInfo; +HANDLE hStdout, hStdin; +DWORD oldMode; + +struct run +{ + wstring s = L""; + bool rtl = false; + unsigned int whitespace = 0; +}; + +void exit(int c) +{ + SetConsoleMode(hStdin, oldMode); + _exit(c); +} + +void error() +{ + exit(99000 + (int)GetLastError()); +} + +void writeStr(wstring s) +{ + DWORD out; + if (!WriteConsoleW(hStdout, s.c_str(), (DWORD)s.size(), &out, NULL)) + error(); + if (out != s.size()) + exit(10000 + (int)out); +} + +wchar_t getWChar() +{ + wchar_t ch; + DWORD read; + if (!ReadConsoleW(hStdin, &ch, 1, &read, NULL)) + error(); + if (read != 1) + exit(11000 + (int)read); + return ch; +} + +void writeWChar(wchar_t ch) +{ + DWORD written; + if (!WriteConsoleW(hStdout, &ch, 1, &written, NULL)) + error(); + if (written != 1) + exit(12000 + (int)written); +} + +wstring utf8ToUtf16(string s) +{ + auto chars = (size_t)MultiByteToWideChar(CP_UTF8, 0, s.c_str(), s.size(), NULL, 0); + wchar_t *ws = (wchar_t *)malloc(chars * sizeof(wchar_t)); + int converted = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), s.size(), ws, chars); + if (converted != chars) + exit(13000 + converted); + return wstring(ws, chars); +} + +string utf16ToUtf8(wstring ws) +{ + auto buffSize = (size_t)WideCharToMultiByte(CP_UTF8, 0, ws.c_str(), ws.size(), NULL, 0, NULL, NULL); + char *s = (char *)malloc(buffSize); + int converted = WideCharToMultiByte(CP_UTF8, 0, ws.c_str(), ws.size(), s, buffSize, NULL, NULL); + if (converted != buffSize) + exit(14000 + converted); + return string(s, buffSize); +} + +inline short windowWidth() +{ + return cInfo.dwSize.X; +} + +inline short cursorLocation() +{ + GetConsoleScreenBufferInfo(hStdout, &cInfo); + return cInfo.dwCursorPosition.X; +} + +inline short cursorRow() +{ + GetConsoleScreenBufferInfo(hStdout, &cInfo); + return cInfo.dwCursorPosition.Y; +} + +void moveCursor(int x) +{ + if (!x) + return; + + GetConsoleScreenBufferInfo(hStdout, &cInfo); + cInfo.dwCursorPosition.X += x; + if (cInfo.dwCursorPosition.X < 0) + { + cInfo.dwCursorPosition.X += cInfo.dwSize.X; + cInfo.dwCursorPosition.Y--; + } + else if (cInfo.dwCursorPosition.X >= cInfo.dwSize.X) + { + cInfo.dwCursorPosition.X -= cInfo.dwSize.X; + cInfo.dwCursorPosition.Y++; + } + SetConsoleCursorPosition(hStdout, cInfo.dwCursorPosition); +} + +void moveCursorRTL(int x) +{ + // wrap cursor in a right-to-left fashion + if (!x) + return; + + GetConsoleScreenBufferInfo(hStdout, &cInfo); + cInfo.dwCursorPosition.X += x; + // printf(" ========= %d %d ======== ", cInfo.dwCursorPosition.X, cInfo.dwSize.X); + if (cInfo.dwCursorPosition.X < 0) + { + cInfo.dwCursorPosition.X += cInfo.dwSize.X; + cInfo.dwCursorPosition.Y++; + } + else if (cInfo.dwCursorPosition.X >= cInfo.dwSize.X) + { + cInfo.dwCursorPosition.X -= cInfo.dwSize.X; + cInfo.dwCursorPosition.Y--; + } + SetConsoleCursorPosition(hStdout, cInfo.dwCursorPosition); +} + +void setCursor(short x, short y) +{ + GetConsoleScreenBufferInfo(hStdout, &cInfo); + cInfo.dwCursorPosition.X = x; + cInfo.dwCursorPosition.Y = y; + SetConsoleCursorPosition(hStdout, cInfo.dwCursorPosition); +} + +bool isHebrew(wchar_t ch) +{ + return 1488 <= ch && ch <= 1514; +} + +inline void reverse(wstring &from, wstring &to) +{ + to = L""; + for (auto pch = from.end() - 1; pch >= from.begin(); pch--) + to += *pch; +} + +wstring complexInputLTR() +{ + /* + * States: + * empty - current.s = "", runs size is 0, tmpStr is "", current.rtl does'nt matter. + * rtl run - cursor is at end of run ($לכל מי שכאן). + * rtl run and whitespace - cursor is at end of txt after whitespace (לכל מי שכאן $). + * also tmpStr contains whitespace characters. + */ + auto runs = stack(); + bool whitespace = true; + wstring tmpStr = L""; + + auto current = run(); + current.rtl = false; + + while (wchar_t ch = getWChar()) + { + switch (ch) + { + case BACKSPACE: + if (tmpStr.empty() && current.s.empty() && runs.empty()) + break; + + if (whitespace) + { + if (!tmpStr.empty()) + { // prevent backspace after hebrew run deletion + tmpStr = tmpStr.substr(0, tmpStr.length() - 1); + writeWChar(BACKSPACE); + if (tmpStr.empty()) + { + whitespace = false; + current.whitespace = 0; + if (current.rtl && !current.s.empty()) + moveCursor(-current.s.length()); + } + } + } + else + { + current.s = current.s.substr(0, current.s.length() - 1); + if (current.rtl) + { + reverse(current.s, tmpStr); + writeStr(tmpStr + L" "); + moveCursor(-current.s.length() - 1); + } + else + { + moveCursor(-1); + writeWChar(SPACE); + moveCursor(-1); + } + } + + if (!whitespace && current.s.empty()) + { // reached end of current run + if (!runs.empty()) + { + current = runs.top(); + runs.pop(); + + // prev run ends with whitespace + whitespace = true; + if (current.whitespace) + { + tmpStr = current.s.substr(current.s.length() - current.whitespace, current.whitespace); + current.s = current.s.substr(0, current.s.length() - current.whitespace); + } + else + { + tmpStr = L""; + } + } + else + { + // empty state + whitespace = true; + tmpStr = L""; + } + } + break; + case ENTER: + if (!whitespace && current.rtl) + moveCursor(current.s.length() + 1); + + tmpStr = L""; + runs.push(current); + while (runs.size()) + { + current = runs.top(); + tmpStr = current.s + tmpStr; + runs.pop(); + } + writeWChar(ENTER); + return tmpStr; + case TAB: + case SPACE: + if (!whitespace) + { + tmpStr = L""; + if (current.rtl) + { + moveCursor(current.s.length()); + } + } + writeWChar(SPACE); + tmpStr += SPACE; + whitespace = true; + break; + default: + if (whitespace) + { + current.s += tmpStr; + current.whitespace = tmpStr.length(); + if (current.rtl) + { + if (isHebrew(ch)) + { + moveCursor(-current.s.length()); + } + else + { + runs.push(current); + current = run(); + } + } + else + { + if (isHebrew(ch)) + { + runs.push(current); + current = run(); + current.rtl = true; + } + } + whitespace = false; + } + + current.s += ch; + if (current.rtl) + { + reverse(current.s, tmpStr); + writeStr(tmpStr); + moveCursor(-current.s.length()); + } + else + { + writeWChar(ch); + } + } + } + + return L""; +} + +wstring complexInputRTL() +{ + /* + * States: + * empty - current.s = "", runs size is 0, tmpStr is "", current.rtl does'nt matter. + * rtl run - cursor is at left end of run ($לכל מי שכאן). + * rtl run and whitespace - cursor is at the left of the txt ($ לכל מי שכאן). + * also tmpStr contains whitespace characters. + * otherwise cursor is at right end of ltr run. + * ltr run - cursor is at right side of run (hello$ יאללה מגניב). + * ltr run and whitespace - whitespace at left end of run ($ hello שלום וגם). + */ + auto runs = stack(); + bool whitespace = true; + wstring tmpStr = L""; + auto clearCell = wstring{SPACE, BACKSPACE}; + + auto current = run(); + current.rtl = false; + + while (wchar_t ch = getWChar()) + { + switch (ch) + { + case BACKSPACE: + if (tmpStr.empty() && current.s.empty() && runs.empty()) + break; + + if (whitespace) + { + if (!tmpStr.empty()) + { // prevent backspace after run deletion + tmpStr = tmpStr.substr(0, tmpStr.length() - 1); + writeStr(clearCell); + moveCursorRTL(1); + if (tmpStr.empty()) + { + whitespace = false; + current.whitespace = 0; + if (!current.rtl && !current.s.empty()) + moveCursorRTL(current.s.length()); + } + } + } + else + { + current.s = current.s.substr(0, current.s.length() - 1); + if (current.rtl) { + moveCursorRTL(1); + writeStr(clearCell); + } + else + { + if (current.s.length() <= cursorLocation()) { + moveCursorRTL(-current.s.length()); + writeStr(L" " + current.s + L"\b"); + } else { + moveCursorRTL(-current.s.length()); + volatile auto tailOffset = current.s.length() - (windowWidth() - cursorLocation()) + 1; + writeStr(L" " + current.s.substr(tailOffset) + L"\b"); + moveCursorRTL(tailOffset); + } + } + } + + if (!whitespace && current.s.empty()) + { // reached end of current run + if (!runs.empty()) + { + current = runs.top(); + runs.pop(); + + // prev run ends with whitespace + whitespace = true; + if (current.whitespace) + { + tmpStr = current.s.substr(current.s.length() - current.whitespace, current.whitespace); + current.s = current.s.substr(0, current.s.length() - current.whitespace); + } + else + tmpStr = L""; + } + else + { + // empty state + whitespace = true; + tmpStr = L""; + } + } + break; + case ENTER: + if (!whitespace && !current.rtl) + moveCursorRTL(-current.s.length()); + setCursor(0, cursorRow() + 1); + + tmpStr = L""; + runs.push(current); + while (runs.size()) + { + current = runs.top(); + tmpStr = current.s + tmpStr; + runs.pop(); + } + return tmpStr; + case TAB: + case SPACE: + { + int offset = -1; + if (!whitespace) + { + tmpStr = L""; + if (!current.rtl) + offset -= current.s.length(); + } + moveCursorRTL(offset); + tmpStr += SPACE; + whitespace = true; + break; + } + default: + if (whitespace) + { + current.s += tmpStr; + current.whitespace = tmpStr.length(); + if (current.rtl) + { + if (!isHebrew(ch)) + { + runs.push(current); + current = run(); + } + } + else + { + if (isHebrew(ch)) + { + runs.push(current); + current = run(); + current.rtl = true; + } + else + moveCursorRTL(current.s.length()); + } + whitespace = false; + } + + current.s += ch; + if (current.rtl) + { + writeStr(clearCell); + moveCursorRTL(-1); + } + else + { + auto loc = cursorLocation(); + auto row = cursorRow(); + if (loc + 1 < current.s.length()) { + moveCursor(-loc); + writeStr(current.s.substr(0, loc)); + auto i = loc + 1; + auto j = row; + while (i < current.s.length()) { // print lines + j++; + volatile auto tail = current.s.length() - i; + if (tail <= windowWidth()) + setCursor(windowWidth() - tail, j); + else + setCursor(0, j); + writeStr(current.s.substr(i, windowWidth())); + i += windowWidth(); + } + setCursor(loc, row); + } else { + moveCursor(1 - current.s.length()); + writeStr(current.s); + moveCursor(-1); + } + } + } + } + + return L""; +} + +void complexOutputLTR(wstring s, bool endLine) +{ + auto runs = stack(); + auto current = new wstring(); + bool whitespace = true; + bool rtl = false; + wstring tmpStr; + + for (auto pch = s.begin(); pch < s.end(); pch++) + { + auto ch = *pch; + switch (ch) + { + case ENTER: + case TAB: + case SPACE: + whitespace = true; + tmpStr += ch; + break; + default: + if (whitespace) + { + if (rtl) + { + if (!isHebrew(ch)) + { + wstring rev; + reverse(*current, rev); + runs.push(new wstring(rev)); + *current = tmpStr; + rtl = false; + } + else + *current += tmpStr; + } + else + { + *current += tmpStr; + if (isHebrew(ch)) + { + runs.push(current); + current = new wstring(); + rtl = true; + } + } + tmpStr = L""; + whitespace = false; + } + + *current += ch; + } + } + + if (whitespace) + *current += tmpStr; + if (rtl) + { + reverse(*current, tmpStr); + delete current; + runs.push(new wstring(tmpStr)); + } + else + runs.push(current); + + tmpStr = L""; + while (runs.size()) + { + current = runs.top(); + tmpStr = *current + tmpStr; + delete current; + runs.pop(); + } + if (endLine) + tmpStr += wstring{ENTER}; + writeStr(tmpStr); +} + +void complexOutputRTL(wstring s, bool endLine) +{ + auto runs = stack(); + auto current = new wstring(); + bool whitespace = true; + wstring tmpStr = L""; + bool rtl = false; + + for (auto pch = s.begin(); pch < s.end(); pch++) + { + auto ch = *pch; + switch (ch) + { + case ENTER: + case TAB: + case SPACE: + whitespace = true; + tmpStr += ch; + break; + default: + if (whitespace) + { + if (rtl) + { + *current += tmpStr; + if (!isHebrew(ch)) + { + reverse(*current, tmpStr); + runs.push(new wstring(tmpStr)); + *current = L""; + rtl = false; + } + } + else + { + if (isHebrew(ch)) + { + runs.push(current); + current = new wstring(tmpStr); + rtl = true; + } + else + *current += tmpStr; + } + tmpStr = L""; + whitespace = false; + } + + *current += ch; + } + } + + if (whitespace) + *current += tmpStr; + if (rtl) + { + reverse(*current, tmpStr); + delete current; + runs.push(new wstring(tmpStr)); + } + else + runs.push(current); + + tmpStr = L""; + while (runs.size()) + { + current = runs.top(); + tmpStr += *current; + delete current; + runs.pop(); + } + + if (tmpStr.length() > cursorLocation()) + { + unsigned int nextLines = tmpStr.length() - cursorLocation(); + // position for str ending + setCursor(1 + windowWidth() - (nextLines % windowWidth()), cursorRow() + 1 + nextLines / windowWidth()); + + // write last line + writeStr(tmpStr.substr(0, tmpStr.length() % windowWidth())); + tmpStr = tmpStr.substr(tmpStr.length() % windowWidth()); + setCursor(0, cursorRow() - (short)2); + + while (tmpStr.length() > windowWidth()) + { + writeStr(tmpStr.substr(0, (unsigned int)windowWidth())); + tmpStr = tmpStr.substr((unsigned int)windowWidth()); + setCursor(0, cursorRow() - (short)2); + } + + // write first line + writeStr(tmpStr); + setCursor(windowWidth() - (nextLines % windowWidth()), cursorRow() + (short)nextLines / windowWidth()); + } + else + { + moveCursor(-tmpStr.length() + 1); + writeStr(tmpStr); + moveCursor(-tmpStr.length()); + } + if (endLine) + setCursor(0, cursorRow() + 1); +} + +#endif + +void simpleInput() +{ + wstring txt = L""; + while (wchar_t ch = getWChar()) + { + switch (ch) + { + case BACKSPACE: + if (!txt.length()) + continue; + txt = txt.substr(0, txt.length() - 1); + writeStr(L"\b \b"); + moveCursor(-txt.length()); + break; + default: + moveCursor(-txt.length()); + txt += ch; + } + for (auto pch = txt.end() - 1; pch >= txt.begin(); pch--) + writeWChar(*pch); + } +} + +int main() +{ + hStdout = GetStdHandle(STD_OUTPUT_HANDLE); + hStdin = GetStdHandle(STD_INPUT_HANDLE); + GetConsoleMode(hStdin, &oldMode); + SetConsoleMode(hStdin, ENABLE_PROCESSED_INPUT); + + // setCursor(cInfo.dwSize.X - (short)1, cursorRow()); + // complexInputRTL(); + complexInputLTR(); + + SetConsoleMode(hStdin, oldMode); +} \ No newline at end of file diff --git a/src/BuiltIns.cpp b/src/BuiltIns.cpp index fc78750..2f8649d 100644 --- a/src/BuiltIns.cpp +++ b/src/BuiltIns.cpp @@ -13,6 +13,10 @@ void RPP::init() { vector arguments) -> Value* { if (arguments.size()) interpreter->print(arguments[0], false, false); + #ifdef ComplexOutput + if (IO->enabled) + return interpreter->createString(IO->complexInputRTL()); + #endif string input; getline(cin, input); return interpreter->createString(input); @@ -212,6 +216,12 @@ void RPP::init() { return new Value((double)value); }))}, + {AddOperator, new Value(new NativeFunction(1, [] + (Interpreter* interpreter, vector arguments) -> Value* { + if (arguments[0]->isString()) + return interpreter->createString(strAttr + arguments[0]->getString()); + return interpreter->createString(strAttr + arguments[0]->toString(interpreter)); + }))}, }), -1, StringClass)); // endregion diff --git a/src/Exception.h b/src/Exception.h new file mode 100644 index 0000000..814d848 --- /dev/null +++ b/src/Exception.h @@ -0,0 +1,30 @@ +// +// Created by Daniel on 10/26/2018. +// + +#ifndef RPP_EXCEPTION_H +#define RPP_EXCEPTION_H + +#include +#include + +using namespace std; + +class RPPException : public exception +{ +private: + string type; + string signature; + string message; + +public: + RPPException(string type, string signature, string message = "") : type(type), signature(signature), message(message){}; + virtual const char *what() const throw() + { + if (message.empty()) + return (new string(type + " " + signature))->c_str(); + return (new string(type + " " + signature + ": " + message))->c_str(); + }; +}; + +#endif //RPP_EXCEPTION_H diff --git a/src/Hebrew.cpp b/src/Hebrew.cpp index 91d44a4..5ec7732 100644 --- a/src/Hebrew.cpp +++ b/src/Hebrew.cpp @@ -21,10 +21,22 @@ string Hebrew::englishify(string value) { return output; } -template<> void Hebrew::print(string value, bool endLine) { +void Hebrew::print(string value, bool endLine, bool rtl) { + #ifdef ComplexOutput + if (IO->enabled) { + if (rtl) { + IO->rightAlign(); + IO->complexOutputRTL(value, endLine); + } else { + IO->leftAlign(); + IO->complexOutputLTR(value, endLine); + } + return; + } + #endif pprint(Hebrew::englishify(value)); } -template<> void Hebrew::print(const char* value, bool endLine) { - pprint(Hebrew::englishify(string(value))); +void Hebrew::print(const char* value, bool endLine, bool rtl) { + print(string(value), endLine, rtl); } diff --git a/src/Hebrew.h b/src/Hebrew.h index 702f4ca..8c69960 100644 --- a/src/Hebrew.h +++ b/src/Hebrew.h @@ -6,14 +6,13 @@ #ifndef RPP_HEBREW_H #define RPP_HEBREW_H -#include +#include "IO.h" +#include "utf8.h" + #include #include -#include #include -#include -#include "utf8.h" #define Self "אני" #define Init "__התחל__" #define ToString "__טקסט__" @@ -29,8 +28,6 @@ #define pprint(x) cout << x; if (endLine) cout << endl; -using namespace std; - namespace Hebrew { namespace { map createCharMap() { @@ -72,11 +69,8 @@ namespace Hebrew { } string englishify(string value); - template void print(T value, bool endLine = false) { - pprint(value); - }; - template<> void print(string value, bool endLine); - template<> void print(const char* value, bool endLine); + void print(string value, bool endLine, bool rtl=true); + void print(const char* value, bool endLine, bool rtl=true); }; #endif //RPP_HEBREW_H diff --git a/src/IO.cpp b/src/IO.cpp new file mode 100644 index 0000000..16ffa96 --- /dev/null +++ b/src/IO.cpp @@ -0,0 +1,7 @@ +// +// Created by Daniel on 10/26/2018. +// + +#include "IO.h" + +_IO* IO = new _IO(); \ No newline at end of file diff --git a/src/IO.h b/src/IO.h new file mode 100644 index 0000000..e39afea --- /dev/null +++ b/src/IO.h @@ -0,0 +1,629 @@ +// +// Created by Daniel on 10/26/2018. +// + +#ifndef RPP_IO_H +#define RPP_IO_H + +#include "Exception.h" +#include + +#define warning(msg) cout << "\n***" << msg << "***\n" << endl; + +#ifndef _WIN32 +#warning Bi-directional io is only supported on Windows. + +#else + +#define ComplexOutput +#define WIN32_LEAN_AND_MEAN + +#include +#include + +#define BACKSPACE 8 +#define TAB 9 +#define ENTER 13 +#define SPACE 32 + +#define windowsError throw RPPException("WinApi Error", "", to_string(GetLastError())); +#define consoleError(message) throw RPPException("Console Error", "", message); +#define encodingError(message) throw RPPException("Encoding Error", "", message); + +struct run { + wstring s = L""; + bool rtl = false; + unsigned int whitespace = 0; +}; + +class _IO { +public: + CONSOLE_SCREEN_BUFFER_INFO cInfo; + HANDLE hStdout, hStdin; + DWORD oldMode; + bool enabled; + + _IO() { + hStdout = GetStdHandle(STD_OUTPUT_HANDLE); + hStdin = GetStdHandle(STD_INPUT_HANDLE); + if (!GetConsoleMode(hStdin, &oldMode)) { + warning("Cannot get console mode. reverting to ascii output."); + enabled = false; + } else if (!SetConsoleMode(hStdin, ENABLE_PROCESSED_INPUT)) { + warning("Cannot change console mode. reverting to ascii output."); + enabled = false; + } else + enabled = true; + } + + void restore() { + SetConsoleMode(hStdin, oldMode); + } + + bool isHebrew(wchar_t ch) { + return ch == L'_' || (1488 <= ch && ch <= 1514); + } + + void writeStr(wstring s) { + DWORD out; + if (!WriteConsoleW(hStdout, s.c_str(), (DWORD) s.size(), &out, NULL)) + windowsError + if (out != s.size()) + consoleError("Cannot write " + to_string(s.size()) + " characters"); + } + + wchar_t getWChar() { + wchar_t ch; + DWORD read; + if (!ReadConsoleW(hStdin, &ch, 1, &read, NULL)) + windowsError + if (read != 1) + consoleError("Cannot read character") + return ch; + } + + void writeWChar(wchar_t ch) { + DWORD written; + if (!WriteConsoleW(hStdout, &ch, 1, &written, NULL)) + windowsError; + if (written != 1) + consoleError("Cannot write character") + } + + wstring utf8ToUtf16(string s) { + auto chars = (size_t) MultiByteToWideChar(CP_UTF8, 0, s.c_str(), s.size(), NULL, 0); + wchar_t *ws = (wchar_t *) malloc(chars * sizeof(wchar_t)); + int converted = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), s.size(), ws, chars); + if (converted != chars) + encodingError("Cannot convert to UTF16") + return wstring(ws, chars); + } + + string utf16ToUtf8(wstring ws) { + auto buffSize = (size_t) WideCharToMultiByte(CP_UTF8, 0, ws.c_str(), ws.size(), NULL, 0, NULL, NULL); + char *s = (char *) malloc(buffSize); + int converted = WideCharToMultiByte(CP_UTF8, 0, ws.c_str(), ws.size(), s, buffSize, NULL, NULL); + if (converted != buffSize) + encodingError("Cannot convert to UTF8") + return string(s, buffSize); + } + + inline short windowWidth() { + return cInfo.dwSize.X; + } + + inline short cursorLocation() { + GetConsoleScreenBufferInfo(hStdout, &cInfo); + return cInfo.dwCursorPosition.X; + } + + inline short cursorRow() { + GetConsoleScreenBufferInfo(hStdout, &cInfo); + return cInfo.dwCursorPosition.Y; + } + + void moveCursor(int x) { + if (!x) + return; + + GetConsoleScreenBufferInfo(hStdout, &cInfo); + cInfo.dwCursorPosition.X += x; + if (cInfo.dwCursorPosition.X < 0) { + cInfo.dwCursorPosition.X += cInfo.dwSize.X; + cInfo.dwCursorPosition.Y--; + } else if (cInfo.dwCursorPosition.X >= cInfo.dwSize.X) { + cInfo.dwCursorPosition.X -= cInfo.dwSize.X; + cInfo.dwCursorPosition.Y++; + } + SetConsoleCursorPosition(hStdout, cInfo.dwCursorPosition); + } + + void moveCursorRTL(int x) { + // wrap cursor in a right-to-left fashion + if (!x) + return; + + GetConsoleScreenBufferInfo(hStdout, &cInfo); + cInfo.dwCursorPosition.X += x; + if (cInfo.dwCursorPosition.X < 0) { + cInfo.dwCursorPosition.X += cInfo.dwSize.X; + cInfo.dwCursorPosition.Y++; + } else if (cInfo.dwCursorPosition.X >= cInfo.dwSize.X) { + cInfo.dwCursorPosition.X -= cInfo.dwSize.X; + cInfo.dwCursorPosition.Y--; + } + SetConsoleCursorPosition(hStdout, cInfo.dwCursorPosition); + } + + void setCursor(short x, short y) { + GetConsoleScreenBufferInfo(hStdout, &cInfo); + cInfo.dwCursorPosition.X = x; + cInfo.dwCursorPosition.Y = y; + SetConsoleCursorPosition(hStdout, cInfo.dwCursorPosition); + } + + inline void reverse(wstring &from, wstring &to) { + to = L""; + for (auto pch = from.end() - 1; pch >= from.begin(); pch--) + to += *pch; + } + + string complexInputLTR() { + /* + * States: + * empty - current.s = "", runs size is 0, tmpStr is "", current.rtl does'nt matter. + * rtl run - cursor is at end of run ($לכל מי שכאן). + * rtl run and whitespace - cursor is at end of txt after whitespace (לכל מי שכאן $). + * also tmpStr contains whitespace characters. + */ + auto runs = stack(); + bool whitespace = true; + wstring tmpStr = L""; + + auto current = run(); + current.rtl = false; + + while (wchar_t ch = getWChar()) { + switch (ch) { + case BACKSPACE: + if (tmpStr.empty() && current.s.empty() && runs.empty()) + break; + + if (whitespace) { + if (!tmpStr.empty()) { // prevent backspace after hebrew run deletion + tmpStr = tmpStr.substr(0, tmpStr.length() - 1); + writeWChar(BACKSPACE); + if (tmpStr.empty()) { + whitespace = false; + current.whitespace = 0; + if (current.rtl && !current.s.empty()) + moveCursor(-current.s.length()); + } + } + } else { + current.s = current.s.substr(0, current.s.length() - 1); + if (current.rtl) { + reverse(current.s, tmpStr); + writeStr(tmpStr + L" "); + moveCursor(-current.s.length() - 1); + } else { + moveCursor(-1); + writeWChar(SPACE); + moveCursor(-1); + } + } + + if (!whitespace && current.s.empty()) { // reached end of current run + if (!runs.empty()) { + current = runs.top(); + runs.pop(); + + // prev run ends with whitespace + whitespace = true; + if (current.whitespace) { + tmpStr = current.s.substr(current.s.length() - current.whitespace, + current.whitespace); + current.s = current.s.substr(0, current.s.length() - current.whitespace); + } else { + tmpStr = L""; + } + } else { + // empty state + whitespace = true; + tmpStr = L""; + } + } + break; + case ENTER: + if (!whitespace && current.rtl) + moveCursor(current.s.length() + 1); + + tmpStr = L""; + runs.push(current); + while (runs.size()) { + current = runs.top(); + tmpStr = current.s + tmpStr; + runs.pop(); + } + writeWChar(ENTER); + return utf16ToUtf8(tmpStr); + case TAB: + case SPACE: + if (!whitespace) { + tmpStr = L""; + if (current.rtl) { + moveCursor(current.s.length()); + } + } + writeWChar(SPACE); + tmpStr += SPACE; + whitespace = true; + break; + default: + if (whitespace) { + current.s += tmpStr; + current.whitespace = tmpStr.length(); + if (current.rtl) { + if (isHebrew(ch)) { + moveCursor(-current.s.length()); + } else { + runs.push(current); + current = run(); + } + } else { + if (isHebrew(ch)) { + runs.push(current); + current = run(); + current.rtl = true; + } + } + whitespace = false; + } + + current.s += ch; + if (current.rtl) { + reverse(current.s, tmpStr); + writeStr(tmpStr); + moveCursor(-current.s.length()); + } else { + writeWChar(ch); + } + } + } + + return ""; + } + + string complexInputRTL() { + /* + * States: + * empty - current.s = "", runs size is 0, tmpStr is "", current.rtl does'nt matter. + * rtl run - cursor is at left end of run ($לכל מי שכאן). + * rtl run and whitespace - cursor is at the left of the txt ($ לכל מי שכאן). + * also tmpStr contains whitespace characters. + * otherwise cursor is at right end of ltr run. + * ltr run - cursor is at right side of run (hello$ יאללה מגניב). + * ltr run and whitespace - whitespace at left end of run ($ hello שלום וגם). + */ + auto runs = stack(); + bool whitespace = true; + wstring tmpStr = L""; + auto clearCell = wstring{SPACE, BACKSPACE}; + + auto current = run(); + current.rtl = false; + + while (wchar_t ch = getWChar()) { + switch (ch) { + case BACKSPACE: + if (tmpStr.empty() && current.s.empty() && runs.empty()) + break; + + if (whitespace) { + if (!tmpStr.empty()) { // prevent backspace after run deletion + tmpStr = tmpStr.substr(0, tmpStr.length() - 1); + writeStr(clearCell); + moveCursorRTL(1); + if (tmpStr.empty()) { + whitespace = false; + current.whitespace = 0; + if (!current.rtl && !current.s.empty()) + moveCursorRTL(current.s.length()); + } + } + } else { + current.s = current.s.substr(0, current.s.length() - 1); + if (current.rtl) { + moveCursorRTL(1); + writeStr(clearCell); + } else { + if (current.s.length() <= cursorLocation()) { + moveCursorRTL(-current.s.length()); + writeStr(L" " + current.s + L"\b"); + } else { + moveCursorRTL(-current.s.length()); + volatile auto tailOffset = + current.s.length() - (windowWidth() - cursorLocation()) + 1; + writeStr(L" " + current.s.substr(tailOffset) + L"\b"); + moveCursorRTL(tailOffset); + } + } + } + + if (!whitespace && current.s.empty()) { // reached end of current run + if (!runs.empty()) { + current = runs.top(); + runs.pop(); + + // prev run ends with whitespace + whitespace = true; + if (current.whitespace) { + tmpStr = current.s.substr(current.s.length() - current.whitespace, + current.whitespace); + current.s = current.s.substr(0, current.s.length() - current.whitespace); + } else + tmpStr = L""; + } else { + // empty state + whitespace = true; + tmpStr = L""; + } + } + break; + case ENTER: + if (!whitespace && !current.rtl) + moveCursorRTL(-current.s.length()); + setCursor(0, cursorRow() + (short) 1); + + tmpStr = L""; + runs.push(current); + while (runs.size()) { + current = runs.top(); + tmpStr = current.s + tmpStr; + runs.pop(); + } + return utf16ToUtf8(tmpStr); + case TAB: + case SPACE: { + int offset = -1; + if (!whitespace) { + tmpStr = L""; + if (!current.rtl) + offset -= current.s.length(); + } + moveCursorRTL(offset); + tmpStr += SPACE; + whitespace = true; + break; + } + default: + if (whitespace) { + current.s += tmpStr; + current.whitespace = tmpStr.length(); + if (current.rtl) { + if (!isHebrew(ch)) { + runs.push(current); + current = run(); + } + } else { + if (isHebrew(ch)) { + runs.push(current); + current = run(); + current.rtl = true; + } else + moveCursorRTL(current.s.length()); + } + whitespace = false; + } + + current.s += ch; + if (current.rtl) { + writeStr(clearCell); + moveCursorRTL(-1); + } else { + auto loc = cursorLocation(); + auto row = cursorRow(); + if (loc + 1 < current.s.length()) { + moveCursor(-loc); + writeStr(current.s.substr(0, (unsigned int) loc)); + auto i = (unsigned int) (loc + 1); + auto j = row; + while (i < current.s.length()) { // print lines + j++; + volatile short tail = (short) (current.s.length() - i); + if (tail <= windowWidth()) + setCursor(windowWidth() - tail, j); + else + setCursor(0, j); + writeStr(current.s.substr(i, (unsigned int) windowWidth())); + i += windowWidth(); + } + setCursor(loc, row); + } else { + moveCursor(1 - current.s.length()); + writeStr(current.s); + moveCursor(-1); + } + } + } + } + + return ""; + } + + void complexOutputLTR(string _s, bool endLine) { + wstring s = utf8ToUtf16(_s); + auto runs = stack(); + auto current = new wstring(); + bool whitespace = true; + bool rtl = false; + wstring tmpStr; + + for (auto pch = s.begin(); pch < s.end(); pch++) { + auto ch = *pch; + switch (ch) { + case ENTER: + case TAB: + case SPACE: + whitespace = true; + tmpStr += ch; + break; + default: + if (whitespace) { + if (rtl) { + if (!isHebrew(ch)) { + wstring rev; + reverse(*current, rev); + runs.push(new wstring(rev)); + *current = tmpStr; + rtl = false; + } else + *current += tmpStr; + } else { + *current += tmpStr; + if (isHebrew(ch)) { + runs.push(current); + current = new wstring(); + rtl = true; + } + } + tmpStr = L""; + whitespace = false; + } + + *current += ch; + } + } + + if (whitespace) + *current += tmpStr; + if (rtl) { + reverse(*current, tmpStr); + delete current; + runs.push(new wstring(tmpStr)); + } else + runs.push(current); + + tmpStr = L""; + while (runs.size()) { + current = runs.top(); + tmpStr = *current + tmpStr; + delete current; + runs.pop(); + } + if (endLine) + tmpStr += wstring{ENTER}; + writeStr(tmpStr); + } + + void complexOutputRTL(string _s, bool endLine) { + wstring s = utf8ToUtf16(_s); + auto runs = stack(); + auto current = new wstring(); + bool whitespace = true; + wstring tmpStr = L""; + bool rtl = false; + + for (auto pch = s.begin(); pch < s.end(); pch++) { + auto ch = *pch; + switch (ch) { + case ENTER: + case TAB: + case SPACE: + whitespace = true; + tmpStr += ch; + break; + default: + if (whitespace) { + if (rtl) { + *current += tmpStr; + if (!isHebrew(ch)) { + reverse(*current, tmpStr); + runs.push(new wstring(tmpStr)); + *current = L""; + rtl = false; + } + } else { + if (isHebrew(ch)) { + runs.push(current); + current = new wstring(tmpStr); + rtl = true; + } else + *current += tmpStr; + } + tmpStr = L""; + whitespace = false; + } + + *current += ch; + } + } + + if (whitespace) + *current += tmpStr; + if (rtl) { + reverse(*current, tmpStr); + delete current; + runs.push(new wstring(tmpStr)); + } else + runs.push(current); + + tmpStr = L""; + while (runs.size()) { + current = runs.top(); + tmpStr += *current; + delete current; + runs.pop(); + } + + if (tmpStr.length() > cursorLocation()) { + unsigned int nextLines = tmpStr.length() - cursorLocation(); + // position for str ending + setCursor((short) 1 + windowWidth() - (short) (nextLines % windowWidth()), + cursorRow() + (short) 1 + (short) nextLines / windowWidth()); + + // write last line + writeStr(tmpStr.substr(0, tmpStr.length() % windowWidth())); + tmpStr = tmpStr.substr(tmpStr.length() % windowWidth()); + setCursor(0, cursorRow() - (short) 2); + + while (tmpStr.length() > windowWidth()) { + writeStr(tmpStr.substr(0, (unsigned int) windowWidth())); + tmpStr = tmpStr.substr((unsigned int) windowWidth()); + setCursor(0, cursorRow() - (short) 2); + } + + // write first line + writeStr(tmpStr); + setCursor(windowWidth() - (short) (nextLines % windowWidth()), + cursorRow() + (short) nextLines / windowWidth()); + } else { + moveCursor(-tmpStr.length() + 1); + writeStr(tmpStr); + moveCursor(-tmpStr.length()); + } + if (endLine) + setCursor(0, cursorRow() + (short) 1); + else + moveCursorRTL(-1); + } + + inline void rightAlign() { + setCursor(cInfo.dwSize.X - (short) 1, cursorRow()); + } + + inline void leftAlign() { + setCursor(0, cursorRow()); + } +}; + +extern _IO *IO; + +#define exit(c) \ + IO->restore(); \ + exit(c); + +#endif + + +#endif //RPP_IO_H diff --git a/src/Interpreter.cpp b/src/Interpreter.cpp index 6c6bc2a..5f3a5d6 100644 --- a/src/Interpreter.cpp +++ b/src/Interpreter.cpp @@ -7,15 +7,14 @@ // region evaluation -Value* Interpreter::evaluate(Expression* expression) { +Value *Interpreter::evaluate(Expression *expression) { if (expression->implicitValue) return expression->implicitValue; return expression->accept(this); } -Value* Interpreter::evaluateLiteral(LiteralExpression* literal) { - switch (literal->token->type) - { +Value *Interpreter::evaluateLiteral(LiteralExpression *literal) { + switch (literal->token->type) { case False: return new Value(false); case True: @@ -23,25 +22,24 @@ Value* Interpreter::evaluateLiteral(LiteralExpression* literal) { case None: return Value::None; case StringLiteral: { - return createString(literal->token, (string*)literal->token->value); + return createString(literal->token, (string *) literal->token->value); } case NumberLiteral: - return new Value((double*)literal->token->value); + return new Value((double *) literal->token->value); } runtimeError(literal->token, "unsupported literal"); } -Value* Interpreter::evaluateGrouping(GroupingExpression *grouping) { +Value *Interpreter::evaluateGrouping(GroupingExpression *grouping) { return this->evaluate(grouping->value); } -Value* Interpreter::evaluateUnary(UnaryExpression *unary) { - Value* value = evaluate(unary->expression); +Value *Interpreter::evaluateUnary(UnaryExpression *unary) { + Value *value = evaluate(unary->expression); value->token = unary->op; - switch (unary->op->type) - { + switch (unary->op->type) { case Not: return new Value(!truthEvaluation(value)); case Minus: @@ -51,19 +49,18 @@ Value* Interpreter::evaluateUnary(UnaryExpression *unary) { runtimeError(unary->op); } -Value* Interpreter::evaluateBinary(BinaryExpression *binary) { +Value *Interpreter::evaluateBinary(BinaryExpression *binary) { if (binary->op->type == And) return new Value(truthEvaluation(evaluate(binary->first)) && truthEvaluation(evaluate(binary->second))); if (binary->op->type == Or) return new Value(truthEvaluation(evaluate(binary->first)) || truthEvaluation(evaluate(binary->second))); - Value* first = evaluate(binary->first); + Value *first = evaluate(binary->first); first->token = binary->op; - Value* second = evaluate(binary->second); + Value *second = evaluate(binary->second); second->token = binary->op; - switch (binary->op->type) - { + switch (binary->op->type) { case Equals: return new Value(equalityEvaluation(first, second)); case NotEquals: @@ -83,7 +80,7 @@ Value* Interpreter::evaluateBinary(BinaryExpression *binary) { if (!first->getInstance()->attributes.count(AddOperator)) attributeError(binary->op, first->toString(), AddOperator); return first->getInstance()->attributes[AddOperator] - ->getFunction()->call(this, vector({second})); + ->getFunction()->call(this, vector({second})); } break; case Minus: @@ -93,18 +90,15 @@ Value* Interpreter::evaluateBinary(BinaryExpression *binary) { case Multiply: if (first->type == Number && second->type == Number) return new Value(first->getNumber() * second->getNumber()); - if ((first->isString() && second->type == Number) || (first->type == Number && second->isString())) - { + if ((first->isString() && second->type == Number) || (first->type == Number && second->isString())) { string repeat; int count; - if (first->isString()) - { + if (first->isString()) { repeat = first->getString(); - count = (int)second->getNumber(); - } else - { + count = (int) second->getNumber(); + } else { repeat = second->getString(); - count = (int)first->getNumber(); + count = (int) first->getNumber(); } string value = ""; for (; count > 0; count--) @@ -121,26 +115,26 @@ Value* Interpreter::evaluateBinary(BinaryExpression *binary) { } Value *Interpreter::evaluateVariable(VariableExpression *variable) { - Value* value = environment->get(*(string*)variable->token->value); + Value *value = environment->get(*(string *) variable->token->value); if (value == nullptr) - nameError(variable->token, *(string*)variable->token->value); + nameError(variable->token, *(string *) variable->token->value); return value; } -Value *Interpreter::evaluateCall(CallExpression* call) { - Value* callee = evaluate(call->callee); +Value *Interpreter::evaluateCall(CallExpression *call) { + Value *callee = evaluate(call->callee); Token *token = call->token; callee->token = token; currentToken = token; - vector arguments; + vector arguments; - for (Expression* expression : call->arguments) + for (Expression *expression : call->arguments) arguments.push_back(evaluate(expression)); if (callee->type == Function) { - FunctionValue* function = callee->getFunction(); + FunctionValue *function = callee->getFunction(); if (function->arity == arguments.size() || function->arity == -1) return function->call(this, arguments); @@ -159,17 +153,17 @@ Value *Interpreter::evaluateCall(CallExpression* call) { Value *Interpreter::evaluateFunction(FunctionExpression *function) { vector arguments; - for (Token* token : function->arguments) - arguments.push_back(*(string*)token->value); + for (Token *token : function->arguments) + arguments.push_back(*(string *) token->value); return new Value(new DeclaredFunction(arguments, function->action)); } Value *Interpreter::evaluateClass(ClassExpression *klass) { int initArity = 0; - map staticAttributes, methods; - for (AssignStatement* statement : klass->actions) { - Value* value = evaluate(statement->value); + map staticAttributes, methods; + for (AssignStatement *statement : klass->actions) { + Value *value = evaluate(statement->value); string name = *(string *) statement->token->value; if (value->type == Function) { methods[name] = value; @@ -183,10 +177,11 @@ Value *Interpreter::evaluateClass(ClassExpression *klass) { } Value *Interpreter::evaluateGet(GetExpression *get) { - Value* callee = evaluate(get->callee); - string name = get->name.empty() ? *(string*)get->token->value : get->name; + Value *callee = evaluate(get->callee); + currentToken = get->token; + string name = get->name.empty() ? *(string *) get->token->value : get->name; - Value* value = nullptr; + Value *value = nullptr; if (callee->type == Instance) { if (callee->getInstance()->klass->staticAttributes.count(name)) value = callee->getInstance()->klass->staticAttributes[name]; @@ -207,16 +202,15 @@ Value *Interpreter::evaluateGet(GetExpression *get) { return value; } -bool Interpreter::truthEvaluation(Value* value) { +bool Interpreter::truthEvaluation(Value *value) { if (value->type == Bool) return value->getBool(); return value->type != NoneType; } -bool Interpreter::equalityEvaluation(Value* first, Value* second) { +bool Interpreter::equalityEvaluation(Value *first, Value *second) { if (first->type == second->type) - switch (first->type) - { + switch (first->type) { case Bool: return first->getBool() == second->getBool(); case Number: @@ -236,48 +230,41 @@ bool Interpreter::equalityEvaluation(Value* first, Value* second) { // region execution -Value* Interpreter::execute(vector statements, bool evaluate) { - Value* returnValue = Value::None; - for (Statement* statement : statements) - { - try - { +Value *Interpreter::execute(vector statements, bool evaluate) { + Value *returnValue = Value::None; + for (Statement *statement : statements) { + try { if (evaluate) - if (ExpressionStatement* expression = dynamic_cast(statement)) { + if (ExpressionStatement *expression = dynamic_cast(statement)) { returnValue = executeExpression(expression); break; } statement->accept(this); } - catch (ReturnValue value) - { + catch (ReturnValue value) { runtimeError(value.token, "return statement from non-function"); } - catch (BreakValue value) - { + catch (BreakValue value) { runtimeError(value.token, "break statement from non-loop"); } - catch (ContinueValue value) - { + catch (ContinueValue value) { runtimeError(value.token, "continue statement from non-loop"); } - catch (Value* value) - { + catch (Value *value) { runtimeError(value->token, value->toString() + " thrown without catch"); } } return returnValue; } -Value* Interpreter::executeExpression(ExpressionStatement *statement) { +Value *Interpreter::executeExpression(ExpressionStatement *statement) { return evaluate(statement->expression); } void Interpreter::executeCommand(CommandStatement *statement) { Value *value = statement->expression ? evaluate(statement->expression) : Value::None; value->token = statement->command; - switch (statement->command->type) - { + switch (statement->command->type) { case Print: return print(value); case Exit: @@ -296,7 +283,7 @@ void Interpreter::executeCommand(CommandStatement *statement) { } void Interpreter::executeAssign(AssignStatement *statement) { - string name = *(string*)statement->token->value; + string name = *(string *) statement->token->value; Value *value = evaluate(statement->value); if (value->type == Function) { value->getFunction()->name = name; @@ -310,7 +297,7 @@ void Interpreter::executeIf(IfStatement *statement) { if (truthEvaluation(evaluate(statement->condition))) return statement->action->accept(this); - for (pair elif : statement->elifs) + for (pair elif : statement->elifs) if (truthEvaluation(evaluate(elif.first))) return elif.second->accept(this); @@ -319,10 +306,10 @@ void Interpreter::executeIf(IfStatement *statement) { } void Interpreter::executeBlock(BlockStatement *statement) { - Environment* newEnvironment = new Environment(environment); + Environment *newEnvironment = new Environment(environment); environment = newEnvironment; - for (Statement* inlineStatement : statement->statements) + for (Statement *inlineStatement : statement->statements) inlineStatement->accept(this); environment = newEnvironment->getEnclosing(); @@ -330,8 +317,7 @@ void Interpreter::executeBlock(BlockStatement *statement) { } void Interpreter::executeWhile(WhileStatement *statement) { - while (truthEvaluation(evaluate(statement->condition))) - { + while (truthEvaluation(evaluate(statement->condition))) { try { statement->action->accept(this); } @@ -345,9 +331,9 @@ void Interpreter::executeWhile(WhileStatement *statement) { } void Interpreter::executeSet(SetStatement *statement) { - Value* callee = evaluate(statement->callee); - Value* value = evaluate(statement->value); - string name = *(string*)statement->name->value; + Value *callee = evaluate(statement->callee); + Value *value = evaluate(statement->value); + string name = *(string *) statement->name->value; if (callee->getInstance()->klass->staticAttributes.count(name)) callee->getInstance()->klass->staticAttributes[name] = value; else @@ -356,13 +342,12 @@ void Interpreter::executeSet(SetStatement *statement) { void Interpreter::executeTry(TryStatement *statement) { bool caught = false; - Value* thrown = nullptr; + Value *thrown = nullptr; try { statement->action->accept(this); - } catch (Value* value) { + } catch (Value *value) { for (int i = 0; i < statement->filters.size(); i++) - if (isInstance(value, evaluate(statement->filters[i]))) - { + if (isInstance(value, evaluate(statement->filters[i]))) { Token *name = statement->catches[i].first; if (name) { @@ -397,37 +382,32 @@ void Interpreter::executeTry(TryStatement *statement) { void Interpreter::executeFor(ForStatement *statement) { GetExpression *getIterExpression = new GetExpression(statement->iterator, statement->name, Iterator); - CallExpression* callIterExpression = new CallExpression(statement->name, getIterExpression, vector()); + CallExpression *callIterExpression = new CallExpression(statement->name, getIterExpression, vector()); - Value* iterator = evaluateCall(callIterExpression); + Value *iterator = evaluateCall(callIterExpression); callIterExpression->implicitValue = iterator; GetExpression *getExpression = new GetExpression(callIterExpression, statement->name, NextItem); - CallExpression* callExpression = new CallExpression(statement->name, getExpression, vector()); - string name = *(string*)statement->name->value; + CallExpression *callExpression = new CallExpression(statement->name, getExpression, vector()); + string name = *(string *) statement->name->value; - Environment* newEnvironment = new Environment(environment); + Environment *newEnvironment = new Environment(environment); environment = newEnvironment; - while (true) - { - try - { - Value* value = evaluateCall(callExpression); + while (true) { + try { + Value *value = evaluateCall(callExpression); environment->set(name, value); statement->action->accept(this); } - catch (Value* value) - { + catch (Value *value) { if (!isInstance(value, globals[StopException])) throw value; break; } - catch (ContinueValue value) - { + catch (ContinueValue value) { continue; } - catch (BreakValue value) - { + catch (BreakValue value) { break; } } @@ -440,20 +420,18 @@ void Interpreter::executeFor(ForStatement *statement) { delete callIterExpression; } -Value *DeclaredFunction::call(Interpreter *interpreter, vector arguments) { - Environment* newEnvironment = new Environment(interpreter->environment, true); +Value *DeclaredFunction::call(Interpreter *interpreter, vector arguments) { + Environment *newEnvironment = new Environment(interpreter->environment, true); interpreter->environment = newEnvironment; for (int i = 0; i < argumentNames.size(); i++) newEnvironment->set(argumentNames[i], arguments[i]); - Value* value = Value::None; - try - { + Value *value = Value::None; + try { action->accept(interpreter); } - catch (ReturnValue returnValue) - { + catch (ReturnValue returnValue) { value = returnValue.value; } @@ -464,7 +442,7 @@ Value *DeclaredFunction::call(Interpreter *interpreter, vector arguments } Value *NativeFunction::call(Interpreter *interpreter, vector arguments) { - Value* value = ((Value* (*)(Interpreter*, vector))nativeCall)(interpreter, arguments); + Value *value = ((Value *(*)(Interpreter *, vector)) nativeCall)(interpreter, arguments); if (value == nullptr) return Value::None; @@ -477,7 +455,7 @@ Value *BoundFunction::call(Interpreter *interpreter, vector arguments) interpreter->environment = newEnvironment; newEnvironment->set(Self, self); - Value* value = function->call(interpreter, arguments); + Value *value = function->call(interpreter, arguments); interpreter->environment = newEnvironment->getEnclosing(); delete newEnvironment; @@ -489,27 +467,23 @@ Value *BoundFunction::call(Interpreter *interpreter, vector arguments) // region utils -void Interpreter::print(Value* value, bool printNone, bool printEndLine) { - switch (value->type) - { +void Interpreter::print(Value *value, bool printNone, bool printEndLine) { + switch (value->type) { case NoneType: if (printNone) { - Hebrew::print(value->toString()); + Hebrew::print(value->toString(), printEndLine); break; } return; default: if (value->isString()) - Hebrew::print(value->getString()); + Hebrew::print(value->getString(), printEndLine); else - Hebrew::print(value->toString(this)); - } - - if (printEndLine) - cout << endl; + Hebrew::print(value->toString(this), printEndLine); + } } -void Interpreter::runtimeError(Token* token, string message) { +void Interpreter::runtimeError(Token *token, string message) { if (token) throw RPPException("Runtime Error", token->errorSignature(), message); throw RPPException("Runtime Error", "", message); @@ -519,7 +493,7 @@ void Interpreter::runtimeError(string message) { runtimeError(currentToken, message); } -void Interpreter::nameError(Token* token, string name) { +void Interpreter::nameError(Token *token, string name) { throw RPPException("Name error", token->errorSignature(), name + " is not defined"); } @@ -536,14 +510,15 @@ bool Interpreter::isInstance(Value *obj, Value *cls) { } Value *Interpreter::createInstance(Value *callee, Token *token, const vector &arguments) { - Value* instance = new Value(new InstanceValue(callee->getClass())); - map methods; - for (pair method : callee->getClass()->methods) + Value *instance = new Value(new InstanceValue(callee->getClass())); + instance->token = token; + map methods; + for (pair method : callee->getClass()->methods) methods[method.first] = new Value( new BoundFunction(instance, method.second->getFunction(), method.first)); instance->getInstance()->attributes.insert(methods.begin(), methods.end()); - if (Value* init = methods[Init]) { + if (Value *init = methods[Init]) { int initArity = instance->getInstance()->klass->initArity; if (initArity == arguments.size() || initArity == -1) init->getFunction()->call(this, arguments); @@ -555,7 +530,7 @@ Value *Interpreter::createInstance(Value *callee, Token *token, const vector()); + Value *instance = createInstance(globals[StringClass], token, vector()); instance->getInstance()->nativeAttributes["str"] = name; return instance; } @@ -575,55 +550,54 @@ Environment *Environment::getEnclosing() { // region values -Value* Value::None = new Value(); +Value *Value::None = new Value(); double Value::getNumber() { if (type != Number) Interpreter::runtimeError(token, "value " + toString() + " is not a Number"); - return *(double*)value; + return *(double *) value; } bool Value::getBool() { if (type != Bool) Interpreter::runtimeError(token, "value " + toString() + " is not a Bool"); - return (bool)value; + return (bool) value; } string Value::getString() { if (type != Instance || getInstance()->klass->name != StringClass) Interpreter::runtimeError(token, "value " + toString() + " is not a String"); - return *(string*)getInstance()->nativeAttributes["str"]; + return *(string *) getInstance()->nativeAttributes["str"]; } -FunctionValue* Value::getFunction() { +FunctionValue *Value::getFunction() { if (type != Function) Interpreter::runtimeError(token, "value " + toString() + " is not a Function"); - return (FunctionValue*)value; + return (FunctionValue *) value; } ClassValue *Value::getClass() { if (type != Class) Interpreter::runtimeError(token, "value " + toString() + " is not a Class"); - return (ClassValue*)value; + return (ClassValue *) value; } InstanceValue *Value::getInstance() { if (type != Instance) Interpreter::runtimeError(token, "value " + toString() + " is not an Instance"); - return (InstanceValue*)value; + return (InstanceValue *) value; } -string Value::toString(Interpreter* interpreter) { - switch (type) - { +string Value::toString(Interpreter *interpreter) { + switch (type) { case NoneType: return "none"; case Number: { double value = getNumber(); string number; - if ((int)value == value) - number = to_string((int)value); + if ((int) value == value) + number = to_string((int) value); else number = to_string(getNumber()); @@ -670,7 +644,7 @@ string Value::toString(Interpreter* interpreter) { string str = "<"; if (!getInstance()->klass->name.empty()) - str += "'" + getInstance()->klass->name + "' "; + str += "'" + getInstance()->klass->name + "' "; str += "instance>"; return str; @@ -686,7 +660,7 @@ bool Value::isString() { // region globals -map Interpreter::globals = {}; +map Interpreter::globals = {}; void Interpreter::attributeError(Token *token, string callee, string name) { throw RPPException("Attribute error", token->errorSignature(), diff --git a/src/Lexer.cpp b/src/Lexer.cpp index 97dc4ab..f46f26d 100644 --- a/src/Lexer.cpp +++ b/src/Lexer.cpp @@ -94,7 +94,7 @@ vector Lexer::scan() { else if (isAlpha(ch) || ch == '_') scanIdentifier(); else - throw RPPException("unexpected Character", Token::errorSignature(line, index), string(1, (char)ch)); + throw RPPException("Unexpected character", Token::errorSignature(line, index), string(1, (char)ch)); } } @@ -201,9 +201,3 @@ string Token::errorSignature(int line, int index, string lexeme) { bool Lexer::isAlpha(uint32_t ch) { return (1488 <= ch && ch <= 1514) || ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z'); } - -const char *RPPException::what() const throw() { - if (message.empty()) - return (new string(type + " " + signature))->c_str(); - return (new string(type + " " + signature + ": " + message))->c_str(); -} diff --git a/src/Lexer.h b/src/Lexer.h index 7a5dad3..90c48f4 100644 --- a/src/Lexer.h +++ b/src/Lexer.h @@ -7,6 +7,7 @@ #define RSHI_LEXER_H #include "Hebrew.h" +#define TokenType _TokenType // handle winapi override enum TokenType { @@ -42,18 +43,6 @@ class Token static string errorSignature(int line, int index, string lexeme = ""); }; -class RPPException : public exception -{ -private: - string type; - string signature; - string message; -public: - RPPException(string type, string signature, string message = "") : - type(type), signature(signature), message(message) {}; - virtual const char* what() const throw(); -}; - //region Reserved const map reserved = { diff --git a/src/main.cpp b/src/main.cpp index 150e250..b264ad8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,10 +8,12 @@ #include -using namespace std; - string version = "0.1"; +void print(string s) { + Hebrew::print(s, true, false); +} + int execute(string* source, Interpreter *interpreter = nullptr) { bool eval = false; @@ -37,13 +39,13 @@ int execute(string* source, Interpreter *interpreter = nullptr) } catch (vector exceptions) { for (RPPException exception : exceptions) - Hebrew::print(exception.what(), true); + print(exception.what()); if (!eval) return 1; } catch (RPPException exception) { - Hebrew::print(exception.what(), true); + print(exception.what()); if (!eval) return 1; @@ -58,7 +60,7 @@ int execute(string* source, Interpreter *interpreter = nullptr) return 0; } -int main(int argC, char** argV) +int _main(int argC, char** argV) { string* source = new string(); int returnValue; @@ -66,16 +68,16 @@ int main(int argC, char** argV) if (argC == 2 && (string(argV[1]) == "-v" || string(argV[1]) == "--version")) { - Hebrew::print("rpp version " + version); + print("rpp version " + version); return 0; } if (argC == 2 && (string(argV[1]) == "-i" || string(argV[1]) == "--interactive")) { - Hebrew::print("Welcome to interactive rpp (" + version + ")!", true); + print("Welcome to interactive rpp (" + version + ")!"); Interpreter* interpreter = new Interpreter(); while (true) { - Hebrew::print(">"); + print(">"); getline(cin, *source); returnValue = execute(source, interpreter); if (returnValue != 0) @@ -87,7 +89,7 @@ int main(int argC, char** argV) ifstream file(argV[1]); if (!file.is_open()) { - Hebrew::print("could not open '" + string(argV[1]) + "'", true); + print("could not open '" + string(argV[1]) + "'"); return 2; } @@ -99,7 +101,7 @@ int main(int argC, char** argV) string::iterator invalid = utf8::find_invalid(line.begin(), line.end()); if (invalid != line.end()) { - Hebrew::print("invalid UTF-8 at line " + to_string(lineCount), true); + print("invalid UTF-8 at line " + to_string(lineCount)); return 2; } lineCount++; @@ -115,6 +117,13 @@ int main(int argC, char** argV) return execute(source); } - Hebrew::print("usage:\n\trpp [path] [-v] [--version] [-c code] [-i] [--interactive]", true); + print("usage:\n\trpp [path] [-v] [--version] [-c code] [-i] [--interactive]"); return 1; } + +int main(int argC, char** argV) { + int ret = _main(argC, argV); + #ifdef ComplexOutput + IO->restore(); + #endif +} \ No newline at end of file