Add functions, indentation, better variables parsing and more

Signed-off-by: Username404 <w.iron.zombie@gmail.com>
This commit is contained in:
Username404 2022-03-13 15:11:07 +01:00
parent b47c8b3dc7
commit f3d03adead
Signed by: Username404-59
GPG Key ID: 7AB361FBB257A5D1
9 changed files with 155 additions and 65 deletions

View File

@ -17,6 +17,12 @@ vector<tok> lex(const string& in)
const char& current = in[i];
switch (current) {
case LPAR: case LBRACE: case LBRACKET: {
const auto reversedCharacter = static_cast<char>(tok::inverseLCharacter(current));
if (find_corresponding(in.cbegin() + i + 1, in.cend(), current, reversedCharacter) != in.cend()) {
goto insertToken;
} else throw tok::LexerException(string("Missing \"") + reversedCharacter + "\" character", lineNumber);
}
case DIVIDE: if (in[i + 1] == current) i += 2; else goto insertToken;
case TAG: {
if (current == TAG && in[i + 1] == DEFINE) { // See the IDENTIFIER case in Parser.hpp
@ -42,8 +48,8 @@ vector<tok> lex(const string& in)
break;
} else goto insertToken;
}
case DEFINE: case LPAR: case RPAR: case COMMA:
case LBRACE: case RBRACE: case LBRACKET: case RBRACKET:
case DEFINE: case RPAR: case COMMA:
case RBRACE: case RBRACKET:
case PLUS: case HYPHEN: case LCOMP: case RCOMP:
case DOT: case DOLLAR_SIGN: case SQUOTE:
insertToken: resVal.emplace_back(static_cast<tok::type>(current), lineNumber);

View File

@ -5,6 +5,7 @@
#include "Yerbacon.hpp"
#include <limits>
#include <ostream>
#include <concepts>
struct tok {
typedef Yerbacon::Exception LexerException;
@ -29,6 +30,19 @@ struct tok {
operator char() const { return static_cast<char>(toktype); }
friend std::ostream& operator<<(std::ostream& output, const tok& it) { return output << it.toktext; }
};
auto find_corresponding(std::input_iterator auto begin, std::input_iterator auto end, const unsigned char open, const unsigned char close) {
unsigned short occurrences = 1;
return std::find_if(begin, end, [&open, &close, &occurrences](const char& it){
if (it == open) {
++occurrences;
} else if (it == close) {
return --occurrences == 0;
}
return false;
});
}
std::vector<tok> lex(const std::string& in);
#endif //YERBACON_TEST_H

View File

@ -23,6 +23,7 @@ struct ParseComponent {
struct NamedIdentifier: virtual ParseComponent {
const string name;
explicit NamedIdentifier(string_view nameText): name(nameText) {}
NamedIdentifier() = default;
};
typedef unique_ptr<ParseComponent> component_ptr;
@ -95,8 +96,10 @@ public:
namespace StandardComponents {
struct Define: NamedIdentifier {
const bool final;
explicit Define(const bool& isFinal, string_view nameText): NamedIdentifier(nameText), final(isFinal) {}
explicit Define(string_view nameText): Define(false, nameText) {}
ParseTree content;
explicit Define(const bool& isFinal, string_view nameText, ParseTree&& content): NamedIdentifier(nameText), final(isFinal), content(move(content)) {}
explicit Define(string_view nameText, ParseTree&& content): Define(false, nameText, move(content)) {}
Define(Define&& define) noexcept: Define(define.final, define.name, move(define.content)) {}
};
struct Reference: NamedIdentifier {
using NamedIdentifier::NamedIdentifier;
@ -107,9 +110,16 @@ namespace StandardComponents {
explicit String(const char* string): content(string) {}
};
}
struct Call: ParseTree {};
struct Call: ParseTree, Reference {
using Reference::Reference;
};
struct Function: NamedIdentifier, ParseTree {
ParseTree parameters;
using NamedIdentifier::NamedIdentifier;
};
struct Class: NamedIdentifier {
ParseTree body;
using NamedIdentifier::NamedIdentifier;
};
}

View File

@ -6,6 +6,7 @@
#include "../Yerbacon.hpp"
#include <concepts>
#include <span>
#include <memory>
namespace Parser {
typedef Yerbacon::Exception ParsingException;
@ -50,6 +51,22 @@ namespace Parser {
parsingError(next, hasNext ? " is not a valid class identifier" : "A class identifier is required", hasNext);
}
} else {
unsigned int parametersDistance = 0;
if (next.toktype == LPAR) {
const auto it = find_if(lexed.begin() + i, lexed.end(), [](const tok& token){ return token.toktype == RPAR; });
parametersDistance = distance(lexed.begin() + i, it);
i += parametersDistance;
}
if (nextAre({LCOMP, LCOMP, LBRACE})) {
Function function(current.toktext);
if (parametersDistance > 2) {
function.parameters = parse(lexed.begin() + ((i + 2) - parametersDistance), lexed.begin() + i);
}
parseTree << function;
i += 2;
break;
} else i -= parametersDistance;
bool isFinalDefine = nextAre({TAG, DEFINE});
if (isFinalDefine || next.toktype == DEFINE) {
const optional previousDefinition = parseTree.template findReferenceByName<Define>(current.toktext);
@ -58,39 +75,48 @@ namespace Parser {
parsingError(current, previousDefinition->get().final ? " cannot be redefined as it is final" : " cannot be made final after it has been declared", true);
}
}
parseTree << Define(isFinalDefine, current.toktext);
i += 1 + isFinalDefine;
} else {
parseTree << Define(isFinalDefine, current.toktext, parse( // TODO Find another way of choosing the tokens to parse
lexed.begin() + i + 2 + isFinalDefine, find_if(lexed.begin() + i + 2 + isFinalDefine, lexed.end(), [&current](const tok& it){
return it.line != current.line;
})
));
i += 2 + isFinalDefine;
} else if (next.toktype == '(') {
parseTree << Call(current.toktext);
} else
parseTree << Reference(current.toktext);
}
}
break;
}
case LPAR: case LBRACE: case LBRACKET: {
const auto inverseCharacter = tok::inverseLCharacter(current.toktype);
const auto closingCharacter = find_if(lexed.begin() + i, lexed.end(), [&inverseCharacter](const tok& it){
return it.toktype == inverseCharacter;
});
if (closingCharacter != lexed.end()) {
vector<tok> subTokens(lexed.begin() + i + 1, closingCharacter);
if (current.toktype == LPAR || current.toktype == LBRACKET) {
if (subTokens.size() >= 2 && subTokens[1].toktype != RPAR) {
for (auto iterator = subTokens.cbegin(); iterator < (subTokens.cend() - 1); ++iterator) {
const auto nextIterator = iterator + 1;
if (nextIterator->toktype == COMMA) {
subTokens.erase(nextIterator);
} else throw ParsingException("Missing comma after \"" + iterator->toktext + '"');
}
const auto closingCharacter = find_corresponding(lexed.begin() + i + 1, lexed.end(), current.toktype, tok::inverseLCharacter(current.toktype));
vector<tok> subTokens(lexed.begin() + i + 1, closingCharacter);
if (current.toktype == LPAR || current.toktype == LBRACKET) {
if (subTokens.size() >= 2 && subTokens[1].toktype != RPAR) {
for (auto iterator = subTokens.cbegin(); iterator < (subTokens.cend() - 1); ++iterator) {
const auto nextIterator = iterator + 1;
if (nextIterator->toktype == COMMA) {
subTokens.erase(nextIterator);
} else throw ParsingException("Missing comma after \"" + iterator->toktext + '"');
}
}
switch (current.toktype) {
case LPAR: parseTree << parse<Call>(subTokens); break;
case LBRACE: // TODO Add structures for class/function bodies
case LBRACKET:
default: parseTree << parse(subTokens); break;
}
const component_ptr& previous = parseTree.at(parseTree.size() - 1);
if (current.toktype == LPAR) {
try {
dynamic_cast<Call&>(*previous).ParseTree::operator=(parse<Call>(subTokens));
} catch (const bad_cast&) {
parsingError(current, "Unexpected parenthesis");
}
i = distance(lexed.begin(), closingCharacter);
} else parsingError(current, string(" is missing a closing \"").append(1, inverseCharacter) + '"', true);
} else if (current.toktype == LBRACE) {
const type_info& previous_id = previous->getId();
if (previous_id == typeid(Function)) {
dynamic_cast<Function&>(*previous).ParseTree::operator=(parse(subTokens));
} else if (previous_id == typeid(Class)) {
dynamic_cast<Class&>(*previous).body = parse(subTokens);
}
} else parseTree << parse(subTokens);
i = distance(lexed.begin(), closingCharacter);
break;
}
case RPAR: case RBRACE: case RBRACKET: parsingError(current, " \u27F5 Unexpected character", true);

View File

@ -74,33 +74,58 @@ protected:
typedef pair<const char*, const char*> print_functions_pair;
virtual unordered_task_map getTaskMap() = 0;
virtual print_functions_pair printFunctions() = 0;
void transpileTree(const derived_from<ParseTree> auto& parseTree) {
IS(ParseTree)
void transpileTree(const T& parseTree, const unsigned short& indentationLevel = 0, const function<void(decltype(parseTree.cbegin())&)>&& postInsertionFunction = [](auto&){}) {
if (parseTree.size() > 0) {
unsigned int subIndex = 0;
for (auto pointer_iterator = parseTree.cbegin(); pointer_iterator < parseTree.cend(); ++pointer_iterator) {
getTaskMapInstance().at(pointer_iterator->get()->getId())(parseTree, subIndex); ++subIndex;
if ((pointer_iterator + 1) != parseTree.cend() && parseTree.getId() == typeid(StandardComponents::Call)) {
output << ", ";
const auto added_size = indentationLevel * strlen(indentation);
if (newLines) {
separator.reserve(separator.size() + added_size);
for (unsigned short level = 0; level < indentationLevel; ++level) {
separator.append(indentation);
}
}
unsigned int subIndex = 0;
for (auto pointer_iterator = parseTree.cbegin(); pointer_iterator < parseTree.cend(); ++pointer_iterator) {
const type_info& id = pointer_iterator->get()->getId();
try {
getTaskMapInstance().at(id)(parseTree, subIndex); ++subIndex;
} catch (const out_of_range&) {
throw Yerbacon::Exception(string(
#ifndef __GNUC__
id.name()
#else
abi::__cxa_demangle(id.name(), nullptr, nullptr, nullptr)
#endif
) += " is not supported by the current target");
}
postInsertionFunction(pointer_iterator);
}
if (newLines) separator.erase(separator.size() - added_size);
}
}
IS(ParseTree)
void separate_transpileTree(const T& parseTree, const unsigned short& indentationLevel = 0) {
transpileTree(parseTree, indentationLevel, [this, &parseTree](const auto& iterator){
if (iterator + 1 != parseTree.cend()) {
output << separator;
}
});
}
typedef optional<const char*> optional_string;
virtual optional_string uniqueLineSeparator() { return ";"; };
const bool newLines;
const char* separator;
string separator;
static constexpr const char* indentation = " ";
public:
const unordered_task_map& getTaskMapInstance() {
static unordered_task_map staticMap = getTaskMap();
// Default / Shared tasks:
staticMap.merge(unordered_task_map({
make_task(ParseTree, transpileTree(parseComponent);),
make_task(StandardComponents::Reference,
const auto print_functions = printFunctions();
output << ((parseComponent.name == "print") ? print_functions.first : (parseComponent.name == "print_line") ? print_functions.second : parseComponent.name);
),
make_task(StandardComponents::Reference, output << parseComponent.name;),
make_task(StandardComponents::Call,
output << '(';
const auto print_functions = printFunctions();
output << ((parseComponent.name == "print") ? print_functions.first : (parseComponent.name == "print_line") ? print_functions.second : parseComponent.name) << '(';
transpileTree(parseComponent);
output << ')';
)
@ -110,23 +135,8 @@ public:
static shared_ptr<Target> forName(string_view name, bool newLines);
string transpileWithTree(const ParseTree& tree) {
separator = newLines ? "\n" : (supportsOneLine() ? uniqueLineSeparator().value() : throw Yerbacon::Exception("--newlines=off is not supported by the current target"));
const unordered_task_map& taskMap = getTaskMapInstance();
output.str(string());
for (unsigned int i = 0; i < tree.size(); ++i) {
const component_ptr& component = tree[i];
const type_info& id = component->getId();
try {
taskMap.at(id)(tree, i);
} catch (const out_of_range&) {
throw Yerbacon::Exception(string(
#ifndef __GNUC__
id.name()
#else
abi::__cxa_demangle(id.name(), nullptr, nullptr, nullptr)
#endif
) += " is not supported by the current target");
}
}
separate_transpileTree(tree);
return output.str() + '\n';
};
explicit Target(const bool& newLines): newLines(newLines), separator() {};

View File

@ -8,8 +8,18 @@ struct JsTarget: Target {
return {
make_task(Define,
output << (parseComponent.final ? "const " : "let ") << parseComponent.name << " = ";
transpileTree(parseComponent.content);
),
make_task(types::String, stringInterpolation(parseComponent.content);)
make_task(types::String, stringInterpolation(parseComponent.content);),
make_task(Function,
output << "function " << parseComponent.name << '(';
transpileTree(parseComponent.parameters);
output << ") {";
if (newLines) output << separator << indentation;
separate_transpileTree(parseComponent, 1);
if (newLines) output << separator;
output << '}';
)
};
}
using Target::Target;

View File

@ -12,8 +12,17 @@ struct LuaTarget: Target {
output << parseComponent.name;
if (parseComponent.final) output << " <const>"; // TODO Find an alternative to <const> for lua <5.4
output << " = ";
transpileTree(parseComponent.content);
),
make_task(types::String, stringInterpolation(parseComponent.content, "[[", "]]", "..");)
make_task(types::String, stringInterpolation(parseComponent.content, "[[", "]]", "..");),
make_task(Function,
output << "function " << parseComponent.name << '(';
transpileTree(parseComponent.parameters);
output << ')' << separator;
if (newLines) output << indentation;
separate_transpileTree(parseComponent, 1);
output << separator << "end";
)
};
}
using Target::Target;

View File

@ -7,8 +7,14 @@ struct PyTarget: Target {
optional_string uniqueLineSeparator() final { return {}; }
unordered_task_map getTaskMap() final {
return {
make_task(Define, output << parseComponent.name << " = ";),
make_task(types::String, stringInterpolation(R"(""")", parseComponent.content);)
make_task(Define, output << parseComponent.name << " = "; transpileTree(parseComponent.content);),
make_task(types::String, stringInterpolation(R"(""")", parseComponent.content);),
make_task(Function,
output << "def " << parseComponent.name << '(';
transpileTree(parseComponent.parameters);
output << "):" << separator << indentation;
separate_transpileTree(parseComponent, 1);
),
};
}
using Target::Target;

View File

@ -46,9 +46,8 @@ int main(int argc, char* argv[]) {
} else invalid_argument: Yerbacon::fail({"\"", currentArg.data(), "\" is not a valid argument."});
}
}
const auto currentTarget = Target::forName(target, newLines);
const auto compile = [&target, &currentTarget](string_view name) -> string {
string transpiledString = currentTarget->transpileWithTree(parseString(getFileContent(name.data())));
const auto compile = [&target, &newLines](string_view name) -> string {
string transpiledString = Target::forName(target, newLines)->transpileWithTree(parseString(getFileContent(name.data())));
name.remove_suffix(6);
string outputFile;
(outputFile = name).append(target);