580 lines
18 KiB
C++
580 lines
18 KiB
C++
#include "CrossLang.hpp"
|
|
#include <iostream>
|
|
#include <sstream>
|
|
namespace Tesses::CrossLang {
|
|
std::string EscapeString(std::string text, bool quote) {
|
|
std::string str = {};
|
|
if (quote)
|
|
str.push_back('\"');
|
|
for (auto item : text) {
|
|
if (item == '\\' || item == '\"' || item == '\'') {
|
|
str.push_back('\\');
|
|
str.push_back(item);
|
|
} else if (item == '\n') {
|
|
str.push_back('\\');
|
|
str.push_back('n');
|
|
} else if (item == '\r') {
|
|
str.push_back('\\');
|
|
str.push_back('r');
|
|
} else if (item == '\t') {
|
|
str.push_back('\\');
|
|
str.push_back('t');
|
|
} else if (item == '\f') {
|
|
str.push_back('\\');
|
|
str.push_back('f');
|
|
} else if (item == '\0') {
|
|
str.push_back('\\');
|
|
str.push_back('0');
|
|
} else if (item == '\b') {
|
|
str.push_back('\\');
|
|
str.push_back('b');
|
|
} else if (item == '\a') {
|
|
str.push_back('\\');
|
|
str.push_back('a');
|
|
} else if (item == '\v') {
|
|
str.push_back('\\');
|
|
str.push_back('v');
|
|
} else if (item == '\x1B') {
|
|
str.push_back('\\');
|
|
str.push_back('e');
|
|
} else if ((uint8_t)item < 32 || (uint8_t)item > 126) {
|
|
str.append("\\x");
|
|
str.push_back(Tesses::Framework::Http::HttpUtils::NibbleToHex(
|
|
((uint8_t)item >> 4) & 0x0F));
|
|
str.push_back(Tesses::Framework::Http::HttpUtils::NibbleToHex(
|
|
(uint8_t)item & 0x0F));
|
|
} else {
|
|
str.push_back(item);
|
|
}
|
|
}
|
|
|
|
if (quote)
|
|
str.push_back('\"');
|
|
return str;
|
|
}
|
|
|
|
void LexTokenLineInfo::Subtract(size_t len) {
|
|
this->offset -= len;
|
|
this->column -= len;
|
|
}
|
|
void LexTokenLineInfo::Add(int c) {
|
|
this->offset++;
|
|
switch (c) {
|
|
case ' ':
|
|
this->column++;
|
|
break;
|
|
case '\n':
|
|
this->column = 1;
|
|
this->line++;
|
|
break;
|
|
case '\t':
|
|
this->column += 4;
|
|
break;
|
|
case '\r':
|
|
this->column++;
|
|
break;
|
|
default:
|
|
this->column++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
int Lex(std::string filename, std::istream &strm,
|
|
std::vector<LexToken> &tokens) {
|
|
int _peeked = -1;
|
|
|
|
auto Read = [&_peeked, &strm]() -> int {
|
|
if (_peeked > -1) {
|
|
int _peek2 = _peeked;
|
|
_peeked = -1;
|
|
return _peek2;
|
|
}
|
|
uint8_t b;
|
|
|
|
strm.read((char *)&b, 1);
|
|
if (strm.eof())
|
|
return -1;
|
|
return b;
|
|
};
|
|
|
|
auto Peek = [&_peeked, Read]() -> int {
|
|
if (_peeked > -1)
|
|
return _peeked;
|
|
_peeked = Read();
|
|
return _peeked;
|
|
};
|
|
|
|
int read;
|
|
int peek;
|
|
|
|
std::string buffer = {};
|
|
|
|
LexTokenLineInfo lineInfo;
|
|
|
|
lineInfo.filename = filename;
|
|
lineInfo.column = 1;
|
|
lineInfo.line = 1;
|
|
lineInfo.offset = 0;
|
|
|
|
std::string whiteSpaceCharsBefore = "";
|
|
|
|
auto Flush = [&buffer, &tokens, &lineInfo,
|
|
&whiteSpaceCharsBefore]() -> void {
|
|
if (!buffer.empty()) {
|
|
LexToken token;
|
|
token.text = buffer;
|
|
token.whiteSpaceCharsBefore = whiteSpaceCharsBefore;
|
|
token.type = LexTokenType::Identifier;
|
|
token.lineInfo = lineInfo;
|
|
token.lineInfo.Subtract(buffer.size());
|
|
tokens.push_back(token);
|
|
buffer.clear();
|
|
|
|
whiteSpaceCharsBefore = "";
|
|
}
|
|
};
|
|
|
|
auto Symbol = [&tokens, &lineInfo, &whiteSpaceCharsBefore](
|
|
std::initializer_list<int> chrs) -> void {
|
|
LexToken token;
|
|
|
|
token.type = LexTokenType::Symbol;
|
|
token.lineInfo = lineInfo;
|
|
|
|
token.whiteSpaceCharsBefore = whiteSpaceCharsBefore;
|
|
|
|
token.text.reserve(chrs.size());
|
|
|
|
for (auto i : chrs)
|
|
token.text.push_back((char)i);
|
|
|
|
tokens.push_back(token);
|
|
|
|
whiteSpaceCharsBefore = "";
|
|
};
|
|
|
|
auto ReadChr = [&lineInfo, &strm, Read]() -> std::pair<int, bool> {
|
|
int read = Read();
|
|
lineInfo.Add(read);
|
|
|
|
if (read == -1) {
|
|
|
|
return std::pair<int, bool>(-1, false);
|
|
}
|
|
|
|
if (read == '\\') {
|
|
read = Read();
|
|
lineInfo.Add(read);
|
|
if (read == -1) {
|
|
return std::pair<int, bool>(-1, true);
|
|
} else if (read == 'n') {
|
|
return std::pair<int, bool>('\n', true);
|
|
} else if (read == 'r') {
|
|
return std::pair<int, bool>('\r', true);
|
|
} else if (read == 'f') {
|
|
return std::pair<int, bool>('\f', true);
|
|
} else if (read == 'b') {
|
|
return std::pair<int, bool>('\b', true);
|
|
} else if (read == 'a') {
|
|
return std::pair<int, bool>('\a', true);
|
|
} else if (read == '0') {
|
|
return std::pair<int, bool>('\0', true);
|
|
} else if (read == 'v') {
|
|
return std::pair<int, bool>('\v', true);
|
|
} else if (read == 'e') {
|
|
return std::pair<int, bool>('\x1B', true);
|
|
} else if (read == 't') {
|
|
return std::pair<int, bool>('\t', true);
|
|
} else if (read == 'x') {
|
|
int r1 = Read();
|
|
lineInfo.Add(r1);
|
|
if (r1 == -1) {
|
|
return std::pair<int, bool>(-1, true);
|
|
}
|
|
int r2 = Read();
|
|
lineInfo.Add(r2);
|
|
if (r2 == -1) {
|
|
return std::pair<int, bool>(-1, true);
|
|
}
|
|
|
|
uint8_t c = (uint8_t)std::stoi(std::string{(char)r1, (char)r2},
|
|
nullptr, 16);
|
|
|
|
return std::pair<int, bool>(c, true);
|
|
} else {
|
|
return std::pair<int, bool>(read, true);
|
|
}
|
|
} else {
|
|
return std::pair<int, bool>(read, false);
|
|
}
|
|
};
|
|
|
|
auto ParseString = [&lineInfo, &strm, Read, ReadChr,
|
|
&tokens](bool interopolated) -> int {
|
|
auto lI = lineInfo;
|
|
|
|
std::string b = {};
|
|
|
|
auto rChr = ReadChr();
|
|
lineInfo.Add(rChr.first);
|
|
|
|
while (rChr.first != '\"' || rChr.second) {
|
|
if (rChr.first == -1)
|
|
return lineInfo.line;
|
|
|
|
b.push_back((char)rChr.first);
|
|
rChr = ReadChr();
|
|
lineInfo.Add(rChr.first);
|
|
}
|
|
|
|
if (interopolated) {
|
|
int e = 0;
|
|
int escapeI = 0;
|
|
std::string b2 = {};
|
|
|
|
for (size_t i = 0; i < b.size(); i++) {
|
|
if (b[i] == '{') {
|
|
if ((i + 1 < b.size() && b[i + 1] != '{') || escapeI >= 1) {
|
|
if (b2.size() > 0 && escapeI < 1) {
|
|
if (e > 0) {
|
|
LexToken _tkn;
|
|
_tkn.type = LexTokenType::Symbol;
|
|
_tkn.text = "+";
|
|
_tkn.lineInfo = lI;
|
|
tokens.push_back(_tkn);
|
|
}
|
|
LexToken _tkn2;
|
|
_tkn2.type = LexTokenType::String;
|
|
_tkn2.text = b2;
|
|
_tkn2.lineInfo = lI;
|
|
tokens.push_back(_tkn2);
|
|
b2.clear();
|
|
e++;
|
|
}
|
|
escapeI++;
|
|
if (escapeI > 1) {
|
|
b2.push_back('{');
|
|
}
|
|
} else {
|
|
b2.push_back('{');
|
|
i++;
|
|
}
|
|
} else if (b[i] == '}') {
|
|
if (escapeI >= 1) {
|
|
escapeI--;
|
|
if (b2.size() > 0 && escapeI == 0) {
|
|
if (e > 0) {
|
|
LexToken _tkn;
|
|
_tkn.type = LexTokenType::Symbol;
|
|
_tkn.text = "+";
|
|
_tkn.lineInfo = lI;
|
|
tokens.push_back(_tkn);
|
|
}
|
|
LexToken _tkn2;
|
|
_tkn2.type = LexTokenType::Symbol;
|
|
_tkn2.text = "(";
|
|
_tkn2.lineInfo = lI;
|
|
tokens.push_back(_tkn2);
|
|
std::stringstream strm2(
|
|
b2, std::ios_base::in | std::ios_base::binary);
|
|
int res = Lex("lexGen", strm2, tokens);
|
|
if (res != 0)
|
|
return res;
|
|
|
|
_tkn2.text = ")";
|
|
tokens.push_back(_tkn2);
|
|
|
|
_tkn2.text = ".";
|
|
tokens.push_back(_tkn2);
|
|
_tkn2.type = LexTokenType::Identifier;
|
|
_tkn2.text = "ToString";
|
|
tokens.push_back(_tkn2);
|
|
_tkn2.type = LexTokenType::Symbol;
|
|
_tkn2.text = "(";
|
|
tokens.push_back(_tkn2);
|
|
_tkn2.text = ")";
|
|
tokens.push_back(_tkn2);
|
|
b2.clear();
|
|
e++;
|
|
}
|
|
if (escapeI >= 1) {
|
|
b2.push_back('}');
|
|
}
|
|
}
|
|
} else {
|
|
b2.push_back(b[i]);
|
|
}
|
|
}
|
|
if (b2.size() > 0) {
|
|
if (escapeI > 0) {
|
|
if (e > 0) {
|
|
LexToken _tkn;
|
|
_tkn.type = LexTokenType::Symbol;
|
|
_tkn.text = "+";
|
|
_tkn.lineInfo = lI;
|
|
tokens.push_back(_tkn);
|
|
}
|
|
LexToken _tkn2;
|
|
_tkn2.type = LexTokenType::Symbol;
|
|
_tkn2.text = "(";
|
|
_tkn2.lineInfo = lI;
|
|
tokens.push_back(_tkn2);
|
|
std::stringstream strm2(b2, std::ios_base::in |
|
|
std::ios_base::binary);
|
|
int res = Lex("lexGen", strm2, tokens);
|
|
if (res != 0)
|
|
return res;
|
|
|
|
_tkn2.text = ")";
|
|
tokens.push_back(_tkn2);
|
|
|
|
_tkn2.text = ".";
|
|
tokens.push_back(_tkn2);
|
|
_tkn2.type = LexTokenType::Identifier;
|
|
_tkn2.text = "ToString";
|
|
tokens.push_back(_tkn2);
|
|
_tkn2.type = LexTokenType::Symbol;
|
|
_tkn2.text = "(";
|
|
tokens.push_back(_tkn2);
|
|
_tkn2.text = ")";
|
|
tokens.push_back(_tkn2);
|
|
b2.clear();
|
|
e++;
|
|
|
|
} else {
|
|
if (e > 0) {
|
|
LexToken _tkn;
|
|
_tkn.type = LexTokenType::Symbol;
|
|
_tkn.text = "+";
|
|
_tkn.lineInfo = lI;
|
|
tokens.push_back(_tkn);
|
|
}
|
|
LexToken _tkn2;
|
|
_tkn2.type = LexTokenType::String;
|
|
_tkn2.text = b2;
|
|
_tkn2.lineInfo = lI;
|
|
tokens.push_back(_tkn2);
|
|
b2.clear();
|
|
e++;
|
|
}
|
|
}
|
|
} else {
|
|
LexToken _tkn2;
|
|
_tkn2.type = LexTokenType::String;
|
|
_tkn2.text = b;
|
|
_tkn2.lineInfo = lI;
|
|
tokens.push_back(_tkn2);
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
while ((read = Read()) != -1) {
|
|
|
|
peek = Peek();
|
|
|
|
switch (read) {
|
|
case '$':
|
|
if (peek == '\"') {
|
|
Flush();
|
|
lineInfo.Add(Read());
|
|
int re = ParseString(true);
|
|
if (re != 0)
|
|
return re;
|
|
} else {
|
|
buffer.push_back('$');
|
|
}
|
|
break;
|
|
case '\"': {
|
|
Flush();
|
|
int re = ParseString(false);
|
|
if (re != 0)
|
|
return re;
|
|
} break;
|
|
case '\'': {
|
|
Flush();
|
|
auto res = ReadChr();
|
|
if (res.first == -1)
|
|
return lineInfo.line;
|
|
int r = Read();
|
|
lineInfo.Add(r);
|
|
if (r != '\'')
|
|
return lineInfo.line;
|
|
LexToken token;
|
|
token.text = {(char)(uint8_t)res.first};
|
|
token.lineInfo = lineInfo;
|
|
token.type = LexTokenType::Char;
|
|
|
|
tokens.push_back(token);
|
|
} break;
|
|
case '#':
|
|
Flush();
|
|
while (true) {
|
|
int r = Read();
|
|
lineInfo.Add(r);
|
|
if (r == '\n' || r == -1)
|
|
break;
|
|
}
|
|
break;
|
|
case '/':
|
|
if (peek == '/') {
|
|
Flush();
|
|
while (true) {
|
|
int r = Read();
|
|
lineInfo.Add(r);
|
|
if (r == '\n' || r == -1)
|
|
break;
|
|
}
|
|
} else if (peek == '*') {
|
|
Flush();
|
|
while (true) {
|
|
int r = Read();
|
|
lineInfo.Add(r);
|
|
if (r == -1) {
|
|
return lineInfo.line;
|
|
}
|
|
if (r == '*') {
|
|
r = Read();
|
|
lineInfo.Add(r);
|
|
if (r == -1)
|
|
return lineInfo.line;
|
|
if (r == '/')
|
|
break;
|
|
}
|
|
}
|
|
} else if (peek == '^') {
|
|
Flush();
|
|
lineInfo.Add(Read());
|
|
std::string str = {};
|
|
while (true) {
|
|
int r = Read();
|
|
lineInfo.Add(r);
|
|
if (r == -1) {
|
|
return lineInfo.line;
|
|
}
|
|
if (r == '^') {
|
|
r = Read();
|
|
lineInfo.Add(r);
|
|
if (r == -1)
|
|
return lineInfo.line;
|
|
if (r == '^') {
|
|
str.push_back('^');
|
|
continue;
|
|
}
|
|
if (r == '/')
|
|
break;
|
|
str.push_back('^');
|
|
}
|
|
str.push_back((char)r);
|
|
}
|
|
LexToken token;
|
|
|
|
token.type = LexTokenType::Documentation;
|
|
token.lineInfo = lineInfo;
|
|
token.text = str;
|
|
tokens.push_back(token);
|
|
} else if (peek == '=') {
|
|
Flush();
|
|
lineInfo.Add(Read());
|
|
Symbol({read, peek});
|
|
} else {
|
|
Flush();
|
|
Symbol({read});
|
|
}
|
|
break;
|
|
case '<':
|
|
case '?':
|
|
if (peek == read) {
|
|
Flush();
|
|
lineInfo.Add(Read());
|
|
int peek2 = Peek();
|
|
if (peek2 == '=') {
|
|
lineInfo.Add(Read());
|
|
Symbol({read, peek, peek2});
|
|
} else {
|
|
Symbol({read, peek});
|
|
}
|
|
} else if (peek == '=') {
|
|
Flush();
|
|
lineInfo.Add(Read());
|
|
Symbol({read, peek});
|
|
} else {
|
|
Flush();
|
|
Symbol({read});
|
|
}
|
|
break;
|
|
case '+':
|
|
case '-':
|
|
case '|':
|
|
case '&':
|
|
if (peek == '=' || peek == read) {
|
|
Flush();
|
|
lineInfo.Add(Read());
|
|
Symbol({read, peek});
|
|
} else {
|
|
Flush();
|
|
Symbol({read});
|
|
}
|
|
break;
|
|
case '=':
|
|
if (peek == '>') {
|
|
Flush();
|
|
lineInfo.Add(Read());
|
|
Symbol({read, peek});
|
|
} else if (peek == '=') {
|
|
Flush();
|
|
lineInfo.Add(Read());
|
|
Symbol({read, peek});
|
|
} else {
|
|
Flush();
|
|
Symbol({read});
|
|
}
|
|
break;
|
|
case '^':
|
|
case '~':
|
|
case '!':
|
|
case '>':
|
|
case '*':
|
|
case '%':
|
|
//*
|
|
//*=
|
|
if (peek == '=') {
|
|
Flush();
|
|
lineInfo.Add(Read());
|
|
Symbol({read, peek});
|
|
} else {
|
|
Flush();
|
|
Symbol({read});
|
|
}
|
|
break;
|
|
case '(':
|
|
case ')':
|
|
case '[':
|
|
case ']':
|
|
case '{':
|
|
case '}':
|
|
case '.':
|
|
case ':':
|
|
case ';':
|
|
case ',':
|
|
Flush();
|
|
Symbol({read});
|
|
break;
|
|
case '\n':
|
|
case '\t':
|
|
case '\r':
|
|
case ' ':
|
|
Flush();
|
|
whiteSpaceCharsBefore += read;
|
|
|
|
break;
|
|
default:
|
|
buffer.push_back((char)read);
|
|
break;
|
|
}
|
|
|
|
lineInfo.Add(read);
|
|
}
|
|
Flush();
|
|
return 0;
|
|
}
|
|
} // namespace Tesses::CrossLang
|